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(dest->digit_count); - - size_t i = 0; - for (; i < op1->digit_count && i < op2->digit_count; i += 1) { - dest->data.digits[i] = op1_digits[i] & op2_digits[i]; - } - for (; i < dest->digit_count; i += 1) { - dest->data.digits[i] = 0; - } - bigint_normalize(dest); - } -} - -void bigint_xor(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_xor(&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); - - assert(op1->digit_count > 0 && op2->digit_count > 0); - 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); - size_t i = 0; - for (; i < op1->digit_count && i < op2->digit_count; i += 1) { - dest->data.digits[i] = op1_digits[i] ^ op2_digits[i]; - } - for (; i < dest->digit_count; i += 1) { - if (i < op1->digit_count) { - dest->data.digits[i] = op1_digits[i]; - } else if (i < op2->digit_count) { - dest->data.digits[i] = op2_digits[i]; - } else { - zig_unreachable(); - } - } - bigint_normalize(dest); - } -} - -void bigint_shl(BigInt *dest, const BigInt *op1, const BigInt *op2) { - assert(!op2->is_negative); - - if (op2->digit_count == 0) { - bigint_init_bigint(dest, op1); - return; - } - - if (op1->digit_count == 0) { - bigint_init_unsigned(dest, 0); - return; - } - - if (op2->digit_count != 1) { - zig_panic("TODO shift left by amount greater than 64 bit integer"); - } - - const uint64_t *op1_digits = bigint_ptr(op1); - uint64_t shift_amt = bigint_as_unsigned(op2); - - if (op1->digit_count == 1 && shift_amt < 64) { - dest->data.digit = op1_digits[0] << shift_amt; - if (dest->data.digit > op1_digits[0]) { - dest->digit_count = 1; - dest->is_negative = op1->is_negative; - return; - } - } - - uint64_t digit_shift_count = shift_amt / 64; - uint64_t leftover_shift_count = shift_amt % 64; - - dest->data.digits = heap::c_allocator.allocate(op1->digit_count + digit_shift_count + 1); - dest->digit_count = digit_shift_count; - uint64_t carry = 0; - for (size_t i = 0; i < op1->digit_count; i += 1) { - uint64_t digit = op1_digits[i]; - dest->data.digits[dest->digit_count] = carry | (digit << leftover_shift_count); - dest->digit_count += 1; - if (leftover_shift_count > 0) { - carry = digit >> (64 - leftover_shift_count); - } else { - carry = 0; - } - } - dest->data.digits[dest->digit_count] = carry; - dest->digit_count += 1; - dest->is_negative = op1->is_negative; - bigint_normalize(dest); -} - -void bigint_shl_trunc(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed) { - BigInt unwrapped = {0}; - bigint_shl(&unwrapped, op1, op2); - bigint_truncate(dest, &unwrapped, bit_count, is_signed); -} - -void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2) { - assert(!op2->is_negative); - - if (op1->digit_count == 0) { - return bigint_init_unsigned(dest, 0); - } - - if (op2->digit_count == 0) { - return bigint_init_bigint(dest, op1); - } - - if (op2->digit_count != 1) { - zig_panic("TODO shift right by amount greater than 64 bit integer"); - } - - const uint64_t *op1_digits = bigint_ptr(op1); - uint64_t shift_amt = bigint_as_unsigned(op2); - - if (op1->digit_count == 1) { - dest->data.digit = (shift_amt < 64) ? op1_digits[0] >> shift_amt : 0; - dest->digit_count = 1; - dest->is_negative = op1->is_negative; - bigint_normalize(dest); - return; - } - - size_t digit_shift_count = shift_amt / 64; - size_t leftover_shift_count = shift_amt % 64; - - if (digit_shift_count >= op1->digit_count) { - return bigint_init_unsigned(dest, 0); - } - - dest->digit_count = op1->digit_count - digit_shift_count; - uint64_t *digits; - if (dest->digit_count == 1) { - digits = &dest->data.digit; - } else { - digits = heap::c_allocator.allocate(dest->digit_count); - dest->data.digits = digits; - } - - uint64_t carry = 0; - for (size_t op_digit_index = op1->digit_count - 1;;) { - uint64_t digit = op1_digits[op_digit_index]; - size_t dest_digit_index = op_digit_index - digit_shift_count; - digits[dest_digit_index] = carry | (digit >> leftover_shift_count); - carry = (leftover_shift_count != 0) ? (digit << (64 - leftover_shift_count)) : 0; - - if (dest_digit_index == 0) { break; } - op_digit_index -= 1; - } - dest->is_negative = op1->is_negative; - bigint_normalize(dest); -} - -void bigint_negate(BigInt *dest, const BigInt *op) { - bigint_init_bigint(dest, op); - dest->is_negative = !dest->is_negative; - bigint_normalize(dest); -} - -void bigint_negate_wrap(BigInt *dest, const BigInt *op, size_t bit_count) { - BigInt zero; - bigint_init_unsigned(&zero, 0); - bigint_sub_wrap(dest, &zero, op, bit_count, true); -} - -void bigint_not(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed) { - if (bit_count == 0) { - bigint_init_unsigned(dest, 0); - return; - } - - if (is_signed) { - BigInt twos_comp = {0}; - to_twos_complement(&twos_comp, op, bit_count); - - BigInt inverted = {0}; - bigint_not(&inverted, &twos_comp, bit_count, false); - - from_twos_complement(dest, &inverted, bit_count, true); - return; - } - - assert(!op->is_negative); - - dest->is_negative = false; - const uint64_t *op_digits = bigint_ptr(op); - if (bit_count <= 64) { - dest->digit_count = 1; - if (op->digit_count == 0) { - if (bit_count == 64) { - dest->data.digit = UINT64_MAX; - } else { - dest->data.digit = (1ULL << bit_count) - 1; - } - } else if (op->digit_count == 1) { - dest->data.digit = ~op_digits[0]; - if (bit_count != 64) { - uint64_t mask = (1ULL << bit_count) - 1; - dest->data.digit &= mask; - } - } - bigint_normalize(dest); - return; - } - dest->digit_count = (bit_count + 63) / 64; - assert(dest->digit_count >= op->digit_count); - dest->data.digits = heap::c_allocator.allocate_nonzero(dest->digit_count); - size_t i = 0; - for (; i < op->digit_count; i += 1) { - dest->data.digits[i] = ~op_digits[i]; - } - for (; i < dest->digit_count; i += 1) { - dest->data.digits[i] = 0xffffffffffffffffULL; - } - size_t digit_index = dest->digit_count - 1; - size_t digit_bit_index = bit_count % 64; - if (digit_bit_index != 0) { - uint64_t mask = (1ULL << digit_bit_index) - 1; - dest->data.digits[digit_index] &= mask; - } - bigint_normalize(dest); -} - -void bigint_truncate(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed) { - BigInt twos_comp; - to_twos_complement(&twos_comp, op, bit_count); - from_twos_complement(dest, &twos_comp, bit_count, is_signed); -} - -Cmp bigint_cmp(const BigInt *op1, const BigInt *op2) { - if (op1->is_negative && !op2->is_negative) { - return CmpLT; - } else if (!op1->is_negative && op2->is_negative) { - return CmpGT; - } else if (op1->digit_count > op2->digit_count) { - return op1->is_negative ? CmpLT : CmpGT; - } else if (op2->digit_count > op1->digit_count) { - return op1->is_negative ? CmpGT : CmpLT; - } else if (op1->digit_count == 0) { - return CmpEQ; - } - const uint64_t *op1_digits = bigint_ptr(op1); - const uint64_t *op2_digits = bigint_ptr(op2); - for (size_t i = op1->digit_count - 1; ;) { - uint64_t op1_digit = op1_digits[i]; - uint64_t op2_digit = op2_digits[i]; - - if (op1_digit > op2_digit) { - return op1->is_negative ? CmpLT : CmpGT; - } - if (op1_digit < op2_digit) { - return op1->is_negative ? CmpGT : CmpLT; - } - - if (i == 0) { - return CmpEQ; - } - i -= 1; - } -} - -void bigint_append_buf(Buf *buf, const BigInt *op, uint64_t base) { - if (op->digit_count == 0) { - buf_append_char(buf, '0'); - return; - } - if (op->is_negative) { - buf_append_char(buf, '-'); - } - if (op->digit_count == 1 && base == 10) { - buf_appendf(buf, "%" ZIG_PRI_u64, op->data.digit); - return; - } - if (op->digit_count == 1 && base == 16) { - buf_appendf(buf, "%" ZIG_PRI_x64, op->data.digit); - return; - } - size_t first_digit_index = buf_len(buf); - - BigInt digit_bi = {0}; - BigInt a1 = {0}; - BigInt a2 = {0}; - - BigInt *a = &a1; - BigInt *other_a = &a2; - bigint_init_bigint(a, op); - - BigInt base_bi = {0}; - bigint_init_unsigned(&base_bi, base); - - for (;;) { - bigint_rem(&digit_bi, a, &base_bi); - uint8_t digit = bigint_as_unsigned(&digit_bi); - buf_append_char(buf, digit_to_char(digit, false)); - bigint_div_trunc(other_a, a, &base_bi); - { - BigInt *tmp = a; - a = other_a; - other_a = tmp; - } - if (bigint_cmp_zero(a) == CmpEQ) { - break; - } - } - - // reverse - for (size_t i = first_digit_index; i < buf_len(buf) / 2; i += 1) { - size_t other_i = buf_len(buf) + first_digit_index - i - 1; - uint8_t tmp = buf_ptr(buf)[i]; - buf_ptr(buf)[i] = buf_ptr(buf)[other_i]; - buf_ptr(buf)[other_i] = tmp; - } -} - -size_t bigint_popcount_unsigned(const BigInt *bi) { - assert(!bi->is_negative); - if (bi->digit_count == 0) - return 0; - - size_t count = 0; - size_t bit_count = bi->digit_count * 64; - for (size_t i = 0; i < bit_count; i += 1) { - if (bit_at_index(bi, i)) - count += 1; - } - return count; -} - -size_t bigint_popcount_signed(const BigInt *bi, size_t bit_count) { - if (bit_count == 0) - return 0; - if (bi->digit_count == 0) - return 0; - - BigInt twos_comp = {0}; - to_twos_complement(&twos_comp, bi, bit_count); - - size_t count = 0; - for (size_t i = 0; i < bit_count; i += 1) { - if (bit_at_index(&twos_comp, i)) - count += 1; - } - return count; -} - -size_t bigint_ctz(const BigInt *bi, size_t bit_count) { - if (bit_count == 0) - return 0; - if (bi->digit_count == 0) - return bit_count; - - BigInt twos_comp = {0}; - to_twos_complement(&twos_comp, bi, bit_count); - - size_t count = 0; - for (size_t i = 0; i < bit_count; i += 1) { - if (bit_at_index(&twos_comp, i)) - return count; - count += 1; - } - return count; -} - -size_t bigint_clz(const BigInt *bi, size_t bit_count) { - if (bi->is_negative || bit_count == 0) - return 0; - if (bi->digit_count == 0) - return bit_count; - - size_t count = 0; - for (size_t i = bit_count - 1;;) { - if (bit_at_index(bi, i)) - return count; - count += 1; - - if (i == 0) break; - i -= 1; - } - return count; -} - -static uint64_t bigint_as_unsigned(const BigInt *bigint) { - assert(!bigint->is_negative); - if (bigint->digit_count == 0) { - return 0; - } else if (bigint->digit_count == 1) { - return bigint->data.digit; - } else { - zig_unreachable(); - } -} - -uint64_t bigint_as_u64(const BigInt *bigint) -{ - return bigint_as_unsigned(bigint); -} - -uint32_t bigint_as_u32(const BigInt *bigint) { - uint64_t value64 = bigint_as_unsigned(bigint); - uint32_t value32 = (uint32_t)value64; - assert (value64 == value32); - return value32; -} - -size_t bigint_as_usize(const BigInt *bigint) { - uint64_t value64 = bigint_as_unsigned(bigint); - size_t valueUsize = (size_t)value64; - assert (value64 == valueUsize); - return valueUsize; -} - -int64_t bigint_as_signed(const BigInt *bigint) { - if (bigint->digit_count == 0) { - return 0; - } else if (bigint->digit_count == 1) { - if (bigint->is_negative) { - if (bigint->data.digit <= 9223372036854775808ULL) { - return (-((int64_t)(bigint->data.digit - 1))) - 1; - } else { - zig_unreachable(); - } - } else { - return bigint->data.digit; - } - } else { - zig_unreachable(); - } -} - -Cmp bigint_cmp_zero(const BigInt *op) { - if (op->digit_count == 0) { - return CmpEQ; - } - return op->is_negative ? CmpLT : CmpGT; -} - -uint32_t bigint_hash(BigInt x) { - if (x.digit_count == 0) { - return 0; - } else { - return bigint_ptr(&x)[0]; - } -} - -bool bigint_eql(BigInt a, BigInt b) { - return bigint_cmp(&a, &b) == CmpEQ; -} - -void bigint_incr(BigInt *x) { - if (x->digit_count == 0) { - bigint_init_unsigned(x, 1); - return; - } - - if (x->digit_count == 1) { - if (x->is_negative && x->data.digit != 0) { - x->data.digit -= 1; - return; - } else if (!x->is_negative && x->data.digit != UINT64_MAX) { - x->data.digit += 1; - return; - } - } - - BigInt copy; - bigint_init_bigint(©, x); - - BigInt one; - bigint_init_unsigned(&one, 1); - - bigint_add(x, ©, &one); -} - -void bigint_decr(BigInt *x) { - if (x->digit_count == 0) { - bigint_init_signed(x, -1); - return; - } - - if (x->digit_count == 1) { - if (x->is_negative && x->data.digit != UINT64_MAX) { - x->data.digit += 1; - return; - } else if (!x->is_negative && x->data.digit != 0) { - x->data.digit -= 1; - return; - } - } - - BigInt copy; - bigint_init_bigint(©, x); - - BigInt neg_one; - bigint_init_signed(&neg_one, -1); - - bigint_add(x, ©, &neg_one); -} diff --git a/src/bigint.hpp b/src/bigint.hpp deleted file mode 100644 index 044ea6642370e69e92ac3e291ed81c14d12dc360..0000000000000000000000000000000000000000 --- a/src/bigint.hpp +++ /dev/null @@ -1,105 +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_BIGINT_HPP -#define ZIG_BIGINT_HPP - -#include -#include - -struct BigInt { - size_t digit_count; - union { - uint64_t digit; - uint64_t *digits; // Least significant digit first - } data; - bool is_negative; -}; - -struct Buf; -struct BigFloat; - -enum Cmp { - CmpLT, - CmpGT, - CmpEQ, -}; - -void bigint_init_unsigned(BigInt *dest, uint64_t x); -void bigint_init_signed(BigInt *dest, int64_t x); -void bigint_init_bigint(BigInt *dest, const BigInt *src); -void bigint_init_bigfloat(BigInt *dest, const BigFloat *op); -void bigint_init_data(BigInt *dest, const uint64_t *digits, size_t digit_count, bool is_negative); -void bigint_deinit(BigInt *bi); - -// panics if number won't fit -uint64_t bigint_as_u64(const BigInt *bigint); -uint32_t bigint_as_u32(const BigInt *bigint); -size_t bigint_as_usize(const BigInt *bigint); - -int64_t bigint_as_signed(const BigInt *bigint); - -static inline const uint64_t *bigint_ptr(const BigInt *bigint) { - if (bigint->digit_count == 1) { - return &bigint->data.digit; - } else { - return bigint->data.digits; - } -} - -bool bigint_fits_in_bits(const BigInt *bn, size_t bit_count, bool is_signed); -void bigint_write_twos_complement(const BigInt *big_int, uint8_t *buf, size_t bit_count, bool is_big_endian); -void bigint_read_twos_complement(BigInt *dest, const uint8_t *buf, size_t bit_count, bool is_big_endian, - bool is_signed); -void bigint_add(BigInt *dest, const BigInt *op1, const BigInt *op2); -void bigint_add_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed); -void bigint_sub(BigInt *dest, const BigInt *op1, const BigInt *op2); -void bigint_sub_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed); -void bigint_mul(BigInt *dest, const BigInt *op1, const BigInt *op2); -void bigint_mul_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed); -void bigint_div_trunc(BigInt *dest, const BigInt *op1, const BigInt *op2); -void bigint_div_floor(BigInt *dest, const BigInt *op1, const BigInt *op2); -void bigint_rem(BigInt *dest, const BigInt *op1, const BigInt *op2); -void bigint_mod(BigInt *dest, const BigInt *op1, const BigInt *op2); - -void bigint_or(BigInt *dest, const BigInt *op1, const BigInt *op2); -void bigint_and(BigInt *dest, const BigInt *op1, const BigInt *op2); -void bigint_xor(BigInt *dest, const BigInt *op1, const BigInt *op2); - -void bigint_shl(BigInt *dest, const BigInt *op1, const BigInt *op2); -void bigint_shl_trunc(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed); -void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2); - -void bigint_negate(BigInt *dest, const BigInt *op); -void bigint_negate_wrap(BigInt *dest, const BigInt *op, size_t bit_count); -void bigint_not(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed); -void bigint_truncate(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed); - -Cmp bigint_cmp(const BigInt *op1, const BigInt *op2); - -void bigint_append_buf(Buf *buf, const BigInt *op, uint64_t base); - -size_t bigint_ctz(const BigInt *bi, size_t bit_count); -size_t bigint_clz(const BigInt *bi, size_t bit_count); -size_t bigint_popcount_signed(const BigInt *bi, size_t bit_count); -size_t bigint_popcount_unsigned(const BigInt *bi); - -size_t bigint_bits_needed(const BigInt *op); - - -// convenience functions -Cmp bigint_cmp_zero(const BigInt *op); - -void bigint_incr(BigInt *value); -void bigint_decr(BigInt *value); - -bool mul_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result); - -uint32_t bigint_hash(BigInt x); -bool bigint_eql(BigInt a, BigInt b); - -#endif diff --git a/src/blake2.h b/src/blake2.h deleted file mode 100644 index 6420c5367a2ebb09dd055381cebf5f2d20c82951..0000000000000000000000000000000000000000 --- a/src/blake2.h +++ /dev/null @@ -1,196 +0,0 @@ -/* - BLAKE2 reference source code package - reference C implementations - - Copyright 2012, Samuel Neves . You may use this under the - terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at - your option. The terms of these licenses can be found at: - - - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 - - OpenSSL license : https://www.openssl.org/source/license.html - - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 - - More information about the BLAKE2 hash function can be found at - https://blake2.net. -*/ -#ifndef BLAKE2_H -#define BLAKE2_H - -#include -#include - -#if defined(_MSC_VER) -#define BLAKE2_PACKED(x) __pragma(pack(push, 1)) x __pragma(pack(pop)) -#else -#define BLAKE2_PACKED(x) x __attribute__((packed)) -#endif - -#if defined(__cplusplus) -extern "C" { -#endif - - enum blake2s_constant - { - BLAKE2S_BLOCKBYTES = 64, - BLAKE2S_OUTBYTES = 32, - BLAKE2S_KEYBYTES = 32, - BLAKE2S_SALTBYTES = 8, - BLAKE2S_PERSONALBYTES = 8 - }; - - enum blake2b_constant - { - BLAKE2B_BLOCKBYTES = 128, - BLAKE2B_OUTBYTES = 64, - BLAKE2B_KEYBYTES = 64, - BLAKE2B_SALTBYTES = 16, - BLAKE2B_PERSONALBYTES = 16 - }; - - typedef struct blake2s_state__ - { - uint32_t h[8]; - uint32_t t[2]; - uint32_t f[2]; - uint8_t buf[BLAKE2S_BLOCKBYTES]; - size_t buflen; - size_t outlen; - uint8_t last_node; - } blake2s_state; - - typedef struct blake2b_state__ - { - uint64_t h[8]; - uint64_t t[2]; - uint64_t f[2]; - uint8_t buf[BLAKE2B_BLOCKBYTES]; - size_t buflen; - size_t outlen; - uint8_t last_node; - } blake2b_state; - - typedef struct blake2sp_state__ - { - blake2s_state S[8][1]; - blake2s_state R[1]; - uint8_t buf[8 * BLAKE2S_BLOCKBYTES]; - size_t buflen; - size_t outlen; - } blake2sp_state; - - typedef struct blake2bp_state__ - { - blake2b_state S[4][1]; - blake2b_state R[1]; - uint8_t buf[4 * BLAKE2B_BLOCKBYTES]; - size_t buflen; - size_t outlen; - } blake2bp_state; - - - BLAKE2_PACKED(struct blake2s_param__ - { - uint8_t digest_length; /* 1 */ - uint8_t key_length; /* 2 */ - uint8_t fanout; /* 3 */ - uint8_t depth; /* 4 */ - uint32_t leaf_length; /* 8 */ - uint32_t node_offset; /* 12 */ - uint16_t xof_length; /* 14 */ - uint8_t node_depth; /* 15 */ - uint8_t inner_length; /* 16 */ - /* uint8_t reserved[0]; */ - uint8_t salt[BLAKE2S_SALTBYTES]; /* 24 */ - uint8_t personal[BLAKE2S_PERSONALBYTES]; /* 32 */ - }); - - typedef struct blake2s_param__ blake2s_param; - - BLAKE2_PACKED(struct blake2b_param__ - { - uint8_t digest_length; /* 1 */ - uint8_t key_length; /* 2 */ - uint8_t fanout; /* 3 */ - uint8_t depth; /* 4 */ - uint32_t leaf_length; /* 8 */ - uint32_t node_offset; /* 12 */ - uint32_t xof_length; /* 16 */ - uint8_t node_depth; /* 17 */ - uint8_t inner_length; /* 18 */ - uint8_t reserved[14]; /* 32 */ - uint8_t salt[BLAKE2B_SALTBYTES]; /* 48 */ - uint8_t personal[BLAKE2B_PERSONALBYTES]; /* 64 */ - }); - - typedef struct blake2b_param__ blake2b_param; - - typedef struct blake2xs_state__ - { - blake2s_state S[1]; - blake2s_param P[1]; - } blake2xs_state; - - typedef struct blake2xb_state__ - { - blake2b_state S[1]; - blake2b_param P[1]; - } blake2xb_state; - - /* Padded structs result in a compile-time error */ - enum { - BLAKE2_DUMMY_1 = 1/(sizeof(blake2s_param) == BLAKE2S_OUTBYTES), - BLAKE2_DUMMY_2 = 1/(sizeof(blake2b_param) == BLAKE2B_OUTBYTES) - }; - - /* Streaming API */ - int blake2s_init( blake2s_state *S, size_t outlen ); - int blake2s_init_key( blake2s_state *S, size_t outlen, const void *key, size_t keylen ); - int blake2s_init_param( blake2s_state *S, const blake2s_param *P ); - int blake2s_update( blake2s_state *S, const void *in, size_t inlen ); - int blake2s_final( blake2s_state *S, void *out, size_t outlen ); - - int blake2b_init( blake2b_state *S, size_t outlen ); - int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen ); - int blake2b_init_param( blake2b_state *S, const blake2b_param *P ); - int blake2b_update( blake2b_state *S, const void *in, size_t inlen ); - int blake2b_final( blake2b_state *S, void *out, size_t outlen ); - - int blake2sp_init( blake2sp_state *S, size_t outlen ); - int blake2sp_init_key( blake2sp_state *S, size_t outlen, const void *key, size_t keylen ); - int blake2sp_update( blake2sp_state *S, const void *in, size_t inlen ); - int blake2sp_final( blake2sp_state *S, void *out, size_t outlen ); - - int blake2bp_init( blake2bp_state *S, size_t outlen ); - int blake2bp_init_key( blake2bp_state *S, size_t outlen, const void *key, size_t keylen ); - int blake2bp_update( blake2bp_state *S, const void *in, size_t inlen ); - int blake2bp_final( blake2bp_state *S, void *out, size_t outlen ); - - /* Variable output length API */ - int blake2xs_init( blake2xs_state *S, const size_t outlen ); - int blake2xs_init_key( blake2xs_state *S, const size_t outlen, const void *key, size_t keylen ); - int blake2xs_update( blake2xs_state *S, const void *in, size_t inlen ); - int blake2xs_final(blake2xs_state *S, void *out, size_t outlen); - - int blake2xb_init( blake2xb_state *S, const size_t outlen ); - int blake2xb_init_key( blake2xb_state *S, const size_t outlen, const void *key, size_t keylen ); - int blake2xb_update( blake2xb_state *S, const void *in, size_t inlen ); - int blake2xb_final(blake2xb_state *S, void *out, size_t outlen); - - /* Simple API */ - int blake2s( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); - int blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); - - int blake2sp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); - int blake2bp( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); - - int blake2xs( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); - int blake2xb( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); - - /* This is simply an alias for blake2b */ - int blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ); - -#if defined(__cplusplus) -} -#endif - -#endif - diff --git a/src/blake2b.c b/src/blake2b.c deleted file mode 100644 index 600f951b9b5e8afe5c59bd55153b0f040c63822e..0000000000000000000000000000000000000000 --- a/src/blake2b.c +++ /dev/null @@ -1,539 +0,0 @@ -/* - BLAKE2 reference source code package - reference C implementations - - Copyright 2012, Samuel Neves . You may use this under the - terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at - your option. The terms of these licenses can be found at: - - - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 - - OpenSSL license : https://www.openssl.org/source/license.html - - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 - - More information about the BLAKE2 hash function can be found at - https://blake2.net. -*/ - -#include -#include -#include - -#include "blake2.h" -/* - BLAKE2 reference source code package - reference C implementations - - Copyright 2012, Samuel Neves . You may use this under the - terms of the CC0, the OpenSSL Licence, or the Apache Public License 2.0, at - your option. The terms of these licenses can be found at: - - - CC0 1.0 Universal : http://creativecommons.org/publicdomain/zero/1.0 - - OpenSSL license : https://www.openssl.org/source/license.html - - Apache 2.0 : http://www.apache.org/licenses/LICENSE-2.0 - - More information about the BLAKE2 hash function can be found at - https://blake2.net. -*/ -#ifndef BLAKE2_IMPL_H -#define BLAKE2_IMPL_H - -#include -#include - -#if !defined(__cplusplus) && (!defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L) - #if defined(_MSC_VER) - #define BLAKE2_INLINE __inline - #elif defined(__GNUC__) - #define BLAKE2_INLINE __inline__ - #else - #define BLAKE2_INLINE - #endif -#else - #define BLAKE2_INLINE inline -#endif - -static BLAKE2_INLINE uint32_t load32( const void *src ) -{ -#if defined(NATIVE_LITTLE_ENDIAN) - uint32_t w; - memcpy(&w, src, sizeof w); - return w; -#else - const uint8_t *p = ( const uint8_t * )src; - return (( uint32_t )( p[0] ) << 0) | - (( uint32_t )( p[1] ) << 8) | - (( uint32_t )( p[2] ) << 16) | - (( uint32_t )( p[3] ) << 24) ; -#endif -} - -static BLAKE2_INLINE uint64_t load64( const void *src ) -{ -#if defined(NATIVE_LITTLE_ENDIAN) - uint64_t w; - memcpy(&w, src, sizeof w); - return w; -#else - const uint8_t *p = ( const uint8_t * )src; - return (( uint64_t )( p[0] ) << 0) | - (( uint64_t )( p[1] ) << 8) | - (( uint64_t )( p[2] ) << 16) | - (( uint64_t )( p[3] ) << 24) | - (( uint64_t )( p[4] ) << 32) | - (( uint64_t )( p[5] ) << 40) | - (( uint64_t )( p[6] ) << 48) | - (( uint64_t )( p[7] ) << 56) ; -#endif -} - -static BLAKE2_INLINE uint16_t load16( const void *src ) -{ -#if defined(NATIVE_LITTLE_ENDIAN) - uint16_t w; - memcpy(&w, src, sizeof w); - return w; -#else - const uint8_t *p = ( const uint8_t * )src; - return ( uint16_t )((( uint32_t )( p[0] ) << 0) | - (( uint32_t )( p[1] ) << 8)); -#endif -} - -static BLAKE2_INLINE void store16( void *dst, uint16_t w ) -{ -#if defined(NATIVE_LITTLE_ENDIAN) - memcpy(dst, &w, sizeof w); -#else - uint8_t *p = ( uint8_t * )dst; - *p++ = ( uint8_t )w; w >>= 8; - *p++ = ( uint8_t )w; -#endif -} - -static BLAKE2_INLINE void store32( void *dst, uint32_t w ) -{ -#if defined(NATIVE_LITTLE_ENDIAN) - memcpy(dst, &w, sizeof w); -#else - uint8_t *p = ( uint8_t * )dst; - p[0] = (uint8_t)(w >> 0); - p[1] = (uint8_t)(w >> 8); - p[2] = (uint8_t)(w >> 16); - p[3] = (uint8_t)(w >> 24); -#endif -} - -static BLAKE2_INLINE void store64( void *dst, uint64_t w ) -{ -#if defined(NATIVE_LITTLE_ENDIAN) - memcpy(dst, &w, sizeof w); -#else - uint8_t *p = ( uint8_t * )dst; - p[0] = (uint8_t)(w >> 0); - p[1] = (uint8_t)(w >> 8); - p[2] = (uint8_t)(w >> 16); - p[3] = (uint8_t)(w >> 24); - p[4] = (uint8_t)(w >> 32); - p[5] = (uint8_t)(w >> 40); - p[6] = (uint8_t)(w >> 48); - p[7] = (uint8_t)(w >> 56); -#endif -} - -static BLAKE2_INLINE uint64_t load48( const void *src ) -{ - const uint8_t *p = ( const uint8_t * )src; - return (( uint64_t )( p[0] ) << 0) | - (( uint64_t )( p[1] ) << 8) | - (( uint64_t )( p[2] ) << 16) | - (( uint64_t )( p[3] ) << 24) | - (( uint64_t )( p[4] ) << 32) | - (( uint64_t )( p[5] ) << 40) ; -} - -static BLAKE2_INLINE void store48( void *dst, uint64_t w ) -{ - uint8_t *p = ( uint8_t * )dst; - p[0] = (uint8_t)(w >> 0); - p[1] = (uint8_t)(w >> 8); - p[2] = (uint8_t)(w >> 16); - p[3] = (uint8_t)(w >> 24); - p[4] = (uint8_t)(w >> 32); - p[5] = (uint8_t)(w >> 40); -} - -static BLAKE2_INLINE uint32_t rotr32( const uint32_t w, const unsigned c ) -{ - return ( w >> c ) | ( w << ( 32 - c ) ); -} - -static BLAKE2_INLINE uint64_t rotr64( const uint64_t w, const unsigned c ) -{ - return ( w >> c ) | ( w << ( 64 - c ) ); -} - -/* prevents compiler optimizing out memset() */ -static BLAKE2_INLINE void secure_zero_memory(void *v, size_t n) -{ - static void *(*const volatile memset_v)(void *, int, size_t) = &memset; - memset_v(v, 0, n); -} - -#endif - -static const uint64_t blake2b_IV[8] = -{ - 0x6a09e667f3bcc908ULL, 0xbb67ae8584caa73bULL, - 0x3c6ef372fe94f82bULL, 0xa54ff53a5f1d36f1ULL, - 0x510e527fade682d1ULL, 0x9b05688c2b3e6c1fULL, - 0x1f83d9abfb41bd6bULL, 0x5be0cd19137e2179ULL -}; - -static const uint8_t blake2b_sigma[12][16] = -{ - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , - { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } , - { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } , - { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } , - { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } , - { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } , - { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } , - { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } , - { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } , - { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } , - { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } , - { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } -}; - - -static void blake2b_set_lastnode( blake2b_state *S ) -{ - S->f[1] = (uint64_t)-1; -} - -/* Some helper functions, not necessarily useful */ -static int blake2b_is_lastblock( const blake2b_state *S ) -{ - return S->f[0] != 0; -} - -static void blake2b_set_lastblock( blake2b_state *S ) -{ - if( S->last_node ) blake2b_set_lastnode( S ); - - S->f[0] = (uint64_t)-1; -} - -static void blake2b_increment_counter( blake2b_state *S, const uint64_t inc ) -{ - S->t[0] += inc; - S->t[1] += ( S->t[0] < inc ); -} - -static void blake2b_init0( blake2b_state *S ) -{ - size_t i; - memset( S, 0, sizeof( blake2b_state ) ); - - for( i = 0; i < 8; ++i ) S->h[i] = blake2b_IV[i]; -} - -/* init xors IV with input parameter block */ -int blake2b_init_param( blake2b_state *S, const blake2b_param *P ) -{ - const uint8_t *p = ( const uint8_t * )( P ); - size_t i; - - blake2b_init0( S ); - - /* IV XOR ParamBlock */ - for( i = 0; i < 8; ++i ) - S->h[i] ^= load64( p + sizeof( S->h[i] ) * i ); - - S->outlen = P->digest_length; - return 0; -} - - - -int blake2b_init( blake2b_state *S, size_t outlen ) -{ - blake2b_param P[1]; - - if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1; - - P->digest_length = (uint8_t)outlen; - P->key_length = 0; - P->fanout = 1; - P->depth = 1; - store32( &P->leaf_length, 0 ); - store32( &P->node_offset, 0 ); - store32( &P->xof_length, 0 ); - P->node_depth = 0; - P->inner_length = 0; - memset( P->reserved, 0, sizeof( P->reserved ) ); - memset( P->salt, 0, sizeof( P->salt ) ); - memset( P->personal, 0, sizeof( P->personal ) ); - return blake2b_init_param( S, P ); -} - - -int blake2b_init_key( blake2b_state *S, size_t outlen, const void *key, size_t keylen ) -{ - blake2b_param P[1]; - - if ( ( !outlen ) || ( outlen > BLAKE2B_OUTBYTES ) ) return -1; - - if ( !key || !keylen || keylen > BLAKE2B_KEYBYTES ) return -1; - - P->digest_length = (uint8_t)outlen; - P->key_length = (uint8_t)keylen; - P->fanout = 1; - P->depth = 1; - store32( &P->leaf_length, 0 ); - store32( &P->node_offset, 0 ); - store32( &P->xof_length, 0 ); - P->node_depth = 0; - P->inner_length = 0; - memset( P->reserved, 0, sizeof( P->reserved ) ); - memset( P->salt, 0, sizeof( P->salt ) ); - memset( P->personal, 0, sizeof( P->personal ) ); - - if( blake2b_init_param( S, P ) < 0 ) return -1; - - { - uint8_t block[BLAKE2B_BLOCKBYTES]; - memset( block, 0, BLAKE2B_BLOCKBYTES ); - memcpy( block, key, keylen ); - blake2b_update( S, block, BLAKE2B_BLOCKBYTES ); - secure_zero_memory( block, BLAKE2B_BLOCKBYTES ); /* Burn the key from stack */ - } - return 0; -} - -#define G(r,i,a,b,c,d) \ - do { \ - a = a + b + m[blake2b_sigma[r][2*i+0]]; \ - d = rotr64(d ^ a, 32); \ - c = c + d; \ - b = rotr64(b ^ c, 24); \ - a = a + b + m[blake2b_sigma[r][2*i+1]]; \ - d = rotr64(d ^ a, 16); \ - c = c + d; \ - b = rotr64(b ^ c, 63); \ - } while(0) - -#define ROUND(r) \ - do { \ - G(r,0,v[ 0],v[ 4],v[ 8],v[12]); \ - G(r,1,v[ 1],v[ 5],v[ 9],v[13]); \ - G(r,2,v[ 2],v[ 6],v[10],v[14]); \ - G(r,3,v[ 3],v[ 7],v[11],v[15]); \ - G(r,4,v[ 0],v[ 5],v[10],v[15]); \ - G(r,5,v[ 1],v[ 6],v[11],v[12]); \ - G(r,6,v[ 2],v[ 7],v[ 8],v[13]); \ - G(r,7,v[ 3],v[ 4],v[ 9],v[14]); \ - } while(0) - -static void blake2b_compress( blake2b_state *S, const uint8_t block[BLAKE2B_BLOCKBYTES] ) -{ - uint64_t m[16]; - uint64_t v[16]; - size_t i; - - for( i = 0; i < 16; ++i ) { - m[i] = load64( block + i * sizeof( m[i] ) ); - } - - for( i = 0; i < 8; ++i ) { - v[i] = S->h[i]; - } - - v[ 8] = blake2b_IV[0]; - v[ 9] = blake2b_IV[1]; - v[10] = blake2b_IV[2]; - v[11] = blake2b_IV[3]; - v[12] = blake2b_IV[4] ^ S->t[0]; - v[13] = blake2b_IV[5] ^ S->t[1]; - v[14] = blake2b_IV[6] ^ S->f[0]; - v[15] = blake2b_IV[7] ^ S->f[1]; - - ROUND( 0 ); - ROUND( 1 ); - ROUND( 2 ); - ROUND( 3 ); - ROUND( 4 ); - ROUND( 5 ); - ROUND( 6 ); - ROUND( 7 ); - ROUND( 8 ); - ROUND( 9 ); - ROUND( 10 ); - ROUND( 11 ); - - for( i = 0; i < 8; ++i ) { - S->h[i] = S->h[i] ^ v[i] ^ v[i + 8]; - } -} - -#undef G -#undef ROUND - -int blake2b_update( blake2b_state *S, const void *pin, size_t inlen ) -{ - const unsigned char * in = (const unsigned char *)pin; - if( inlen > 0 ) - { - size_t left = S->buflen; - size_t fill = BLAKE2B_BLOCKBYTES - left; - if( inlen > fill ) - { - S->buflen = 0; - memcpy( S->buf + left, in, fill ); /* Fill buffer */ - blake2b_increment_counter( S, BLAKE2B_BLOCKBYTES ); - blake2b_compress( S, S->buf ); /* Compress */ - in += fill; inlen -= fill; - while(inlen > BLAKE2B_BLOCKBYTES) { - blake2b_increment_counter(S, BLAKE2B_BLOCKBYTES); - blake2b_compress( S, in ); - in += BLAKE2B_BLOCKBYTES; - inlen -= BLAKE2B_BLOCKBYTES; - } - } - memcpy( S->buf + S->buflen, in, inlen ); - S->buflen += inlen; - } - return 0; -} - -int blake2b_final( blake2b_state *S, void *out, size_t outlen ) -{ - uint8_t buffer[BLAKE2B_OUTBYTES] = {0}; - size_t i; - - if( out == NULL || outlen < S->outlen ) - return -1; - - if( blake2b_is_lastblock( S ) ) - return -1; - - blake2b_increment_counter( S, S->buflen ); - blake2b_set_lastblock( S ); - memset( S->buf + S->buflen, 0, BLAKE2B_BLOCKBYTES - S->buflen ); /* Padding */ - blake2b_compress( S, S->buf ); - - for( i = 0; i < 8; ++i ) /* Output full hash to temp buffer */ - store64( buffer + sizeof( S->h[i] ) * i, S->h[i] ); - - memcpy( out, buffer, S->outlen ); - secure_zero_memory(buffer, sizeof(buffer)); - return 0; -} - -/* inlen, at least, should be uint64_t. Others can be size_t. */ -int blake2b( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) -{ - blake2b_state S[1]; - - /* Verify parameters */ - if ( NULL == in && inlen > 0 ) return -1; - - if ( NULL == out ) return -1; - - if( NULL == key && keylen > 0 ) return -1; - - if( !outlen || outlen > BLAKE2B_OUTBYTES ) return -1; - - if( keylen > BLAKE2B_KEYBYTES ) return -1; - - if( keylen > 0 ) - { - if( blake2b_init_key( S, outlen, key, keylen ) < 0 ) return -1; - } - else - { - if( blake2b_init( S, outlen ) < 0 ) return -1; - } - - blake2b_update( S, ( const uint8_t * )in, inlen ); - blake2b_final( S, out, outlen ); - return 0; -} - -int blake2( void *out, size_t outlen, const void *in, size_t inlen, const void *key, size_t keylen ) { - return blake2b(out, outlen, in, inlen, key, keylen); -} - -#if defined(SUPERCOP) -int crypto_hash( unsigned char *out, unsigned char *in, unsigned long long inlen ) -{ - return blake2b( out, BLAKE2B_OUTBYTES, in, inlen, NULL, 0 ); -} -#endif - -#if defined(BLAKE2B_SELFTEST) -#include -#include "blake2-kat.h" -int main( void ) -{ - uint8_t key[BLAKE2B_KEYBYTES]; - uint8_t buf[BLAKE2_KAT_LENGTH]; - size_t i, step; - - for( i = 0; i < BLAKE2B_KEYBYTES; ++i ) - key[i] = ( uint8_t )i; - - for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) - buf[i] = ( uint8_t )i; - - /* Test simple API */ - for( i = 0; i < BLAKE2_KAT_LENGTH; ++i ) - { - uint8_t hash[BLAKE2B_OUTBYTES]; - blake2b( hash, BLAKE2B_OUTBYTES, buf, i, key, BLAKE2B_KEYBYTES ); - - if( 0 != memcmp( hash, blake2b_keyed_kat[i], BLAKE2B_OUTBYTES ) ) - { - goto fail; - } - } - - /* Test streaming API */ - for(step = 1; step < BLAKE2B_BLOCKBYTES; ++step) { - for (i = 0; i < BLAKE2_KAT_LENGTH; ++i) { - uint8_t hash[BLAKE2B_OUTBYTES]; - blake2b_state S; - uint8_t * p = buf; - size_t mlen = i; - int err = 0; - - if( (err = blake2b_init_key(&S, BLAKE2B_OUTBYTES, key, BLAKE2B_KEYBYTES)) < 0 ) { - goto fail; - } - - while (mlen >= step) { - if ( (err = blake2b_update(&S, p, step)) < 0 ) { - goto fail; - } - mlen -= step; - p += step; - } - if ( (err = blake2b_update(&S, p, mlen)) < 0) { - goto fail; - } - if ( (err = blake2b_final(&S, hash, BLAKE2B_OUTBYTES)) < 0) { - goto fail; - } - - if (0 != memcmp(hash, blake2b_keyed_kat[i], BLAKE2B_OUTBYTES)) { - goto fail; - } - } - } - - puts( "ok" ); - return 0; -fail: - puts("error"); - return -1; -} -#endif - diff --git a/src/buffer.cpp b/src/buffer.cpp deleted file mode 100644 index 86435e0f1496fa19f080bf442cd37ba9e348701c..0000000000000000000000000000000000000000 --- a/src/buffer.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (c) 2016 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#include "buffer.hpp" -#include -#include -#include - -Buf *buf_vprintf(const char *format, va_list ap) { - va_list ap2; - va_copy(ap2, ap); - - int len1 = vsnprintf(nullptr, 0, format, ap); - assert(len1 >= 0); - - size_t required_size = len1 + 1; - - Buf *buf = buf_alloc_fixed(len1); - - int len2 = vsnprintf(buf_ptr(buf), required_size, format, ap2); - assert(len2 == len1); - - va_end(ap2); - - return buf; -} - -Buf *buf_sprintf(const char *format, ...) { - va_list ap; - va_start(ap, format); - Buf *result = buf_vprintf(format, ap); - va_end(ap); - return result; -} - -void buf_appendf(Buf *buf, const char *format, ...) { - assert(buf->list.length); - va_list ap, ap2; - va_start(ap, format); - va_copy(ap2, ap); - - int len1 = vsnprintf(nullptr, 0, format, ap); - assert(len1 >= 0); - - size_t required_size = len1 + 1; - - size_t orig_len = buf_len(buf); - - buf_resize(buf, orig_len + len1); - - int len2 = vsnprintf(buf_ptr(buf) + orig_len, required_size, format, ap2); - assert(len2 == len1); - - va_end(ap2); - va_end(ap); -} - -// these functions are not static inline so they can be better used as template parameters -bool buf_eql_buf(Buf *buf, Buf *other) { - return buf_eql_mem(buf, buf_ptr(other), buf_len(other)); -} - -uint32_t buf_hash(Buf *buf) { - assert(buf->list.length); - size_t interval = buf->list.length / 256; - if (interval == 0) - interval = 1; - // FNV 32-bit hash - uint32_t h = 2166136261; - for (size_t i = 0; i < buf_len(buf); i += interval) { - h = h ^ ((uint8_t)buf->list.at(i)); - h = h * 16777619; - } - return h; -} diff --git a/src/buffer.hpp b/src/buffer.hpp deleted file mode 100644 index 8876316589e76c8a069609d0a375c73acaf4a322..0000000000000000000000000000000000000000 --- a/src/buffer.hpp +++ /dev/null @@ -1,211 +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_BUFFER_HPP -#define ZIG_BUFFER_HPP - -#include "list.hpp" - -#include -#include -#include - -#define BUF_INIT {{0}} - -// Note, you must call one of the alloc, init, or resize functions to have an -// initialized buffer. The assertions should help with this. -struct Buf { - ZigList list; -}; - -Buf *buf_sprintf(const char *format, ...) - ATTRIBUTE_PRINTF(1, 2); -Buf *buf_vprintf(const char *format, va_list ap); - -static inline size_t buf_len(Buf *buf) { - assert(buf); - assert(buf->list.length); - return buf->list.length - 1; -} - -static inline char *buf_ptr(Buf *buf) { - assert(buf); - assert(buf->list.length); - return buf->list.items; -} - -static inline const char *buf_ptr(const Buf *buf) { - assert(buf); - assert(buf->list.length); - return buf->list.items; -} - -static inline void buf_resize(Buf *buf, size_t new_len) { - buf->list.resize(new_len + 1); - buf->list.at(buf_len(buf)) = 0; -} - -static inline Buf *buf_alloc_fixed(size_t size) { - Buf *buf = heap::c_allocator.create(); - buf_resize(buf, size); - return buf; -} - -static inline Buf *buf_alloc(void) { - return buf_alloc_fixed(0); -} - -static inline void buf_deinit(Buf *buf) { - buf->list.deinit(); -} - -static inline void buf_destroy(Buf *buf) { - buf_deinit(buf); - heap::c_allocator.destroy(buf); -} - -static inline void buf_init_from_mem(Buf *buf, const char *ptr, size_t len) { - assert(len != SIZE_MAX); - buf->list.resize(len + 1); - memcpy(buf_ptr(buf), ptr, len); - buf->list.at(buf_len(buf)) = 0; -} - -static inline void buf_init_from_str(Buf *buf, const char *str) { - buf_init_from_mem(buf, str, strlen(str)); -} - -static inline void buf_init_from_buf(Buf *buf, Buf *other) { - buf_init_from_mem(buf, buf_ptr(other), buf_len(other)); -} - -static inline Buf *buf_create_from_mem(const char *ptr, size_t len) { - assert(len != SIZE_MAX); - Buf *buf = heap::c_allocator.create(); - buf_init_from_mem(buf, ptr, len); - return buf; -} - -static inline Buf *buf_create_from_slice(Slice slice) { - return buf_create_from_mem((const char *)slice.ptr, slice.len); -} - -static inline Buf *buf_create_from_str(const char *str) { - return buf_create_from_mem(str, strlen(str)); -} - -static inline Buf *buf_create_from_buf(Buf *buf) { - return buf_create_from_mem(buf_ptr(buf), buf_len(buf)); -} - -static inline Buf *buf_slice(Buf *in_buf, size_t start, size_t end) { - assert(in_buf->list.length); - assert(start != SIZE_MAX); - assert(end != SIZE_MAX); - assert(start < buf_len(in_buf)); - assert(end <= buf_len(in_buf)); - Buf *out_buf = heap::c_allocator.create(); - out_buf->list.resize(end - start + 1); - memcpy(buf_ptr(out_buf), buf_ptr(in_buf) + start, end - start); - out_buf->list.at(buf_len(out_buf)) = 0; - return out_buf; -} - -static inline void buf_append_mem(Buf *buf, const char *mem, size_t mem_len) { - assert(buf->list.length); - assert(mem_len != SIZE_MAX); - size_t old_len = buf_len(buf); - buf_resize(buf, old_len + mem_len); - memcpy(buf_ptr(buf) + old_len, mem, mem_len); - buf->list.at(buf_len(buf)) = 0; -} - -static inline void buf_append_str(Buf *buf, const char *str) { - assert(buf->list.length); - buf_append_mem(buf, str, strlen(str)); -} - -static inline void buf_append_buf(Buf *buf, Buf *append_buf) { - assert(buf->list.length); - buf_append_mem(buf, buf_ptr(append_buf), buf_len(append_buf)); -} - -static inline void buf_append_char(Buf *buf, uint8_t c) { - assert(buf->list.length); - buf_append_mem(buf, (const char *)&c, 1); -} - -void buf_appendf(Buf *buf, const char *format, ...) - ATTRIBUTE_PRINTF(2, 3); - -static inline bool buf_eql_mem(Buf *buf, const char *mem, size_t mem_len) { - assert(buf->list.length); - return mem_eql_mem(buf_ptr(buf), buf_len(buf), mem, mem_len); -} - -static inline bool buf_eql_mem_ignore_case(Buf *buf, const char *mem, size_t mem_len) { - assert(buf->list.length); - return mem_eql_mem_ignore_case(buf_ptr(buf), buf_len(buf), mem, mem_len); -} - -static inline bool buf_eql_str(Buf *buf, const char *str) { - assert(buf->list.length); - return buf_eql_mem(buf, str, strlen(str)); -} - -static inline bool buf_eql_str_ignore_case(Buf *buf, const char *str) { - assert(buf->list.length); - return buf_eql_mem_ignore_case(buf, str, strlen(str)); -} - -static inline bool buf_starts_with_mem(Buf *buf, const char *mem, size_t mem_len) { - if (buf_len(buf) < mem_len) { - return false; - } - return memcmp(buf_ptr(buf), mem, mem_len) == 0; -} - -static inline bool buf_starts_with_buf(Buf *buf, Buf *sub) { - return buf_starts_with_mem(buf, buf_ptr(sub), buf_len(sub)); -} - -static inline bool buf_starts_with_str(Buf *buf, const char *str) { - return buf_starts_with_mem(buf, str, strlen(str)); -} - -static inline bool buf_ends_with_mem(Buf *buf, const char *mem, size_t mem_len) { - return mem_ends_with_mem(buf_ptr(buf), buf_len(buf), mem, mem_len); -} - -static inline bool buf_ends_with_str(Buf *buf, const char *str) { - return buf_ends_with_mem(buf, str, strlen(str)); -} - -bool buf_eql_buf(Buf *buf, Buf *other); -uint32_t buf_hash(Buf *buf); - -static inline void buf_upcase(Buf *buf) { - for (size_t i = 0; i < buf_len(buf); i += 1) { - buf_ptr(buf)[i] = (char)toupper(buf_ptr(buf)[i]); - } -} - -static inline Slice buf_to_slice(Buf *buf) { - return Slice{reinterpret_cast(buf_ptr(buf)), buf_len(buf)}; -} - -static inline void buf_replace(Buf* buf, char from, char to) { - const size_t count = buf_len(buf); - char* ptr = buf_ptr(buf); - for (size_t i = 0; i < count; ++i) { - char& l = ptr[i]; - if (l == from) - l = to; - } -} - -#endif diff --git a/src/cache_hash.cpp b/src/cache_hash.cpp deleted file mode 100644 index c12d8f29ef8a56bd57c74e241fe7d252c87ab3dd..0000000000000000000000000000000000000000 --- a/src/cache_hash.cpp +++ /dev/null @@ -1,595 +0,0 @@ -/* - * Copyright (c) 2018 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#include "stage2.h" -#include "cache_hash.hpp" -#include "all_types.hpp" -#include "buffer.hpp" -#include "os.hpp" - -#include - -void cache_init(CacheHash *ch, Buf *manifest_dir) { - int rc = blake2b_init(&ch->blake, 48); - assert(rc == 0); - ch->files = {}; - ch->manifest_dir = manifest_dir; - ch->manifest_file_path = nullptr; - ch->manifest_dirty = false; - ch->force_check_manifest = false; - ch->b64_digest = BUF_INIT; -} - -void cache_mem(CacheHash *ch, const char *ptr, size_t len) { - assert(ch->manifest_file_path == nullptr); - assert(ptr != nullptr); - blake2b_update(&ch->blake, ptr, len); -} - -void cache_slice(CacheHash *ch, Slice slice) { - // mix the length into the hash so that two juxtaposed cached slices can't collide - cache_usize(ch, slice.len); - cache_mem(ch, slice.ptr, slice.len); -} - -void cache_str(CacheHash *ch, const char *ptr) { - // + 1 to include the null byte - cache_mem(ch, ptr, strlen(ptr) + 1); -} - -void cache_int(CacheHash *ch, int x) { - assert(ch->manifest_file_path == nullptr); - // + 1 to include the null byte - uint8_t buf[sizeof(int) + 1]; - memcpy(buf, &x, sizeof(int)); - buf[sizeof(int)] = 0; - blake2b_update(&ch->blake, buf, sizeof(int) + 1); -} - -void cache_usize(CacheHash *ch, size_t x) { - assert(ch->manifest_file_path == nullptr); - // + 1 to include the null byte - uint8_t buf[sizeof(size_t) + 1]; - memcpy(buf, &x, sizeof(size_t)); - buf[sizeof(size_t)] = 0; - blake2b_update(&ch->blake, buf, sizeof(size_t) + 1); -} - -void cache_bool(CacheHash *ch, bool x) { - assert(ch->manifest_file_path == nullptr); - blake2b_update(&ch->blake, &x, 1); -} - -void cache_buf(CacheHash *ch, Buf *buf) { - assert(ch->manifest_file_path == nullptr); - assert(buf != nullptr); - // + 1 to include the null byte - blake2b_update(&ch->blake, buf_ptr(buf), buf_len(buf) + 1); -} - -void cache_buf_opt(CacheHash *ch, Buf *buf) { - assert(ch->manifest_file_path == nullptr); - if (buf == nullptr) { - cache_str(ch, ""); - cache_str(ch, ""); - } else { - cache_buf(ch, buf); - } -} - -void cache_list_of_link_lib(CacheHash *ch, LinkLib **ptr, size_t len) { - assert(ch->manifest_file_path == nullptr); - for (size_t i = 0; i < len; i += 1) { - LinkLib *lib = ptr[i]; - if (lib->provided_explicitly) { - cache_buf(ch, lib->name); - } - } - cache_str(ch, ""); -} - -void cache_list_of_buf(CacheHash *ch, Buf **ptr, size_t len) { - assert(ch->manifest_file_path == nullptr); - for (size_t i = 0; i < len; i += 1) { - Buf *buf = ptr[i]; - cache_buf(ch, buf); - } - cache_str(ch, ""); -} - -void cache_list_of_file(CacheHash *ch, Buf **ptr, size_t len) { - assert(ch->manifest_file_path == nullptr); - - for (size_t i = 0; i < len; i += 1) { - Buf *buf = ptr[i]; - cache_file(ch, buf); - } - cache_str(ch, ""); -} - -void cache_list_of_str(CacheHash *ch, const char **ptr, size_t len) { - assert(ch->manifest_file_path == nullptr); - - for (size_t i = 0; i < len; i += 1) { - const char *s = ptr[i]; - cache_str(ch, s); - } - cache_str(ch, ""); -} - -void cache_file(CacheHash *ch, Buf *file_path) { - assert(ch->manifest_file_path == nullptr); - assert(file_path != nullptr); - Buf *resolved_path = buf_alloc(); - *resolved_path = os_path_resolve(&file_path, 1); - CacheHashFile *chf = ch->files.add_one(); - chf->path = resolved_path; - cache_buf(ch, resolved_path); -} - -void cache_file_opt(CacheHash *ch, Buf *file_path) { - assert(ch->manifest_file_path == nullptr); - if (file_path == nullptr) { - cache_str(ch, ""); - cache_str(ch, ""); - } else { - cache_file(ch, file_path); - } -} - -// Ported from std/base64.zig -static uint8_t base64_fs_alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; -static void base64_encode(Slice dest, Slice source) { - size_t dest_len = ((source.len + 2) / 3) * 4; - assert(dest.len == dest_len); - - size_t i = 0; - size_t out_index = 0; - for (; i + 2 < source.len; i += 3) { - dest.ptr[out_index] = base64_fs_alphabet[(source.ptr[i] >> 2) & 0x3f]; - out_index += 1; - - dest.ptr[out_index] = base64_fs_alphabet[((source.ptr[i] & 0x3) << 4) | ((source.ptr[i + 1] & 0xf0) >> 4)]; - out_index += 1; - - dest.ptr[out_index] = base64_fs_alphabet[((source.ptr[i + 1] & 0xf) << 2) | ((source.ptr[i + 2] & 0xc0) >> 6)]; - out_index += 1; - - dest.ptr[out_index] = base64_fs_alphabet[source.ptr[i + 2] & 0x3f]; - out_index += 1; - } - - // Assert that we never need pad characters. - assert(i == source.len); -} - -// Ported from std/base64.zig -static Error base64_decode(Slice dest, Slice source) { - if (source.len % 4 != 0) - return ErrorInvalidFormat; - if (dest.len != (source.len / 4) * 3) - return ErrorInvalidFormat; - - // In Zig this is comptime computed. In C++ it's not worth it to do that. - uint8_t char_to_index[256]; - bool char_in_alphabet[256] = {0}; - for (size_t i = 0; i < 64; i += 1) { - uint8_t c = base64_fs_alphabet[i]; - assert(!char_in_alphabet[c]); - char_in_alphabet[c] = true; - char_to_index[c] = i; - } - - size_t src_cursor = 0; - size_t dest_cursor = 0; - - for (;src_cursor < source.len; src_cursor += 4) { - if (!char_in_alphabet[source.ptr[src_cursor + 0]]) return ErrorInvalidFormat; - if (!char_in_alphabet[source.ptr[src_cursor + 1]]) return ErrorInvalidFormat; - if (!char_in_alphabet[source.ptr[src_cursor + 2]]) return ErrorInvalidFormat; - if (!char_in_alphabet[source.ptr[src_cursor + 3]]) return ErrorInvalidFormat; - dest.ptr[dest_cursor + 0] = (char_to_index[source.ptr[src_cursor + 0]] << 2) | (char_to_index[source.ptr[src_cursor + 1]] >> 4); - dest.ptr[dest_cursor + 1] = (char_to_index[source.ptr[src_cursor + 1]] << 4) | (char_to_index[source.ptr[src_cursor + 2]] >> 2); - dest.ptr[dest_cursor + 2] = (char_to_index[source.ptr[src_cursor + 2]] << 6) | (char_to_index[source.ptr[src_cursor + 3]]); - dest_cursor += 3; - } - - assert(src_cursor == source.len); - assert(dest_cursor == dest.len); - return ErrorNone; -} - -static Error hash_file(uint8_t *digest, OsFile handle, Buf *contents) { - Error err; - - if (contents) { - buf_resize(contents, 0); - } - - blake2b_state blake; - int rc = blake2b_init(&blake, 48); - assert(rc == 0); - - for (;;) { - uint8_t buf[4096]; - size_t amt = 4096; - if ((err = os_file_read(handle, buf, &amt))) - return err; - if (amt == 0) { - rc = blake2b_final(&blake, digest, 48); - assert(rc == 0); - return ErrorNone; - } - blake2b_update(&blake, buf, amt); - if (contents) { - buf_append_mem(contents, (char*)buf, amt); - } - } -} - -// If the wall clock time, rounded to the same precision as the -// mtime, is equal to the mtime, then we cannot rely on this mtime -// yet. We will instead save an mtime value that indicates the hash -// must be unconditionally computed. -static bool is_problematic_timestamp(const OsTimeStamp *fs_clock) { - OsTimeStamp wall_clock = os_timestamp_calendar(); - // First make all the least significant zero bits in the fs_clock, also zero bits in the wall clock. - if (fs_clock->nsec == 0) { - wall_clock.nsec = 0; - if (fs_clock->sec == 0) { - wall_clock.sec = 0; - } else { - wall_clock.sec &= (-1ull) << ctzll(fs_clock->sec); - } - } else { - wall_clock.nsec &= (-1ull) << ctzll(fs_clock->nsec); - } - return wall_clock.nsec == fs_clock->nsec && wall_clock.sec == fs_clock->sec; -} - -static Error populate_file_hash(CacheHash *ch, CacheHashFile *chf, Buf *contents) { - Error err; - - assert(chf->path != nullptr); - - OsFile this_file; - if ((err = os_file_open_r(chf->path, &this_file, &chf->attr))) - return err; - - if (is_problematic_timestamp(&chf->attr.mtime)) { - chf->attr.mtime.sec = 0; - chf->attr.mtime.nsec = 0; - chf->attr.inode = 0; - } - - if ((err = hash_file(chf->bin_digest, this_file, contents))) { - os_file_close(&this_file); - return err; - } - os_file_close(&this_file); - - blake2b_update(&ch->blake, chf->bin_digest, 48); - - return ErrorNone; -} - -Error cache_hit(CacheHash *ch, Buf *out_digest) { - Error err; - - uint8_t bin_digest[48]; - int rc = blake2b_final(&ch->blake, bin_digest, 48); - assert(rc == 0); - - buf_resize(&ch->b64_digest, 64); - base64_encode(buf_to_slice(&ch->b64_digest), {bin_digest, 48}); - - if (ch->files.length == 0 && !ch->force_check_manifest) { - buf_resize(out_digest, 64); - base64_encode(buf_to_slice(out_digest), {bin_digest, 48}); - return ErrorNone; - } - - rc = blake2b_init(&ch->blake, 48); - assert(rc == 0); - blake2b_update(&ch->blake, bin_digest, 48); - - ch->manifest_file_path = buf_alloc(); - os_path_join(ch->manifest_dir, &ch->b64_digest, ch->manifest_file_path); - - buf_append_str(ch->manifest_file_path, ".txt"); - - if ((err = os_make_path(ch->manifest_dir))) - return err; - - if ((err = os_file_open_lock_rw(ch->manifest_file_path, &ch->manifest_file))) - return err; - - Buf line_buf = BUF_INIT; - buf_resize(&line_buf, 512); - if ((err = os_file_read_all(ch->manifest_file, &line_buf))) { - os_file_close(&ch->manifest_file); - return err; - } - - size_t input_file_count = ch->files.length; - bool any_file_changed = false; - Error return_code = ErrorNone; - size_t file_i = 0; - SplitIterator line_it = memSplit(buf_to_slice(&line_buf), str("\n")); - for (;; file_i += 1) { - Optional> opt_line = SplitIterator_next(&line_it); - - CacheHashFile *chf; - if (file_i < input_file_count) { - chf = &ch->files.at(file_i); - } else if (any_file_changed) { - // cache miss. - // keep the manifest file open with the rw lock - // reset the hash - rc = blake2b_init(&ch->blake, 48); - assert(rc == 0); - blake2b_update(&ch->blake, bin_digest, 48); - ch->files.resize(input_file_count); - // bring the hash up to the input file hashes - for (file_i = 0; file_i < input_file_count; file_i += 1) { - blake2b_update(&ch->blake, ch->files.at(file_i).bin_digest, 48); - } - // caller can notice that out_digest is unmodified. - return return_code; - } else if (!opt_line.is_some) { - break; - } else { - chf = ch->files.add_one(); - chf->path = nullptr; - } - - if (!opt_line.is_some) - break; - - SplitIterator it = memSplit(opt_line.value, str(" ")); - - Optional> opt_inode = SplitIterator_next(&it); - if (!opt_inode.is_some) { - return_code = ErrorInvalidFormat; - break; - } - chf->attr.inode = strtoull((const char *)opt_inode.value.ptr, nullptr, 10); - - Optional> opt_mtime_sec = SplitIterator_next(&it); - if (!opt_mtime_sec.is_some) { - return_code = ErrorInvalidFormat; - break; - } - chf->attr.mtime.sec = strtoull((const char *)opt_mtime_sec.value.ptr, nullptr, 10); - - Optional> opt_mtime_nsec = SplitIterator_next(&it); - if (!opt_mtime_nsec.is_some) { - return_code = ErrorInvalidFormat; - break; - } - chf->attr.mtime.nsec = strtoull((const char *)opt_mtime_nsec.value.ptr, nullptr, 10); - - Optional> opt_digest = SplitIterator_next(&it); - if (!opt_digest.is_some) { - return_code = ErrorInvalidFormat; - break; - } - if ((err = base64_decode({chf->bin_digest, 48}, opt_digest.value))) { - return_code = ErrorInvalidFormat; - break; - } - - Slice file_path = SplitIterator_rest(&it); - if (file_path.len == 0) { - return_code = ErrorInvalidFormat; - break; - } - Buf *this_path = buf_create_from_slice(file_path); - if (chf->path != nullptr && !buf_eql_buf(this_path, chf->path)) { - return_code = ErrorInvalidFormat; - break; - } - chf->path = this_path; - - // if the mtime matches we can trust the digest - OsFile this_file; - OsFileAttr actual_attr; - if ((err = os_file_open_r(chf->path, &this_file, &actual_attr))) { - fprintf(stderr, "Unable to open %s\n: %s", buf_ptr(chf->path), err_str(err)); - os_file_close(&ch->manifest_file); - return ErrorCacheUnavailable; - } - if (chf->attr.mtime.sec == actual_attr.mtime.sec && - chf->attr.mtime.nsec == actual_attr.mtime.nsec && - chf->attr.inode == actual_attr.inode) - { - os_file_close(&this_file); - } else { - // we have to recompute the digest. - // later we'll rewrite the manifest with the new mtime/digest values - ch->manifest_dirty = true; - chf->attr = actual_attr; - - if (is_problematic_timestamp(&actual_attr.mtime)) { - chf->attr.mtime.sec = 0; - chf->attr.mtime.nsec = 0; - chf->attr.inode = 0; - } - - uint8_t actual_digest[48]; - if ((err = hash_file(actual_digest, this_file, nullptr))) { - os_file_close(&this_file); - os_file_close(&ch->manifest_file); - return err; - } - os_file_close(&this_file); - if (memcmp(chf->bin_digest, actual_digest, 48) != 0) { - memcpy(chf->bin_digest, actual_digest, 48); - // keep going until we have the input file digests - any_file_changed = true; - } - } - if (!any_file_changed) { - blake2b_update(&ch->blake, chf->bin_digest, 48); - } - } - if (file_i < input_file_count || file_i == 0 || return_code != ErrorNone) { - // manifest file is empty or missing entries, so this is a cache miss - ch->manifest_dirty = true; - for (; file_i < input_file_count; file_i += 1) { - CacheHashFile *chf = &ch->files.at(file_i); - if ((err = populate_file_hash(ch, chf, nullptr))) { - fprintf(stderr, "Unable to hash %s: %s\n", buf_ptr(chf->path), err_str(err)); - os_file_close(&ch->manifest_file); - return ErrorCacheUnavailable; - } - } - if (return_code != ErrorNone && return_code != ErrorInvalidFormat) { - os_file_close(&ch->manifest_file); - } - return return_code; - } - // Cache Hit - return cache_final(ch, out_digest); -} - -Error cache_add_file_fetch(CacheHash *ch, Buf *resolved_path, Buf *contents) { - Error err; - - assert(ch->manifest_file_path != nullptr); - CacheHashFile *chf = ch->files.add_one(); - chf->path = resolved_path; - if ((err = populate_file_hash(ch, chf, contents))) { - os_file_close(&ch->manifest_file); - return err; - } - - return ErrorNone; -} - -Error cache_add_file(CacheHash *ch, Buf *path) { - Buf *resolved_path = buf_alloc(); - *resolved_path = os_path_resolve(&path, 1); - return cache_add_file_fetch(ch, resolved_path, nullptr); -} - -Error cache_add_dep_file(CacheHash *ch, Buf *dep_file_path, bool verbose) { - Error err; - Buf *contents = buf_alloc(); - if ((err = os_fetch_file_path(dep_file_path, contents))) { - if (err == ErrorFileNotFound) - return err; - if (verbose) { - fprintf(stderr, "%s: unable to read .d file: %s\n", err_str(err), buf_ptr(dep_file_path)); - } - return ErrorReadingDepFile; - } - auto it = stage2_DepTokenizer_init(buf_ptr(contents), buf_len(contents)); - // skip first token: target - { - auto result = stage2_DepTokenizer_next(&it); - switch (result.type_id) { - case stage2_DepNextResult::error: - if (verbose) { - fprintf(stderr, "%s: failed processing .d file: %s\n", result.textz, buf_ptr(dep_file_path)); - } - err = ErrorInvalidDepFile; - goto finish; - case stage2_DepNextResult::null: - err = ErrorNone; - goto finish; - case stage2_DepNextResult::target: - case stage2_DepNextResult::prereq: - err = ErrorNone; - break; - } - } - // Process 0+ preqreqs. - // clang is invoked in single-source mode so we never get more targets. - for (;;) { - auto result = stage2_DepTokenizer_next(&it); - switch (result.type_id) { - case stage2_DepNextResult::error: - if (verbose) { - fprintf(stderr, "%s: failed processing .d file: %s\n", result.textz, buf_ptr(dep_file_path)); - } - err = ErrorInvalidDepFile; - goto finish; - case stage2_DepNextResult::null: - case stage2_DepNextResult::target: - err = ErrorNone; - goto finish; - case stage2_DepNextResult::prereq: - break; - } - auto textbuf = buf_alloc(); - buf_init_from_str(textbuf, result.textz); - if ((err = cache_add_file(ch, textbuf))) { - if (verbose) { - fprintf(stderr, "unable to add %s to cache: %s\n", result.textz, err_str(err)); - fprintf(stderr, "when processing .d file: %s\n", buf_ptr(dep_file_path)); - } - goto finish; - } - } - - finish: - stage2_DepTokenizer_deinit(&it); - return err; -} - -static Error write_manifest_file(CacheHash *ch) { - Error err; - Buf contents = BUF_INIT; - buf_resize(&contents, 0); - uint8_t encoded_digest[65]; - encoded_digest[64] = 0; - for (size_t i = 0; i < ch->files.length; i += 1) { - CacheHashFile *chf = &ch->files.at(i); - base64_encode({encoded_digest, 64}, {chf->bin_digest, 48}); - buf_appendf(&contents, "%" ZIG_PRI_u64 " %" ZIG_PRI_u64 " %" ZIG_PRI_u64 " %s %s\n", - chf->attr.inode, chf->attr.mtime.sec, chf->attr.mtime.nsec, encoded_digest, buf_ptr(chf->path)); - } - if ((err = os_file_overwrite(ch->manifest_file, &contents))) - return err; - - return ErrorNone; -} - -Error cache_final(CacheHash *ch, Buf *out_digest) { - assert(ch->manifest_file_path != nullptr); - - // We don't close the manifest file yet, because we want to - // keep it locked until the API user is done using it. - // We also don't write out the manifest yet, because until - // cache_release is called we still might be working on creating - // the artifacts to cache. - - uint8_t bin_digest[48]; - int rc = blake2b_final(&ch->blake, bin_digest, 48); - assert(rc == 0); - buf_resize(out_digest, 64); - base64_encode(buf_to_slice(out_digest), {bin_digest, 48}); - - return ErrorNone; -} - -void cache_release(CacheHash *ch) { - assert(ch->manifest_file_path != nullptr); - - Error err; - - if (ch->manifest_dirty) { - if ((err = write_manifest_file(ch))) { - fprintf(stderr, "Warning: Unable to write cache file '%s': %s\n", - buf_ptr(ch->manifest_file_path), err_str(err)); - } - } - - os_file_close(&ch->manifest_file); -} - diff --git a/src/cache_hash.hpp b/src/cache_hash.hpp deleted file mode 100644 index ba2434076a8818c8308db213073e2bef62ecf283..0000000000000000000000000000000000000000 --- a/src/cache_hash.hpp +++ /dev/null @@ -1,83 +0,0 @@ -/* - * Copyright (c) 2018 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#ifndef ZIG_CACHE_HASH_HPP -#define ZIG_CACHE_HASH_HPP - -#include "blake2.h" -#include "os.hpp" - -struct LinkLib; - -struct CacheHashFile { - Buf *path; - OsFileAttr attr; - uint8_t bin_digest[48]; - Buf *contents; -}; - -struct CacheHash { - blake2b_state blake; - ZigList files; - Buf *manifest_dir; - Buf *manifest_file_path; - Buf b64_digest; - OsFile manifest_file; - bool manifest_dirty; - bool force_check_manifest; -}; - -// Always call this first to set up. -void cache_init(CacheHash *ch, Buf *manifest_dir); - -// Next, use the hash population functions to add the initial parameters. -void cache_mem(CacheHash *ch, const char *ptr, size_t len); -void cache_slice(CacheHash *ch, Slice slice); -void cache_str(CacheHash *ch, const char *ptr); -void cache_int(CacheHash *ch, int x); -void cache_bool(CacheHash *ch, bool x); -void cache_usize(CacheHash *ch, size_t x); -void cache_buf(CacheHash *ch, Buf *buf); -void cache_buf_opt(CacheHash *ch, Buf *buf); -void cache_list_of_link_lib(CacheHash *ch, LinkLib **ptr, size_t len); -void cache_list_of_buf(CacheHash *ch, Buf **ptr, size_t len); -void cache_list_of_file(CacheHash *ch, Buf **ptr, size_t len); -void cache_list_of_str(CacheHash *ch, const char **ptr, size_t len); -void cache_file(CacheHash *ch, Buf *path); -void cache_file_opt(CacheHash *ch, Buf *path); - -// Then call cache_hit when you're ready to see if you can skip the next step. -// out_b64_digest will be left unchanged if it was a cache miss. -// If you got a cache hit, the next step is cache_release. -// From this point on, there is a lock on the input params. Release -// the lock with cache_release. -// Set force_check_manifest if you plan to add files later, but have not -// added any files before calling cache_hit. CacheHash::b64_digest becomes -// available for use after this call, even in the case of a miss, and it -// is a hash of the input parameters only. -// If this function returns ErrorInvalidFormat, that error may be treated -// as a cache miss. -Error ATTRIBUTE_MUST_USE cache_hit(CacheHash *ch, Buf *out_b64_digest); - -// If you did not get a cache hit, call this function for every file -// that is depended on, and then finish with cache_final. -Error ATTRIBUTE_MUST_USE cache_add_file(CacheHash *ch, Buf *path); -// This opens a file created by -MD -MF args to Clang -Error ATTRIBUTE_MUST_USE cache_add_dep_file(CacheHash *ch, Buf *path, bool verbose); - -// This variant of cache_add_file returns the file contents. -// Also the file path argument must be already resolved. -Error ATTRIBUTE_MUST_USE cache_add_file_fetch(CacheHash *ch, Buf *resolved_path, Buf *contents); - -// out_b64_digest will be the same thing that cache_hit returns if you got a cache hit -Error ATTRIBUTE_MUST_USE cache_final(CacheHash *ch, Buf *out_b64_digest); - -// Until this function is called, no one will be able to get a lock on your input params. -void cache_release(CacheHash *ch); - - -#endif diff --git a/src/clang.zig b/src/clang.zig new file mode 100644 index 0000000000000000000000000000000000000000..255182908499a3b7c55484392bc7cf7a4b77965b --- /dev/null +++ b/src/clang.zig @@ -0,0 +1,1197 @@ +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/clang_options.zig b/src/clang_options.zig new file mode 100644 index 0000000000000000000000000000000000000000..42bfecb74622c888c4f399b7e00d24e109c243b0 --- /dev/null +++ b/src/clang_options.zig @@ -0,0 +1,134 @@ +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, + + zig_equivalent: @import("main.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/clang_options_data.zig b/src/clang_options_data.zig new file mode 100644 index 0000000000000000000000000000000000000000..bd1237bc002049d99f782eb8a3a978945f6d52c3 --- /dev/null +++ b/src/clang_options_data.zig @@ -0,0 +1,5874 @@ +// 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 = .preprocess_only, + .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 = .asm_only, + .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 = .preprocess_only, + .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 = .asm_only, + .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 = .preprocess_only, + .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("mlvi-cfi"), +flagpd1("mlvi-hardening"), +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-lvi-cfi"), +flagpd1("mno-lvi-hardening"), +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/codegen.cpp b/src/codegen.cpp deleted file mode 100644 index b5c1ca3a4117a6d9d62fc63b16cfde51d0df66f8..0000000000000000000000000000000000000000 --- a/src/codegen.cpp +++ /dev/null @@ -1,11337 +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 "compiler.hpp" -#include "config.h" -#include "errmsg.hpp" -#include "error.hpp" -#include "hash_map.hpp" -#include "ir.hpp" -#include "os.hpp" -#include "target.hpp" -#include "util.hpp" -#include "zig_llvm.h" -#include "stage2.h" -#include "dump_analysis.hpp" -#include "softfloat.hpp" -#include "mem_profile.hpp" - -#include -#include - -enum ResumeId { - ResumeIdManual, - ResumeIdReturn, - ResumeIdCall, -}; - -static ZigPackage *new_package(const char *root_src_dir, const char *root_src_path, const char *pkg_path) { - ZigPackage *entry = heap::c_allocator.create(); - entry->package_table.init(4); - buf_init_from_str(&entry->root_src_dir, root_src_dir); - buf_init_from_str(&entry->root_src_path, root_src_path); - buf_init_from_str(&entry->pkg_path, pkg_path); - return entry; -} - -ZigPackage *new_anonymous_package() { - return new_package("", "", ""); -} - -static const char *symbols_that_llvm_depends_on[] = { - "memcpy", - "memset", - "sqrt", - "powi", - "sin", - "cos", - "pow", - "exp", - "exp2", - "log", - "log10", - "log2", - "fma", - "fabs", - "minnum", - "maxnum", - "copysign", - "floor", - "ceil", - "trunc", - "rint", - "nearbyint", - "round", - // TODO probably all of compiler-rt needs to go here -}; - -void codegen_set_clang_argv(CodeGen *g, const char **args, size_t len) { - g->clang_argv = args; - g->clang_argv_len = len; -} - -void codegen_set_llvm_argv(CodeGen *g, const char **args, size_t len) { - g->llvm_argv = args; - g->llvm_argv_len = len; -} - -void codegen_set_test_filter(CodeGen *g, Buf *filter) { - g->test_filter = filter; -} - -void codegen_set_test_name_prefix(CodeGen *g, Buf *prefix) { - g->test_name_prefix = prefix; -} - -void codegen_set_lib_version(CodeGen *g, bool is_versioned, size_t major, size_t minor, size_t patch) { - g->is_versioned = is_versioned; - g->version_major = major; - g->version_minor = minor; - g->version_patch = patch; -} - -void codegen_set_each_lib_rpath(CodeGen *g, bool each_lib_rpath) { - g->each_lib_rpath = each_lib_rpath; -} - -void codegen_set_errmsg_color(CodeGen *g, ErrColor err_color) { - g->err_color = err_color; -} - -void codegen_set_strip(CodeGen *g, bool strip) { - g->strip_debug_symbols = strip; - if (!target_has_debug_info(g->zig_target)) { - g->strip_debug_symbols = true; - } -} - -void codegen_set_out_name(CodeGen *g, Buf *out_name) { - g->root_out_name = out_name; -} - -void codegen_add_lib_dir(CodeGen *g, const char *dir) { - g->lib_dirs.append(dir); -} - -void codegen_add_rpath(CodeGen *g, const char *name) { - g->rpath_list.append(buf_create_from_str(name)); -} - -LinkLib *codegen_add_link_lib(CodeGen *g, Buf *name) { - return add_link_lib(g, name); -} - -void codegen_add_forbidden_lib(CodeGen *codegen, Buf *lib) { - codegen->forbidden_libs.append(lib); -} - -void codegen_add_framework(CodeGen *g, const char *framework) { - g->darwin_frameworks.append(buf_create_from_str(framework)); -} - -void codegen_set_rdynamic(CodeGen *g, bool rdynamic) { - g->linker_rdynamic = rdynamic; -} - -void codegen_set_linker_script(CodeGen *g, const char *linker_script) { - g->linker_script = linker_script; -} - - -static void render_const_val(CodeGen *g, ZigValue *const_val, const char *name); -static void render_const_val_global(CodeGen *g, ZigValue *const_val, const char *name); -static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *name); -static void generate_error_name_table(CodeGen *g); -static bool value_is_all_undef(CodeGen *g, ZigValue *const_val); -static void gen_undef_init(CodeGen *g, uint32_t ptr_align_bytes, ZigType *value_type, LLVMValueRef ptr); -static LLVMValueRef build_alloca(CodeGen *g, ZigType *type_entry, const char *name, uint32_t alignment); -static LLVMValueRef gen_await_early_return(CodeGen *g, IrInstGen *source_instr, - LLVMValueRef target_frame_ptr, ZigType *result_type, ZigType *ptr_result_type, - LLVMValueRef result_loc, bool non_async); -static Error get_tmp_filename(CodeGen *g, Buf *out, Buf *suffix); - -static void addLLVMAttr(LLVMValueRef val, LLVMAttributeIndex attr_index, const char *attr_name) { - unsigned kind_id = LLVMGetEnumAttributeKindForName(attr_name, strlen(attr_name)); - assert(kind_id != 0); - LLVMAttributeRef llvm_attr = LLVMCreateEnumAttribute(LLVMGetGlobalContext(), kind_id, 0); - LLVMAddAttributeAtIndex(val, attr_index, llvm_attr); -} - -static void addLLVMAttrStr(LLVMValueRef val, LLVMAttributeIndex attr_index, - const char *attr_name, const char *attr_val) -{ - LLVMAttributeRef llvm_attr = LLVMCreateStringAttribute(LLVMGetGlobalContext(), - attr_name, (unsigned)strlen(attr_name), attr_val, (unsigned)strlen(attr_val)); - LLVMAddAttributeAtIndex(val, attr_index, llvm_attr); -} - -static void addLLVMAttrInt(LLVMValueRef val, LLVMAttributeIndex attr_index, - const char *attr_name, uint64_t attr_val) -{ - unsigned kind_id = LLVMGetEnumAttributeKindForName(attr_name, strlen(attr_name)); - assert(kind_id != 0); - LLVMAttributeRef llvm_attr = LLVMCreateEnumAttribute(LLVMGetGlobalContext(), kind_id, attr_val); - LLVMAddAttributeAtIndex(val, attr_index, llvm_attr); -} - -static void addLLVMFnAttr(LLVMValueRef fn_val, const char *attr_name) { - return addLLVMAttr(fn_val, -1, attr_name); -} - -static void addLLVMFnAttrStr(LLVMValueRef fn_val, const char *attr_name, const char *attr_val) { - return addLLVMAttrStr(fn_val, -1, attr_name, attr_val); -} - -static void addLLVMFnAttrInt(LLVMValueRef fn_val, const char *attr_name, uint64_t attr_val) { - return addLLVMAttrInt(fn_val, -1, attr_name, attr_val); -} - -static void addLLVMArgAttr(LLVMValueRef fn_val, unsigned param_index, const char *attr_name) { - return addLLVMAttr(fn_val, param_index + 1, attr_name); -} - -static void addLLVMArgAttrInt(LLVMValueRef fn_val, unsigned param_index, const char *attr_name, uint64_t attr_val) { - return addLLVMAttrInt(fn_val, param_index + 1, attr_name, attr_val); -} - -static bool is_symbol_available(CodeGen *g, const char *name) { - Buf *buf_name = buf_create_from_str(name); - bool result = - g->exported_symbol_names.maybe_get(buf_name) == nullptr && - g->external_symbol_names.maybe_get(buf_name) == nullptr; - buf_destroy(buf_name); - return result; -} - -static const char *get_mangled_name(CodeGen *g, const char *original_name) { - if (is_symbol_available(g, original_name)) - return original_name; - - int n = 0; - for (;; n += 1) { - const char *new_name = buf_ptr(buf_sprintf("%s.%d", original_name, n)); - if (is_symbol_available(g, new_name)) { - return new_name; - } - } -} - -static ZigLLVM_CallingConv get_llvm_cc(CodeGen *g, CallingConvention cc) { - switch (cc) { - case CallingConventionUnspecified: - return ZigLLVM_Fast; - case CallingConventionC: - return ZigLLVM_C; - case CallingConventionCold: - if ((g->zig_target->arch == ZigLLVM_x86 || - g->zig_target->arch == ZigLLVM_x86_64) && - g->zig_target->os != OsWindows) - return ZigLLVM_Cold; - return ZigLLVM_C; - case CallingConventionNaked: - zig_unreachable(); - case CallingConventionStdcall: - if (g->zig_target->arch == ZigLLVM_x86) - return ZigLLVM_X86_StdCall; - return ZigLLVM_C; - case CallingConventionFastcall: - if (g->zig_target->arch == ZigLLVM_x86) - return ZigLLVM_X86_FastCall; - return ZigLLVM_C; - case CallingConventionVectorcall: - if (g->zig_target->arch == ZigLLVM_x86) - return ZigLLVM_X86_VectorCall; - if (target_is_arm(g->zig_target) && - target_arch_pointer_bit_width(g->zig_target->arch) == 64) - return ZigLLVM_AArch64_VectorCall; - return ZigLLVM_C; - case CallingConventionThiscall: - if (g->zig_target->arch == ZigLLVM_x86) - return ZigLLVM_X86_ThisCall; - return ZigLLVM_C; - case CallingConventionAsync: - return ZigLLVM_Fast; - case CallingConventionAPCS: - if (target_is_arm(g->zig_target)) - return ZigLLVM_ARM_APCS; - return ZigLLVM_C; - case CallingConventionAAPCS: - if (target_is_arm(g->zig_target)) - return ZigLLVM_ARM_AAPCS; - return ZigLLVM_C; - case CallingConventionAAPCSVFP: - if (target_is_arm(g->zig_target)) - return ZigLLVM_ARM_AAPCS_VFP; - return ZigLLVM_C; - case CallingConventionInterrupt: - if (g->zig_target->arch == ZigLLVM_x86 || - g->zig_target->arch == ZigLLVM_x86_64) - return ZigLLVM_X86_INTR; - if (g->zig_target->arch == ZigLLVM_avr) - return ZigLLVM_AVR_INTR; - if (g->zig_target->arch == ZigLLVM_msp430) - return ZigLLVM_MSP430_INTR; - return ZigLLVM_C; - case CallingConventionSignal: - if (g->zig_target->arch == ZigLLVM_avr) - return ZigLLVM_AVR_SIGNAL; - return ZigLLVM_C; - } - zig_unreachable(); -} - -static void add_uwtable_attr(CodeGen *g, LLVMValueRef fn_val) { - if (g->zig_target->os == OsWindows) { - addLLVMFnAttr(fn_val, "uwtable"); - } -} - -static LLVMLinkage to_llvm_linkage(GlobalLinkageId id, bool is_extern) { - switch (id) { - case GlobalLinkageIdInternal: - return LLVMInternalLinkage; - case GlobalLinkageIdStrong: - return LLVMExternalLinkage; - case GlobalLinkageIdWeak: - if (is_extern) return LLVMExternalWeakLinkage; - return LLVMWeakODRLinkage; - case GlobalLinkageIdLinkOnce: - return LLVMLinkOnceODRLinkage; - } - zig_unreachable(); -} - -struct CalcLLVMFieldIndex { - uint32_t offset; - uint32_t field_index; -}; - -static void calc_llvm_field_index_add(CodeGen *g, CalcLLVMFieldIndex *calc, ZigType *ty) { - if (!type_has_bits(g, ty)) return; - uint32_t ty_align = get_abi_alignment(g, ty); - if (calc->offset % ty_align != 0) { - uint32_t llvm_align = LLVMABIAlignmentOfType(g->target_data_ref, get_llvm_type(g, ty)); - if (llvm_align >= ty_align) { - ty_align = llvm_align; // llvm's padding is sufficient - } else if (calc->offset) { - calc->field_index += 1; // zig will insert an extra padding field here - } - calc->offset += ty_align - (calc->offset % ty_align); // padding bytes - } - calc->offset += ty->abi_size; - calc->field_index += 1; -} - -// label (grep this): [fn_frame_struct_layout] -static void frame_index_trace_arg_calc(CodeGen *g, CalcLLVMFieldIndex *calc, ZigType *return_type) { - calc_llvm_field_index_add(g, calc, g->builtin_types.entry_usize); // function pointer - calc_llvm_field_index_add(g, calc, g->builtin_types.entry_usize); // resume index - calc_llvm_field_index_add(g, calc, g->builtin_types.entry_usize); // awaiter index - - if (type_has_bits(g, return_type)) { - calc_llvm_field_index_add(g, calc, g->builtin_types.entry_usize); // *ReturnType (callee's) - calc_llvm_field_index_add(g, calc, g->builtin_types.entry_usize); // *ReturnType (awaiter's) - calc_llvm_field_index_add(g, calc, return_type); // ReturnType - } -} - -static uint32_t frame_index_trace_arg(CodeGen *g, ZigType *return_type) { - CalcLLVMFieldIndex calc = {0}; - frame_index_trace_arg_calc(g, &calc, return_type); - return calc.field_index; -} - -// label (grep this): [fn_frame_struct_layout] -static void frame_index_arg_calc(CodeGen *g, CalcLLVMFieldIndex *calc, ZigType *return_type) { - frame_index_trace_arg_calc(g, calc, return_type); - - if (codegen_fn_has_err_ret_tracing_arg(g, return_type)) { - calc_llvm_field_index_add(g, calc, g->builtin_types.entry_usize); // *StackTrace (callee's) - calc_llvm_field_index_add(g, calc, g->builtin_types.entry_usize); // *StackTrace (awaiter's) - } -} - -// label (grep this): [fn_frame_struct_layout] -static uint32_t frame_index_trace_stack(CodeGen *g, ZigFn *fn) { - size_t field_index = 6; - bool have_stack_trace = codegen_fn_has_err_ret_tracing_arg(g, fn->type_entry->data.fn.fn_type_id.return_type); - if (have_stack_trace) { - field_index += 2; - } - field_index += fn->type_entry->data.fn.fn_type_id.param_count; - ZigType *locals_struct = fn->frame_type->data.frame.locals_struct; - TypeStructField *field = locals_struct->data.structure.fields[field_index]; - return field->gen_index; -} - - -static uint32_t get_err_ret_trace_arg_index(CodeGen *g, ZigFn *fn_table_entry) { - if (!g->have_err_ret_tracing) { - return UINT32_MAX; - } - if (fn_is_async(fn_table_entry)) { - return UINT32_MAX; - } - ZigType *fn_type = fn_table_entry->type_entry; - if (!fn_type_can_fail(&fn_type->data.fn.fn_type_id)) { - return UINT32_MAX; - } - ZigType *return_type = fn_type->data.fn.fn_type_id.return_type; - bool first_arg_ret = type_has_bits(g, return_type) && handle_is_ptr(g, return_type); - return first_arg_ret ? 1 : 0; -} - -static void maybe_export_dll(CodeGen *g, LLVMValueRef global_value, GlobalLinkageId linkage) { - if (linkage != GlobalLinkageIdInternal && g->zig_target->os == OsWindows && g->is_dynamic) { - LLVMSetDLLStorageClass(global_value, LLVMDLLExportStorageClass); - } -} - -static void maybe_import_dll(CodeGen *g, LLVMValueRef global_value, GlobalLinkageId linkage) { - if (linkage != GlobalLinkageIdInternal && g->zig_target->os == OsWindows) { - // TODO come up with a good explanation/understanding for why we never do - // DLLImportStorageClass. Empirically it only causes problems. But let's have - // this documented and then clean up the code accordingly. - //LLVMSetDLLStorageClass(global_value, LLVMDLLImportStorageClass); - } -} - -static bool cc_want_sret_attr(CallingConvention cc) { - switch (cc) { - case CallingConventionNaked: - zig_unreachable(); - case CallingConventionC: - case CallingConventionCold: - case CallingConventionInterrupt: - case CallingConventionSignal: - case CallingConventionStdcall: - case CallingConventionFastcall: - case CallingConventionVectorcall: - case CallingConventionThiscall: - case CallingConventionAPCS: - case CallingConventionAAPCS: - case CallingConventionAAPCSVFP: - return true; - case CallingConventionAsync: - case CallingConventionUnspecified: - return false; - } - zig_unreachable(); -} - -static bool codegen_have_frame_pointer(CodeGen *g) { - return g->build_mode == BuildModeDebug; -} - -static LLVMValueRef make_fn_llvm_value(CodeGen *g, ZigFn *fn) { - const char *unmangled_name = buf_ptr(&fn->symbol_name); - const char *symbol_name; - GlobalLinkageId linkage; - if (fn->body_node == nullptr) { - symbol_name = unmangled_name; - linkage = GlobalLinkageIdStrong; - } else if (fn->export_list.length == 0) { - symbol_name = get_mangled_name(g, unmangled_name); - linkage = GlobalLinkageIdInternal; - } else { - GlobalExport *fn_export = &fn->export_list.items[0]; - symbol_name = buf_ptr(&fn_export->name); - linkage = fn_export->linkage; - } - - CallingConvention cc = fn->type_entry->data.fn.fn_type_id.cc; - bool is_async = fn_is_async(fn); - - ZigType *fn_type = fn->type_entry; - // Make the raw_type_ref populated - resolve_llvm_types_fn(g, fn); - LLVMTypeRef fn_llvm_type = fn->raw_type_ref; - LLVMValueRef llvm_fn = nullptr; - if (fn->body_node == nullptr) { - const unsigned fn_addrspace = ZigLLVMDataLayoutGetProgramAddressSpace(g->target_data_ref); - LLVMValueRef existing_llvm_fn = LLVMGetNamedFunction(g->module, symbol_name); - if (existing_llvm_fn) { - return LLVMConstBitCast(existing_llvm_fn, LLVMPointerType(fn_llvm_type, fn_addrspace)); - } else { - Buf *buf_symbol_name = buf_create_from_str(symbol_name); - auto entry = g->exported_symbol_names.maybe_get(buf_symbol_name); - buf_destroy(buf_symbol_name); - - if (entry == nullptr) { - llvm_fn = LLVMAddFunction(g->module, symbol_name, fn_llvm_type); - - if (target_is_wasm(g->zig_target)) { - assert(fn->proto_node->type == NodeTypeFnProto); - AstNodeFnProto *fn_proto = &fn->proto_node->data.fn_proto; - if (fn_proto-> is_extern && fn_proto->lib_name != nullptr ) { - addLLVMFnAttrStr(llvm_fn, "wasm-import-module", buf_ptr(fn_proto->lib_name)); - } - } - } else { - assert(entry->value->id == TldIdFn); - TldFn *tld_fn = reinterpret_cast(entry->value); - // Make the raw_type_ref populated - resolve_llvm_types_fn(g, tld_fn->fn_entry); - tld_fn->fn_entry->llvm_value = LLVMAddFunction(g->module, symbol_name, - tld_fn->fn_entry->raw_type_ref); - llvm_fn = LLVMConstBitCast(tld_fn->fn_entry->llvm_value, LLVMPointerType(fn_llvm_type, fn_addrspace)); - return llvm_fn; - } - } - } else { - if (llvm_fn == nullptr) { - llvm_fn = LLVMAddFunction(g->module, symbol_name, fn_llvm_type); - } - - for (size_t i = 1; i < fn->export_list.length; i += 1) { - GlobalExport *fn_export = &fn->export_list.items[i]; - LLVMAddAlias(g->module, LLVMTypeOf(llvm_fn), llvm_fn, buf_ptr(&fn_export->name)); - } - } - - switch (fn->fn_inline) { - case FnInlineAlways: - addLLVMFnAttr(llvm_fn, "alwaysinline"); - g->inline_fns.append(fn); - break; - case FnInlineNever: - addLLVMFnAttr(llvm_fn, "noinline"); - break; - case FnInlineAuto: - if (fn->alignstack_value != 0) { - addLLVMFnAttr(llvm_fn, "noinline"); - } - break; - } - - if (cc == CallingConventionNaked) { - addLLVMFnAttr(llvm_fn, "naked"); - } else { - ZigLLVMFunctionSetCallingConv(llvm_fn, get_llvm_cc(g, cc)); - } - - bool want_cold = fn->is_cold || cc == CallingConventionCold; - if (want_cold) { - ZigLLVMAddFunctionAttrCold(llvm_fn); - } - - - LLVMSetLinkage(llvm_fn, to_llvm_linkage(linkage, fn->body_node == nullptr)); - - if (linkage == GlobalLinkageIdInternal) { - LLVMSetUnnamedAddr(llvm_fn, true); - } - - ZigType *return_type = fn_type->data.fn.fn_type_id.return_type; - if (return_type->id == ZigTypeIdUnreachable) { - addLLVMFnAttr(llvm_fn, "noreturn"); - } - - if (fn->body_node != nullptr) { - maybe_export_dll(g, llvm_fn, linkage); - - bool want_fn_safety = g->build_mode != BuildModeFastRelease && - g->build_mode != BuildModeSmallRelease && - !fn->def_scope->safety_off; - if (want_fn_safety) { - if (g->libc_link_lib != nullptr) { - addLLVMFnAttr(llvm_fn, "sspstrong"); - addLLVMFnAttrStr(llvm_fn, "stack-protector-buffer-size", "4"); - } - } - if (g->have_stack_probing && !fn->def_scope->safety_off) { - addLLVMFnAttrStr(llvm_fn, "probe-stack", "__zig_probe_stack"); - } else if (g->zig_target->os == OsUefi) { - addLLVMFnAttrStr(llvm_fn, "no-stack-arg-probe", ""); - } - } else { - maybe_import_dll(g, llvm_fn, linkage); - } - - if (fn->alignstack_value != 0) { - addLLVMFnAttrInt(llvm_fn, "alignstack", fn->alignstack_value); - } - - addLLVMFnAttr(llvm_fn, "nounwind"); - add_uwtable_attr(g, llvm_fn); - addLLVMFnAttr(llvm_fn, "nobuiltin"); - if (codegen_have_frame_pointer(g) && fn->fn_inline != FnInlineAlways) { - ZigLLVMAddFunctionAttr(llvm_fn, "frame-pointer", "all"); - } - if (fn->section_name) { - LLVMSetSection(llvm_fn, buf_ptr(fn->section_name)); - } - if (fn->align_bytes > 0) { - LLVMSetAlignment(llvm_fn, (unsigned)fn->align_bytes); - } else { - // We'd like to set the best alignment for the function here, but on Darwin LLVM gives - // "Cannot getTypeInfo() on a type that is unsized!" assertion failure when calling - // any of the functions for getting alignment. Not specifying the alignment should - // use the ABI alignment, which is fine. - } - - if (is_async) { - addLLVMArgAttr(llvm_fn, 0, "nonnull"); - } else { - unsigned init_gen_i = 0; - if (!type_has_bits(g, return_type)) { - // nothing to do - } else if (type_is_nonnull_ptr(g, return_type)) { - addLLVMAttr(llvm_fn, 0, "nonnull"); - } else if (want_first_arg_sret(g, &fn_type->data.fn.fn_type_id)) { - // Sret pointers must not be address 0 - addLLVMArgAttr(llvm_fn, 0, "nonnull"); - addLLVMArgAttr(llvm_fn, 0, "sret"); - if (cc_want_sret_attr(cc)) { - addLLVMArgAttr(llvm_fn, 0, "noalias"); - } - init_gen_i = 1; - } - - // set parameter attributes - FnWalk fn_walk = {}; - fn_walk.id = FnWalkIdAttrs; - fn_walk.data.attrs.fn = fn; - fn_walk.data.attrs.llvm_fn = llvm_fn; - fn_walk.data.attrs.gen_i = init_gen_i; - walk_function_params(g, fn_type, &fn_walk); - - uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, fn); - if (err_ret_trace_arg_index != UINT32_MAX) { - // Error return trace memory is in the stack, which is impossible to be at address 0 - // on any architecture. - addLLVMArgAttr(llvm_fn, (unsigned)err_ret_trace_arg_index, "nonnull"); - } - } - - return llvm_fn; -} - -static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn) { - if (fn->llvm_value) - return fn->llvm_value; - - fn->llvm_value = make_fn_llvm_value(g, fn); - fn->llvm_name = strdup(LLVMGetValueName(fn->llvm_value)); - return fn->llvm_value; -} - -static ZigLLVMDIScope *get_di_scope(CodeGen *g, Scope *scope) { - if (scope->di_scope) - return scope->di_scope; - - ZigType *import = get_scope_import(scope); - switch (scope->id) { - case ScopeIdCImport: - zig_unreachable(); - case ScopeIdFnDef: - { - assert(scope->parent); - ScopeFnDef *fn_scope = (ScopeFnDef *)scope; - ZigFn *fn_table_entry = fn_scope->fn_entry; - if (!fn_table_entry->proto_node) - return get_di_scope(g, scope->parent); - unsigned line_number = (unsigned)(fn_table_entry->proto_node->line == 0) ? - 0 : (fn_table_entry->proto_node->line + 1); - unsigned scope_line = line_number; - bool is_definition = fn_table_entry->body_node != nullptr; - bool is_optimized = g->build_mode != BuildModeDebug; - bool is_internal_linkage = (fn_table_entry->body_node != nullptr && - fn_table_entry->export_list.length == 0); - unsigned flags = ZigLLVM_DIFlags_StaticMember; - ZigLLVMDIScope *fn_di_scope = get_di_scope(g, scope->parent); - assert(fn_di_scope != nullptr); - assert(fn_table_entry->raw_di_type != nullptr); - ZigLLVMDISubprogram *subprogram = ZigLLVMCreateFunction(g->dbuilder, - fn_di_scope, buf_ptr(&fn_table_entry->symbol_name), "", - import->data.structure.root_struct->di_file, line_number, - fn_table_entry->raw_di_type, is_internal_linkage, - is_definition, scope_line, flags, is_optimized, nullptr); - - scope->di_scope = ZigLLVMSubprogramToScope(subprogram); - if (!g->strip_debug_symbols) { - ZigLLVMFnSetSubprogram(fn_llvm_value(g, fn_table_entry), subprogram); - } - return scope->di_scope; - } - case ScopeIdDecls: - if (scope->parent) { - ScopeDecls *decls_scope = (ScopeDecls *)scope; - assert(decls_scope->container_type); - scope->di_scope = ZigLLVMTypeToScope(get_llvm_di_type(g, decls_scope->container_type)); - } else { - scope->di_scope = ZigLLVMFileToScope(import->data.structure.root_struct->di_file); - } - return scope->di_scope; - case ScopeIdBlock: - case ScopeIdDefer: - { - assert(scope->parent); - ZigLLVMDILexicalBlock *di_block = ZigLLVMCreateLexicalBlock(g->dbuilder, - get_di_scope(g, scope->parent), - import->data.structure.root_struct->di_file, - (unsigned)scope->source_node->line + 1, - (unsigned)scope->source_node->column + 1); - scope->di_scope = ZigLLVMLexicalBlockToScope(di_block); - return scope->di_scope; - } - case ScopeIdVarDecl: - case ScopeIdDeferExpr: - case ScopeIdLoop: - case ScopeIdSuspend: - case ScopeIdCompTime: - case ScopeIdNoSuspend: - case ScopeIdRuntime: - case ScopeIdTypeOf: - case ScopeIdExpr: - return get_di_scope(g, scope->parent); - } - zig_unreachable(); -} - -static void clear_debug_source_node(CodeGen *g) { - ZigLLVMClearCurrentDebugLocation(g->builder); -} - -static LLVMValueRef get_arithmetic_overflow_fn(CodeGen *g, ZigType *operand_type, - const char *signed_name, const char *unsigned_name) -{ - ZigType *int_type = (operand_type->id == ZigTypeIdVector) ? operand_type->data.vector.elem_type : operand_type; - char fn_name[64]; - - assert(int_type->id == ZigTypeIdInt); - const char *signed_str = int_type->data.integral.is_signed ? signed_name : unsigned_name; - - LLVMTypeRef param_types[] = { - get_llvm_type(g, operand_type), - get_llvm_type(g, operand_type), - }; - - if (operand_type->id == ZigTypeIdVector) { - sprintf(fn_name, "llvm.%s.with.overflow.v%" PRIu64 "i%" PRIu32, signed_str, - operand_type->data.vector.len, int_type->data.integral.bit_count); - - LLVMTypeRef return_elem_types[] = { - get_llvm_type(g, operand_type), - LLVMVectorType(LLVMInt1Type(), operand_type->data.vector.len), - }; - LLVMTypeRef return_struct_type = LLVMStructType(return_elem_types, 2, false); - LLVMTypeRef fn_type = LLVMFunctionType(return_struct_type, param_types, 2, false); - LLVMValueRef fn_val = LLVMAddFunction(g->module, fn_name, fn_type); - assert(LLVMGetIntrinsicID(fn_val)); - return fn_val; - } else { - sprintf(fn_name, "llvm.%s.with.overflow.i%" PRIu32, signed_str, int_type->data.integral.bit_count); - - LLVMTypeRef return_elem_types[] = { - get_llvm_type(g, operand_type), - LLVMInt1Type(), - }; - LLVMTypeRef return_struct_type = LLVMStructType(return_elem_types, 2, false); - LLVMTypeRef fn_type = LLVMFunctionType(return_struct_type, param_types, 2, false); - LLVMValueRef fn_val = LLVMAddFunction(g->module, fn_name, fn_type); - assert(LLVMGetIntrinsicID(fn_val)); - return fn_val; - } -} - -static LLVMValueRef get_int_overflow_fn(CodeGen *g, ZigType *operand_type, AddSubMul add_sub_mul) { - ZigType *int_type = (operand_type->id == ZigTypeIdVector) ? operand_type->data.vector.elem_type : operand_type; - assert(int_type->id == ZigTypeIdInt); - - ZigLLVMFnKey key = {}; - key.id = ZigLLVMFnIdOverflowArithmetic; - key.data.overflow_arithmetic.is_signed = int_type->data.integral.is_signed; - key.data.overflow_arithmetic.add_sub_mul = add_sub_mul; - key.data.overflow_arithmetic.bit_count = (uint32_t)int_type->data.integral.bit_count; - key.data.overflow_arithmetic.vector_len = (operand_type->id == ZigTypeIdVector) ? - operand_type->data.vector.len : 0; - - auto existing_entry = g->llvm_fn_table.maybe_get(key); - if (existing_entry) - return existing_entry->value; - - LLVMValueRef fn_val; - switch (add_sub_mul) { - case AddSubMulAdd: - fn_val = get_arithmetic_overflow_fn(g, operand_type, "sadd", "uadd"); - break; - case AddSubMulSub: - fn_val = get_arithmetic_overflow_fn(g, operand_type, "ssub", "usub"); - break; - case AddSubMulMul: - fn_val = get_arithmetic_overflow_fn(g, operand_type, "smul", "umul"); - break; - } - - g->llvm_fn_table.put(key, fn_val); - return fn_val; -} - -static LLVMValueRef get_float_fn(CodeGen *g, ZigType *type_entry, ZigLLVMFnId fn_id, BuiltinFnId op) { - assert(type_entry->id == ZigTypeIdFloat || - type_entry->id == ZigTypeIdVector); - - bool is_vector = (type_entry->id == ZigTypeIdVector); - ZigType *float_type = is_vector ? type_entry->data.vector.elem_type : type_entry; - - ZigLLVMFnKey key = {}; - key.id = fn_id; - key.data.floating.bit_count = (uint32_t)float_type->data.floating.bit_count; - key.data.floating.vector_len = is_vector ? (uint32_t)type_entry->data.vector.len : 0; - key.data.floating.op = op; - - auto existing_entry = g->llvm_fn_table.maybe_get(key); - if (existing_entry) - return existing_entry->value; - - const char *name; - uint32_t num_args; - if (fn_id == ZigLLVMFnIdFMA) { - name = "fma"; - num_args = 3; - } else if (fn_id == ZigLLVMFnIdFloatOp) { - name = float_op_to_name(op); - num_args = 1; - } else { - zig_unreachable(); - } - - char fn_name[64]; - if (is_vector) - sprintf(fn_name, "llvm.%s.v%" PRIu32 "f%" PRIu32, name, key.data.floating.vector_len, key.data.floating.bit_count); - else - sprintf(fn_name, "llvm.%s.f%" PRIu32, name, key.data.floating.bit_count); - LLVMTypeRef float_type_ref = get_llvm_type(g, type_entry); - LLVMTypeRef return_elem_types[3] = { - float_type_ref, - float_type_ref, - float_type_ref, - }; - LLVMTypeRef fn_type = LLVMFunctionType(float_type_ref, return_elem_types, num_args, false); - LLVMValueRef fn_val = LLVMAddFunction(g->module, fn_name, fn_type); - assert(LLVMGetIntrinsicID(fn_val)); - - g->llvm_fn_table.put(key, fn_val); - return fn_val; -} - -static LLVMValueRef gen_store_untyped(CodeGen *g, LLVMValueRef value, LLVMValueRef ptr, - uint32_t alignment, bool is_volatile) -{ - LLVMValueRef instruction = LLVMBuildStore(g->builder, value, ptr); - if (is_volatile) LLVMSetVolatile(instruction, true); - if (alignment != 0) { - LLVMSetAlignment(instruction, alignment); - } - return instruction; -} - -static LLVMValueRef gen_store(CodeGen *g, LLVMValueRef value, LLVMValueRef ptr, ZigType *ptr_type) { - assert(ptr_type->id == ZigTypeIdPointer); - uint32_t alignment = get_ptr_align(g, ptr_type); - return gen_store_untyped(g, value, ptr, alignment, ptr_type->data.pointer.is_volatile); -} - -static LLVMValueRef gen_load_untyped(CodeGen *g, LLVMValueRef ptr, uint32_t alignment, bool is_volatile, - const char *name) -{ - LLVMValueRef result = LLVMBuildLoad(g->builder, ptr, name); - if (is_volatile) LLVMSetVolatile(result, true); - if (alignment == 0) { - LLVMSetAlignment(result, LLVMABIAlignmentOfType(g->target_data_ref, LLVMGetElementType(LLVMTypeOf(ptr)))); - } else { - LLVMSetAlignment(result, alignment); - } - return result; -} - -static LLVMValueRef gen_load(CodeGen *g, LLVMValueRef ptr, ZigType *ptr_type, const char *name) { - assert(ptr_type->id == ZigTypeIdPointer); - uint32_t alignment = get_ptr_align(g, ptr_type); - return gen_load_untyped(g, ptr, alignment, ptr_type->data.pointer.is_volatile, name); -} - -static LLVMValueRef get_handle_value(CodeGen *g, LLVMValueRef ptr, ZigType *type, ZigType *ptr_type) { - if (type_has_bits(g, type)) { - if (handle_is_ptr(g, type)) { - return ptr; - } else { - assert(ptr_type->id == ZigTypeIdPointer); - return gen_load(g, ptr, ptr_type, ""); - } - } else { - return nullptr; - } -} - -static void ir_assert_impl(bool ok, IrInstGen *source_instruction, const char *file, unsigned int line) { - if (ok) return; - src_assert_impl(ok, source_instruction->base.source_node, file, line); -} - -#define ir_assert(OK, SOURCE_INSTRUCTION) ir_assert_impl((OK), (SOURCE_INSTRUCTION), __FILE__, __LINE__) - -static bool ir_want_fast_math(CodeGen *g, IrInstGen *instruction) { - // TODO memoize - Scope *scope = instruction->base.scope; - while (scope) { - if (scope->id == ScopeIdBlock) { - ScopeBlock *block_scope = (ScopeBlock *)scope; - if (block_scope->fast_math_set_node) - return block_scope->fast_math_on; - } else if (scope->id == ScopeIdDecls) { - ScopeDecls *decls_scope = (ScopeDecls *)scope; - if (decls_scope->fast_math_set_node) - return decls_scope->fast_math_on; - } - scope = scope->parent; - } - return false; -} - -static bool ir_want_runtime_safety_scope(CodeGen *g, Scope *scope) { - // TODO memoize - while (scope) { - if (scope->id == ScopeIdBlock) { - ScopeBlock *block_scope = (ScopeBlock *)scope; - if (block_scope->safety_set_node) - return !block_scope->safety_off; - } else if (scope->id == ScopeIdDecls) { - ScopeDecls *decls_scope = (ScopeDecls *)scope; - if (decls_scope->safety_set_node) - return !decls_scope->safety_off; - } - scope = scope->parent; - } - - return (g->build_mode != BuildModeFastRelease && - g->build_mode != BuildModeSmallRelease); -} - -static bool ir_want_runtime_safety(CodeGen *g, IrInstGen *instruction) { - return ir_want_runtime_safety_scope(g, instruction->base.scope); -} - -static Buf *panic_msg_buf(PanicMsgId msg_id) { - switch (msg_id) { - case PanicMsgIdCount: - zig_unreachable(); - case PanicMsgIdBoundsCheckFailure: - return buf_create_from_str("index out of bounds"); - case PanicMsgIdCastNegativeToUnsigned: - return buf_create_from_str("attempt to cast negative value to unsigned integer"); - case PanicMsgIdCastTruncatedData: - return buf_create_from_str("integer cast truncated bits"); - case PanicMsgIdIntegerOverflow: - return buf_create_from_str("integer overflow"); - case PanicMsgIdShlOverflowedBits: - return buf_create_from_str("left shift overflowed bits"); - case PanicMsgIdShrOverflowedBits: - return buf_create_from_str("right shift overflowed bits"); - case PanicMsgIdDivisionByZero: - return buf_create_from_str("division by zero"); - case PanicMsgIdRemainderDivisionByZero: - return buf_create_from_str("remainder division by zero or negative value"); - case PanicMsgIdExactDivisionRemainder: - return buf_create_from_str("exact division produced remainder"); - case PanicMsgIdUnwrapOptionalFail: - return buf_create_from_str("attempt to use null value"); - case PanicMsgIdUnreachable: - return buf_create_from_str("reached unreachable code"); - case PanicMsgIdInvalidErrorCode: - return buf_create_from_str("invalid error code"); - case PanicMsgIdIncorrectAlignment: - return buf_create_from_str("incorrect alignment"); - case PanicMsgIdBadUnionField: - return buf_create_from_str("access of inactive union field"); - case PanicMsgIdBadEnumValue: - return buf_create_from_str("invalid enum value"); - case PanicMsgIdFloatToInt: - return buf_create_from_str("integer part of floating point value out of bounds"); - case PanicMsgIdPtrCastNull: - return buf_create_from_str("cast causes pointer to be null"); - case PanicMsgIdBadResume: - return buf_create_from_str("resumed an async function which already returned"); - case PanicMsgIdBadAwait: - return buf_create_from_str("async function awaited twice"); - case PanicMsgIdBadReturn: - return buf_create_from_str("async function returned twice"); - case PanicMsgIdResumedAnAwaitingFn: - return buf_create_from_str("awaiting function resumed"); - case PanicMsgIdFrameTooSmall: - return buf_create_from_str("frame too small"); - case PanicMsgIdResumedFnPendingAwait: - return buf_create_from_str("resumed an async function which can only be awaited"); - case PanicMsgIdBadNoSuspendCall: - return buf_create_from_str("async function called in nosuspend scope suspended"); - case PanicMsgIdResumeNotSuspendedFn: - return buf_create_from_str("resumed a non-suspended function"); - case PanicMsgIdBadSentinel: - return buf_create_from_str("sentinel mismatch"); - case PanicMsgIdShxTooBigRhs: - return buf_create_from_str("shift amount is greater than the type size"); - } - zig_unreachable(); -} - -static LLVMValueRef get_panic_msg_ptr_val(CodeGen *g, PanicMsgId msg_id) { - ZigValue *val = &g->panic_msg_vals[msg_id]; - if (!val->llvm_global) { - - Buf *buf_msg = panic_msg_buf(msg_id); - ZigValue *array_val = create_const_str_lit(g, buf_msg)->data.x_ptr.data.ref.pointee; - init_const_slice(g, val, array_val, 0, buf_len(buf_msg), true); - - render_const_val(g, val, ""); - render_const_val_global(g, val, ""); - - assert(val->llvm_global); - } - - ZigType *u8_ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false, - PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0, false); - ZigType *str_type = get_slice_type(g, u8_ptr_type); - return LLVMConstBitCast(val->llvm_global, LLVMPointerType(get_llvm_type(g, str_type), 0)); -} - -static ZigType *ptr_to_stack_trace_type(CodeGen *g) { - return get_pointer_to_type(g, get_stack_trace_type(g), false); -} - -static void gen_panic(CodeGen *g, LLVMValueRef msg_arg, LLVMValueRef stack_trace_arg, - bool stack_trace_is_llvm_alloca) -{ - assert(g->panic_fn != nullptr); - LLVMValueRef fn_val = fn_llvm_value(g, g->panic_fn); - ZigLLVM_CallingConv llvm_cc = get_llvm_cc(g, g->panic_fn->type_entry->data.fn.fn_type_id.cc); - if (stack_trace_arg == nullptr) { - stack_trace_arg = LLVMConstNull(get_llvm_type(g, ptr_to_stack_trace_type(g))); - } - LLVMValueRef args[] = { - msg_arg, - stack_trace_arg, - }; - ZigLLVMBuildCall(g->builder, fn_val, args, 2, llvm_cc, ZigLLVM_CallAttrAuto, ""); - if (!stack_trace_is_llvm_alloca) { - // The stack trace argument is not in the stack of the caller, so - // we'd like to set tail call here, but because slices (the type of msg_arg) are - // still passed as pointers (see https://github.com/ziglang/zig/issues/561) we still - // cannot make this a tail call. - //LLVMSetTailCall(call_instruction, true); - } - LLVMBuildUnreachable(g->builder); -} - -// TODO update most callsites to call gen_assertion instead of this -static void gen_safety_crash(CodeGen *g, PanicMsgId msg_id) { - gen_panic(g, get_panic_msg_ptr_val(g, msg_id), nullptr, false); -} - -static void gen_assertion_scope(CodeGen *g, PanicMsgId msg_id, Scope *source_scope) { - if (ir_want_runtime_safety_scope(g, source_scope)) { - gen_safety_crash(g, msg_id); - } else { - LLVMBuildUnreachable(g->builder); - } -} - -static void gen_assertion(CodeGen *g, PanicMsgId msg_id, IrInstGen *source_instruction) { - return gen_assertion_scope(g, msg_id, source_instruction->base.scope); -} - -static LLVMValueRef gen_wasm_memory_size(CodeGen *g) { - if (g->wasm_memory_size) - return g->wasm_memory_size; - - // TODO adjust for wasm64 as well - // declare i32 @llvm.wasm.memory.size.i32(i32) nounwind readonly - LLVMTypeRef param_type = LLVMInt32Type(); - LLVMTypeRef fn_type = LLVMFunctionType(LLVMInt32Type(), ¶m_type, 1, false); - g->wasm_memory_size = LLVMAddFunction(g->module, "llvm.wasm.memory.size.i32", fn_type); - assert(LLVMGetIntrinsicID(g->wasm_memory_size)); - - return g->wasm_memory_size; -} - -static LLVMValueRef gen_wasm_memory_grow(CodeGen *g) { - if (g->wasm_memory_grow) - return g->wasm_memory_grow; - - // TODO adjust for wasm64 as well - // declare i32 @llvm.wasm.memory.grow.i32(i32, i32) nounwind - LLVMTypeRef param_types[] = { - LLVMInt32Type(), - LLVMInt32Type(), - }; - LLVMTypeRef fn_type = LLVMFunctionType(LLVMInt32Type(), param_types, 2, false); - g->wasm_memory_grow = LLVMAddFunction(g->module, "llvm.wasm.memory.grow.i32", fn_type); - assert(LLVMGetIntrinsicID(g->wasm_memory_grow)); - - return g->wasm_memory_grow; -} - -static LLVMValueRef get_stacksave_fn_val(CodeGen *g) { - if (g->stacksave_fn_val) - return g->stacksave_fn_val; - - // declare i8* @llvm.stacksave() - - LLVMTypeRef fn_type = LLVMFunctionType(LLVMPointerType(LLVMInt8Type(), 0), nullptr, 0, false); - g->stacksave_fn_val = LLVMAddFunction(g->module, "llvm.stacksave", fn_type); - assert(LLVMGetIntrinsicID(g->stacksave_fn_val)); - - return g->stacksave_fn_val; -} - -static LLVMValueRef get_stackrestore_fn_val(CodeGen *g) { - if (g->stackrestore_fn_val) - return g->stackrestore_fn_val; - - // declare void @llvm.stackrestore(i8* %ptr) - - LLVMTypeRef param_type = LLVMPointerType(LLVMInt8Type(), 0); - LLVMTypeRef fn_type = LLVMFunctionType(LLVMVoidType(), ¶m_type, 1, false); - g->stackrestore_fn_val = LLVMAddFunction(g->module, "llvm.stackrestore", fn_type); - assert(LLVMGetIntrinsicID(g->stackrestore_fn_val)); - - return g->stackrestore_fn_val; -} - -static LLVMValueRef get_write_register_fn_val(CodeGen *g) { - if (g->write_register_fn_val) - return g->write_register_fn_val; - - // declare void @llvm.write_register.i64(metadata, i64 @value) - // !0 = !{!"sp\00"} - - LLVMTypeRef param_types[] = { - LLVMMetadataTypeInContext(LLVMGetGlobalContext()), - LLVMIntType(g->pointer_size_bytes * 8), - }; - - LLVMTypeRef fn_type = LLVMFunctionType(LLVMVoidType(), param_types, 2, false); - Buf *name = buf_sprintf("llvm.write_register.i%d", g->pointer_size_bytes * 8); - g->write_register_fn_val = LLVMAddFunction(g->module, buf_ptr(name), fn_type); - assert(LLVMGetIntrinsicID(g->write_register_fn_val)); - - return g->write_register_fn_val; -} - -static LLVMValueRef get_return_address_fn_val(CodeGen *g) { - if (g->return_address_fn_val) - return g->return_address_fn_val; - - ZigType *return_type = get_pointer_to_type(g, g->builtin_types.entry_u8, true); - - LLVMTypeRef fn_type = LLVMFunctionType(get_llvm_type(g, return_type), - &g->builtin_types.entry_i32->llvm_type, 1, false); - g->return_address_fn_val = LLVMAddFunction(g->module, "llvm.returnaddress", fn_type); - assert(LLVMGetIntrinsicID(g->return_address_fn_val)); - - return g->return_address_fn_val; -} - -static LLVMValueRef get_add_error_return_trace_addr_fn(CodeGen *g) { - if (g->add_error_return_trace_addr_fn_val != nullptr) - return g->add_error_return_trace_addr_fn_val; - - LLVMTypeRef arg_types[] = { - get_llvm_type(g, ptr_to_stack_trace_type(g)), - g->builtin_types.entry_usize->llvm_type, - }; - LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), arg_types, 2, false); - - const char *fn_name = get_mangled_name(g, "__zig_add_err_ret_trace_addr"); - LLVMValueRef fn_val = LLVMAddFunction(g->module, fn_name, fn_type_ref); - addLLVMFnAttr(fn_val, "alwaysinline"); - LLVMSetLinkage(fn_val, LLVMInternalLinkage); - ZigLLVMFunctionSetCallingConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified)); - addLLVMFnAttr(fn_val, "nounwind"); - add_uwtable_attr(g, fn_val); - // Error return trace memory is in the stack, which is impossible to be at address 0 - // on any architecture. - addLLVMArgAttr(fn_val, (unsigned)0, "nonnull"); - if (codegen_have_frame_pointer(g)) { - ZigLLVMAddFunctionAttr(fn_val, "frame-pointer", "all"); - } - - LLVMBasicBlockRef entry_block = LLVMAppendBasicBlock(fn_val, "Entry"); - LLVMBasicBlockRef prev_block = LLVMGetInsertBlock(g->builder); - LLVMValueRef prev_debug_location = LLVMGetCurrentDebugLocation(g->builder); - LLVMPositionBuilderAtEnd(g->builder, entry_block); - ZigLLVMClearCurrentDebugLocation(g->builder); - - LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; - - // stack_trace.instruction_addresses[stack_trace.index & (stack_trace.instruction_addresses.len - 1)] = return_address; - - LLVMValueRef err_ret_trace_ptr = LLVMGetParam(fn_val, 0); - LLVMValueRef address_value = LLVMGetParam(fn_val, 1); - - size_t index_field_index = g->stack_trace_type->data.structure.fields[0]->gen_index; - LLVMValueRef index_field_ptr = LLVMBuildStructGEP(g->builder, err_ret_trace_ptr, (unsigned)index_field_index, ""); - size_t addresses_field_index = g->stack_trace_type->data.structure.fields[1]->gen_index; - LLVMValueRef addresses_field_ptr = LLVMBuildStructGEP(g->builder, err_ret_trace_ptr, (unsigned)addresses_field_index, ""); - - ZigType *slice_type = g->stack_trace_type->data.structure.fields[1]->type_entry; - size_t ptr_field_index = slice_type->data.structure.fields[slice_ptr_index]->gen_index; - LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)ptr_field_index, ""); - size_t len_field_index = slice_type->data.structure.fields[slice_len_index]->gen_index; - LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)len_field_index, ""); - - LLVMValueRef len_value = gen_load_untyped(g, len_field_ptr, 0, false, ""); - LLVMValueRef index_val = gen_load_untyped(g, index_field_ptr, 0, false, ""); - LLVMValueRef len_val_minus_one = LLVMBuildSub(g->builder, len_value, LLVMConstInt(usize_type_ref, 1, false), ""); - LLVMValueRef masked_val = LLVMBuildAnd(g->builder, index_val, len_val_minus_one, ""); - LLVMValueRef address_indices[] = { - masked_val, - }; - - LLVMValueRef ptr_value = gen_load_untyped(g, ptr_field_ptr, 0, false, ""); - LLVMValueRef address_slot = LLVMBuildInBoundsGEP(g->builder, ptr_value, address_indices, 1, ""); - - gen_store_untyped(g, address_value, address_slot, 0, false); - - // stack_trace.index += 1; - LLVMValueRef index_plus_one_val = LLVMBuildNUWAdd(g->builder, index_val, LLVMConstInt(usize_type_ref, 1, false), ""); - gen_store_untyped(g, index_plus_one_val, index_field_ptr, 0, false); - - // return; - LLVMBuildRetVoid(g->builder); - - LLVMPositionBuilderAtEnd(g->builder, prev_block); - if (!g->strip_debug_symbols) { - LLVMSetCurrentDebugLocation(g->builder, prev_debug_location); - } - - g->add_error_return_trace_addr_fn_val = fn_val; - return fn_val; -} - -static LLVMValueRef get_return_err_fn(CodeGen *g) { - if (g->return_err_fn != nullptr) - return g->return_err_fn; - - assert(g->err_tag_type != nullptr); - - LLVMTypeRef arg_types[] = { - // error return trace pointer - get_llvm_type(g, ptr_to_stack_trace_type(g)), - }; - LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), arg_types, 1, false); - - const char *fn_name = get_mangled_name(g, "__zig_return_error"); - LLVMValueRef fn_val = LLVMAddFunction(g->module, fn_name, fn_type_ref); - addLLVMFnAttr(fn_val, "noinline"); // so that we can look at return address - addLLVMFnAttr(fn_val, "cold"); - LLVMSetLinkage(fn_val, LLVMInternalLinkage); - ZigLLVMFunctionSetCallingConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified)); - addLLVMFnAttr(fn_val, "nounwind"); - add_uwtable_attr(g, fn_val); - if (codegen_have_frame_pointer(g)) { - ZigLLVMAddFunctionAttr(fn_val, "frame-pointer", "all"); - } - - // this is above the ZigLLVMClearCurrentDebugLocation - LLVMValueRef add_error_return_trace_addr_fn_val = get_add_error_return_trace_addr_fn(g); - - LLVMBasicBlockRef entry_block = LLVMAppendBasicBlock(fn_val, "Entry"); - LLVMBasicBlockRef prev_block = LLVMGetInsertBlock(g->builder); - LLVMValueRef prev_debug_location = LLVMGetCurrentDebugLocation(g->builder); - LLVMPositionBuilderAtEnd(g->builder, entry_block); - ZigLLVMClearCurrentDebugLocation(g->builder); - - LLVMValueRef err_ret_trace_ptr = LLVMGetParam(fn_val, 0); - - LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; - LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, g->builtin_types.entry_i32)); - LLVMValueRef return_address_ptr = LLVMBuildCall(g->builder, get_return_address_fn_val(g), &zero, 1, ""); - LLVMValueRef return_address = LLVMBuildPtrToInt(g->builder, return_address_ptr, usize_type_ref, ""); - - LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(fn_val, "Return"); - LLVMBasicBlockRef dest_non_null_block = LLVMAppendBasicBlock(fn_val, "DestNonNull"); - - LLVMValueRef null_dest_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, err_ret_trace_ptr, - LLVMConstNull(LLVMTypeOf(err_ret_trace_ptr)), ""); - LLVMBuildCondBr(g->builder, null_dest_bit, return_block, dest_non_null_block); - - LLVMPositionBuilderAtEnd(g->builder, return_block); - LLVMBuildRetVoid(g->builder); - - LLVMPositionBuilderAtEnd(g->builder, dest_non_null_block); - LLVMValueRef args[] = { err_ret_trace_ptr, return_address }; - ZigLLVMBuildCall(g->builder, add_error_return_trace_addr_fn_val, args, 2, - get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_CallAttrAlwaysInline, ""); - LLVMBuildRetVoid(g->builder); - - LLVMPositionBuilderAtEnd(g->builder, prev_block); - if (!g->strip_debug_symbols) { - LLVMSetCurrentDebugLocation(g->builder, prev_debug_location); - } - - g->return_err_fn = fn_val; - return fn_val; -} - -static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) { - if (g->safety_crash_err_fn != nullptr) - return g->safety_crash_err_fn; - - static const char *unwrap_err_msg_text = "attempt to unwrap error: "; - - g->generate_error_name_table = true; - generate_error_name_table(g); - assert(g->err_name_table != nullptr); - - // Generate the constant part of the error message - LLVMValueRef msg_prefix_init = LLVMConstString(unwrap_err_msg_text, strlen(unwrap_err_msg_text), 1); - LLVMValueRef msg_prefix = LLVMAddGlobal(g->module, LLVMTypeOf(msg_prefix_init), ""); - LLVMSetInitializer(msg_prefix, msg_prefix_init); - LLVMSetLinkage(msg_prefix, LLVMPrivateLinkage); - LLVMSetGlobalConstant(msg_prefix, true); - - const char *fn_name = get_mangled_name(g, "__zig_fail_unwrap"); - LLVMTypeRef fn_type_ref; - if (g->have_err_ret_tracing) { - LLVMTypeRef arg_types[] = { - get_llvm_type(g, get_pointer_to_type(g, get_stack_trace_type(g), false)), - get_llvm_type(g, g->err_tag_type), - }; - fn_type_ref = LLVMFunctionType(LLVMVoidType(), arg_types, 2, false); - } else { - LLVMTypeRef arg_types[] = { - get_llvm_type(g, g->err_tag_type), - }; - fn_type_ref = LLVMFunctionType(LLVMVoidType(), arg_types, 1, false); - } - LLVMValueRef fn_val = LLVMAddFunction(g->module, fn_name, fn_type_ref); - addLLVMFnAttr(fn_val, "noreturn"); - addLLVMFnAttr(fn_val, "cold"); - LLVMSetLinkage(fn_val, LLVMInternalLinkage); - ZigLLVMFunctionSetCallingConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified)); - addLLVMFnAttr(fn_val, "nounwind"); - add_uwtable_attr(g, fn_val); - if (codegen_have_frame_pointer(g)) { - ZigLLVMAddFunctionAttr(fn_val, "frame-pointer", "all"); - } - // Not setting alignment here. See the comment above about - // "Cannot getTypeInfo() on a type that is unsized!" - // assertion failure on Darwin. - - LLVMBasicBlockRef entry_block = LLVMAppendBasicBlock(fn_val, "Entry"); - LLVMBasicBlockRef prev_block = LLVMGetInsertBlock(g->builder); - LLVMValueRef prev_debug_location = LLVMGetCurrentDebugLocation(g->builder); - LLVMPositionBuilderAtEnd(g->builder, entry_block); - ZigLLVMClearCurrentDebugLocation(g->builder); - - ZigType *usize_ty = g->builtin_types.entry_usize; - ZigType *u8_ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false, - PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0, false); - ZigType *str_type = get_slice_type(g, u8_ptr_type); - - // Allocate a buffer to hold the fully-formatted error message - const size_t err_buf_len = strlen(unwrap_err_msg_text) + g->largest_err_name_len; - LLVMValueRef max_msg_len = LLVMConstInt(usize_ty->llvm_type, err_buf_len, 0); - LLVMValueRef msg_buffer = LLVMBuildArrayAlloca(g->builder, LLVMInt8Type(), max_msg_len, "msg_buffer"); - - // Allocate a []u8 slice for the message - LLVMValueRef msg_slice = build_alloca(g, str_type, "msg_slice", 0); - - LLVMValueRef err_ret_trace_arg; - LLVMValueRef err_val; - if (g->have_err_ret_tracing) { - err_ret_trace_arg = LLVMGetParam(fn_val, 0); - err_val = LLVMGetParam(fn_val, 1); - } else { - err_ret_trace_arg = nullptr; - err_val = LLVMGetParam(fn_val, 0); - } - - // Fetch the error name from the global table - LLVMValueRef err_table_indices[] = { - LLVMConstNull(usize_ty->llvm_type), - err_val, - }; - LLVMValueRef err_name_val = LLVMBuildInBoundsGEP(g->builder, g->err_name_table, err_table_indices, 2, ""); - - LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, err_name_val, slice_ptr_index, ""); - LLVMValueRef err_name_ptr = gen_load_untyped(g, ptr_field_ptr, 0, false, ""); - - LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, err_name_val, slice_len_index, ""); - LLVMValueRef err_name_len = gen_load_untyped(g, len_field_ptr, 0, false, ""); - - LLVMValueRef msg_prefix_len = LLVMConstInt(usize_ty->llvm_type, strlen(unwrap_err_msg_text), false); - // Points to the beginning of msg_buffer - LLVMValueRef msg_buffer_ptr_indices[] = { - LLVMConstNull(usize_ty->llvm_type), - }; - LLVMValueRef msg_buffer_ptr = LLVMBuildInBoundsGEP(g->builder, msg_buffer, msg_buffer_ptr_indices, 1, ""); - // Points to the beginning of the constant prefix message - LLVMValueRef msg_prefix_ptr_indices[] = { - LLVMConstNull(usize_ty->llvm_type), - }; - LLVMValueRef msg_prefix_ptr = LLVMConstInBoundsGEP(msg_prefix, msg_prefix_ptr_indices, 1); - - // Build the message using the prefix... - ZigLLVMBuildMemCpy(g->builder, msg_buffer_ptr, 1, msg_prefix_ptr, 1, msg_prefix_len, false); - // ..and append the error name - LLVMValueRef msg_buffer_ptr_after_indices[] = { - msg_prefix_len, - }; - LLVMValueRef msg_buffer_ptr_after = LLVMBuildInBoundsGEP(g->builder, msg_buffer, msg_buffer_ptr_after_indices, 1, ""); - ZigLLVMBuildMemCpy(g->builder, msg_buffer_ptr_after, 1, err_name_ptr, 1, err_name_len, false); - - // Set the slice pointer - LLVMValueRef msg_slice_ptr_field_ptr = LLVMBuildStructGEP(g->builder, msg_slice, slice_ptr_index, ""); - gen_store_untyped(g, msg_buffer_ptr, msg_slice_ptr_field_ptr, 0, false); - - // Set the slice length - LLVMValueRef slice_len = LLVMBuildNUWAdd(g->builder, msg_prefix_len, err_name_len, ""); - LLVMValueRef msg_slice_len_field_ptr = LLVMBuildStructGEP(g->builder, msg_slice, slice_len_index, ""); - gen_store_untyped(g, slice_len, msg_slice_len_field_ptr, 0, false); - - // Call panic() - gen_panic(g, msg_slice, err_ret_trace_arg, false); - - LLVMPositionBuilderAtEnd(g->builder, prev_block); - if (!g->strip_debug_symbols) { - LLVMSetCurrentDebugLocation(g->builder, prev_debug_location); - } - - g->safety_crash_err_fn = fn_val; - return fn_val; -} - -static LLVMValueRef get_cur_err_ret_trace_val(CodeGen *g, Scope *scope, bool *is_llvm_alloca) { - if (!g->have_err_ret_tracing) { - *is_llvm_alloca = false; - return nullptr; - } - if (g->cur_err_ret_trace_val_stack != nullptr) { - *is_llvm_alloca = !fn_is_async(g->cur_fn); - return g->cur_err_ret_trace_val_stack; - } - *is_llvm_alloca = false; - return g->cur_err_ret_trace_val_arg; -} - -static void gen_safety_crash_for_err(CodeGen *g, LLVMValueRef err_val, Scope *scope) { - LLVMValueRef safety_crash_err_fn = get_safety_crash_err_fn(g); - LLVMValueRef call_instruction; - bool is_llvm_alloca = false; - if (g->have_err_ret_tracing) { - LLVMValueRef err_ret_trace_val = get_cur_err_ret_trace_val(g, scope, &is_llvm_alloca); - if (err_ret_trace_val == nullptr) { - err_ret_trace_val = LLVMConstNull(get_llvm_type(g, ptr_to_stack_trace_type(g))); - } - LLVMValueRef args[] = { - err_ret_trace_val, - err_val, - }; - call_instruction = ZigLLVMBuildCall(g->builder, safety_crash_err_fn, args, 2, - get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_CallAttrAuto, ""); - } else { - LLVMValueRef args[] = { - err_val, - }; - call_instruction = ZigLLVMBuildCall(g->builder, safety_crash_err_fn, args, 1, - get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_CallAttrAuto, ""); - } - if (!is_llvm_alloca) { - LLVMSetTailCall(call_instruction, true); - } - LLVMBuildUnreachable(g->builder); -} - -static void add_bounds_check(CodeGen *g, LLVMValueRef target_val, - LLVMIntPredicate lower_pred, LLVMValueRef lower_value, - LLVMIntPredicate upper_pred, LLVMValueRef upper_value) -{ - if (!lower_value && !upper_value) { - return; - } - if (upper_value && !lower_value) { - lower_value = upper_value; - lower_pred = upper_pred; - upper_value = nullptr; - } - - LLVMBasicBlockRef bounds_check_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "BoundsCheckFail"); - LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "BoundsCheckOk"); - LLVMBasicBlockRef lower_ok_block = upper_value ? - LLVMAppendBasicBlock(g->cur_fn_val, "FirstBoundsCheckOk") : ok_block; - - LLVMValueRef lower_ok_val = LLVMBuildICmp(g->builder, lower_pred, target_val, lower_value, ""); - LLVMBuildCondBr(g->builder, lower_ok_val, lower_ok_block, bounds_check_fail_block); - - LLVMPositionBuilderAtEnd(g->builder, bounds_check_fail_block); - gen_safety_crash(g, PanicMsgIdBoundsCheckFailure); - - if (upper_value) { - LLVMPositionBuilderAtEnd(g->builder, lower_ok_block); - LLVMValueRef upper_ok_val = LLVMBuildICmp(g->builder, upper_pred, target_val, upper_value, ""); - LLVMBuildCondBr(g->builder, upper_ok_val, ok_block, bounds_check_fail_block); - } - - LLVMPositionBuilderAtEnd(g->builder, ok_block); -} - -static void add_sentinel_check(CodeGen *g, LLVMValueRef sentinel_elem_ptr, ZigValue *sentinel) { - LLVMValueRef expected_sentinel = gen_const_val(g, sentinel, ""); - - LLVMValueRef actual_sentinel = gen_load_untyped(g, sentinel_elem_ptr, 0, false, ""); - LLVMValueRef ok_bit; - if (sentinel->type->id == ZigTypeIdFloat) { - ok_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, actual_sentinel, expected_sentinel, ""); - } else { - ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, actual_sentinel, expected_sentinel, ""); - } - - LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "SentinelFail"); - LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "SentinelOk"); - LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); - - LLVMPositionBuilderAtEnd(g->builder, fail_block); - gen_safety_crash(g, PanicMsgIdBadSentinel); - - LLVMPositionBuilderAtEnd(g->builder, ok_block); -} - -static LLVMValueRef gen_assert_zero(CodeGen *g, LLVMValueRef expr_val, ZigType *int_type) { - LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, int_type)); - LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, expr_val, zero, ""); - LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "CastShortenOk"); - LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "CastShortenFail"); - LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); - - LLVMPositionBuilderAtEnd(g->builder, fail_block); - gen_safety_crash(g, PanicMsgIdCastTruncatedData); - - LLVMPositionBuilderAtEnd(g->builder, ok_block); - return nullptr; -} - -static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, ZigType *actual_type, - ZigType *wanted_type, LLVMValueRef expr_val) -{ - assert(actual_type->id == wanted_type->id); - assert(expr_val != nullptr); - - uint64_t actual_bits; - uint64_t wanted_bits; - if (actual_type->id == ZigTypeIdFloat) { - actual_bits = actual_type->data.floating.bit_count; - wanted_bits = wanted_type->data.floating.bit_count; - } else if (actual_type->id == ZigTypeIdInt) { - actual_bits = actual_type->data.integral.bit_count; - wanted_bits = wanted_type->data.integral.bit_count; - } else { - zig_unreachable(); - } - - if (actual_type->id == ZigTypeIdInt && want_runtime_safety && ( - // negative to unsigned - (!wanted_type->data.integral.is_signed && actual_type->data.integral.is_signed) || - // unsigned would become negative - (wanted_type->data.integral.is_signed && !actual_type->data.integral.is_signed && actual_bits == wanted_bits))) - { - LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, actual_type)); - LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntSGE, expr_val, zero, ""); - - LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "SignCastOk"); - LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "SignCastFail"); - LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); - - LLVMPositionBuilderAtEnd(g->builder, fail_block); - gen_safety_crash(g, actual_type->data.integral.is_signed ? PanicMsgIdCastNegativeToUnsigned : PanicMsgIdCastTruncatedData); - - LLVMPositionBuilderAtEnd(g->builder, ok_block); - } - - if (actual_bits == wanted_bits) { - return expr_val; - } else if (actual_bits < wanted_bits) { - if (actual_type->id == ZigTypeIdFloat) { - return LLVMBuildFPExt(g->builder, expr_val, get_llvm_type(g, wanted_type), ""); - } else if (actual_type->id == ZigTypeIdInt) { - if (actual_type->data.integral.is_signed) { - return LLVMBuildSExt(g->builder, expr_val, get_llvm_type(g, wanted_type), ""); - } else { - return LLVMBuildZExt(g->builder, expr_val, get_llvm_type(g, wanted_type), ""); - } - } else { - zig_unreachable(); - } - } else if (actual_bits > wanted_bits) { - if (actual_type->id == ZigTypeIdFloat) { - return LLVMBuildFPTrunc(g->builder, expr_val, get_llvm_type(g, wanted_type), ""); - } else if (actual_type->id == ZigTypeIdInt) { - if (wanted_bits == 0) { - if (!want_runtime_safety) - return nullptr; - - return gen_assert_zero(g, expr_val, actual_type); - } - LLVMValueRef trunc_val = LLVMBuildTrunc(g->builder, expr_val, get_llvm_type(g, wanted_type), ""); - if (!want_runtime_safety) { - return trunc_val; - } - LLVMValueRef orig_val; - if (wanted_type->data.integral.is_signed) { - orig_val = LLVMBuildSExt(g->builder, trunc_val, get_llvm_type(g, actual_type), ""); - } else { - orig_val = LLVMBuildZExt(g->builder, trunc_val, get_llvm_type(g, actual_type), ""); - } - LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, expr_val, orig_val, ""); - LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "CastShortenOk"); - LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "CastShortenFail"); - LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); - - LLVMPositionBuilderAtEnd(g->builder, fail_block); - gen_safety_crash(g, PanicMsgIdCastTruncatedData); - - LLVMPositionBuilderAtEnd(g->builder, ok_block); - return trunc_val; - } else { - zig_unreachable(); - } - } else { - zig_unreachable(); - } -} - -typedef LLVMValueRef (*BuildBinOpFunc)(LLVMBuilderRef, LLVMValueRef, LLVMValueRef, const char *); -// These are lookup table using the AddSubMul enum as the lookup. -// If AddSubMul ever changes, then these tables will be out of -// date. -static const BuildBinOpFunc float_op[3] = { LLVMBuildFAdd, LLVMBuildFSub, LLVMBuildFMul }; -static const BuildBinOpFunc wrap_op[3] = { LLVMBuildAdd, LLVMBuildSub, LLVMBuildMul }; -static const BuildBinOpFunc signed_op[3] = { LLVMBuildNSWAdd, LLVMBuildNSWSub, LLVMBuildNSWMul }; -static const BuildBinOpFunc unsigned_op[3] = { LLVMBuildNUWAdd, LLVMBuildNUWSub, LLVMBuildNUWMul }; - -static LLVMValueRef gen_overflow_op(CodeGen *g, ZigType *operand_type, AddSubMul op, - LLVMValueRef val1, LLVMValueRef val2) -{ - LLVMValueRef overflow_bit; - LLVMValueRef result; - - if (operand_type->id == ZigTypeIdVector) { - ZigType *int_type = operand_type->data.vector.elem_type; - assert(int_type->id == ZigTypeIdInt); - LLVMTypeRef one_more_bit_int = LLVMIntType(int_type->data.integral.bit_count + 1); - LLVMTypeRef one_more_bit_int_vector = LLVMVectorType(one_more_bit_int, operand_type->data.vector.len); - const auto buildExtFn = int_type->data.integral.is_signed ? LLVMBuildSExt : LLVMBuildZExt; - LLVMValueRef extended1 = buildExtFn(g->builder, val1, one_more_bit_int_vector, ""); - LLVMValueRef extended2 = buildExtFn(g->builder, val2, one_more_bit_int_vector, ""); - LLVMValueRef extended_result = wrap_op[op](g->builder, extended1, extended2, ""); - result = LLVMBuildTrunc(g->builder, extended_result, get_llvm_type(g, operand_type), ""); - - LLVMValueRef re_extended_result = buildExtFn(g->builder, result, one_more_bit_int_vector, ""); - LLVMValueRef overflow_vector = LLVMBuildICmp(g->builder, LLVMIntNE, extended_result, re_extended_result, ""); - LLVMTypeRef bitcast_int_type = LLVMIntType(operand_type->data.vector.len); - LLVMValueRef bitcasted_overflow = LLVMBuildBitCast(g->builder, overflow_vector, bitcast_int_type, ""); - LLVMValueRef zero = LLVMConstNull(bitcast_int_type); - overflow_bit = LLVMBuildICmp(g->builder, LLVMIntNE, bitcasted_overflow, zero, ""); - } else { - LLVMValueRef fn_val = get_int_overflow_fn(g, operand_type, op); - LLVMValueRef params[] = { - val1, - val2, - }; - LLVMValueRef result_struct = LLVMBuildCall(g->builder, fn_val, params, 2, ""); - result = LLVMBuildExtractValue(g->builder, result_struct, 0, ""); - overflow_bit = LLVMBuildExtractValue(g->builder, result_struct, 1, ""); - } - - LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowFail"); - LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowOk"); - LLVMBuildCondBr(g->builder, overflow_bit, fail_block, ok_block); - - LLVMPositionBuilderAtEnd(g->builder, fail_block); - gen_safety_crash(g, PanicMsgIdIntegerOverflow); - - LLVMPositionBuilderAtEnd(g->builder, ok_block); - return result; -} - -static LLVMIntPredicate cmp_op_to_int_predicate(IrBinOp cmp_op, bool is_signed) { - switch (cmp_op) { - case IrBinOpCmpEq: - return LLVMIntEQ; - case IrBinOpCmpNotEq: - return LLVMIntNE; - case IrBinOpCmpLessThan: - return is_signed ? LLVMIntSLT : LLVMIntULT; - case IrBinOpCmpGreaterThan: - return is_signed ? LLVMIntSGT : LLVMIntUGT; - case IrBinOpCmpLessOrEq: - return is_signed ? LLVMIntSLE : LLVMIntULE; - case IrBinOpCmpGreaterOrEq: - return is_signed ? LLVMIntSGE : LLVMIntUGE; - default: - zig_unreachable(); - } -} - -static LLVMRealPredicate cmp_op_to_real_predicate(IrBinOp cmp_op) { - switch (cmp_op) { - case IrBinOpCmpEq: - return LLVMRealOEQ; - case IrBinOpCmpNotEq: - return LLVMRealUNE; - case IrBinOpCmpLessThan: - return LLVMRealOLT; - case IrBinOpCmpGreaterThan: - return LLVMRealOGT; - case IrBinOpCmpLessOrEq: - return LLVMRealOLE; - case IrBinOpCmpGreaterOrEq: - return LLVMRealOGE; - default: - zig_unreachable(); - } -} - -static void gen_assign_raw(CodeGen *g, LLVMValueRef ptr, ZigType *ptr_type, - LLVMValueRef value) -{ - assert(ptr_type->id == ZigTypeIdPointer); - ZigType *child_type = ptr_type->data.pointer.child_type; - - if (!type_has_bits(g, child_type)) - return; - - if (handle_is_ptr(g, child_type)) { - assert(LLVMGetTypeKind(LLVMTypeOf(value)) == LLVMPointerTypeKind); - assert(LLVMGetTypeKind(LLVMTypeOf(ptr)) == LLVMPointerTypeKind); - - LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0); - - LLVMValueRef src_ptr = LLVMBuildBitCast(g->builder, value, ptr_u8, ""); - LLVMValueRef dest_ptr = LLVMBuildBitCast(g->builder, ptr, ptr_u8, ""); - - ZigType *usize = g->builtin_types.entry_usize; - uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, get_llvm_type(g, child_type)); - uint64_t align_bytes = get_ptr_align(g, ptr_type); - assert(size_bytes > 0); - assert(align_bytes > 0); - - ZigLLVMBuildMemCpy(g->builder, dest_ptr, align_bytes, src_ptr, align_bytes, - LLVMConstInt(usize->llvm_type, size_bytes, false), - ptr_type->data.pointer.is_volatile); - return; - } - - assert(ptr_type->data.pointer.vector_index != VECTOR_INDEX_RUNTIME); - if (ptr_type->data.pointer.vector_index != VECTOR_INDEX_NONE) { - LLVMValueRef index_val = LLVMConstInt(LLVMInt32Type(), - ptr_type->data.pointer.vector_index, false); - LLVMValueRef loaded_vector = gen_load(g, ptr, ptr_type, ""); - LLVMValueRef new_vector = LLVMBuildInsertElement(g->builder, loaded_vector, value, - index_val, ""); - gen_store(g, new_vector, ptr, ptr_type); - return; - } - - uint32_t host_int_bytes = ptr_type->data.pointer.host_int_bytes; - if (host_int_bytes == 0) { - gen_store(g, value, ptr, ptr_type); - return; - } - - bool big_endian = g->is_big_endian; - - LLVMTypeRef int_ptr_ty = LLVMPointerType(LLVMIntType(host_int_bytes * 8), 0); - LLVMValueRef int_ptr = LLVMBuildBitCast(g->builder, ptr, int_ptr_ty, ""); - LLVMValueRef containing_int = gen_load(g, int_ptr, ptr_type, ""); - uint32_t host_bit_count = LLVMGetIntTypeWidth(LLVMTypeOf(containing_int)); - assert(host_bit_count == host_int_bytes * 8); - uint32_t size_in_bits = type_size_bits(g, child_type); - - uint32_t bit_offset = ptr_type->data.pointer.bit_offset_in_host; - uint32_t shift_amt = big_endian ? host_bit_count - bit_offset - size_in_bits : bit_offset; - LLVMValueRef shift_amt_val = LLVMConstInt(LLVMTypeOf(containing_int), shift_amt, false); - - // Convert to equally-sized integer type in order to perform the bit - // operations on the value to store - LLVMTypeRef value_bits_type = LLVMIntType(size_in_bits); - LLVMValueRef value_bits = LLVMBuildBitCast(g->builder, value, value_bits_type, ""); - - LLVMValueRef mask_val = LLVMConstAllOnes(value_bits_type); - mask_val = LLVMConstZExt(mask_val, LLVMTypeOf(containing_int)); - mask_val = LLVMConstShl(mask_val, shift_amt_val); - mask_val = LLVMConstNot(mask_val); - - LLVMValueRef anded_containing_int = LLVMBuildAnd(g->builder, containing_int, mask_val, ""); - LLVMValueRef extended_value = LLVMBuildZExt(g->builder, value_bits, LLVMTypeOf(containing_int), ""); - LLVMValueRef shifted_value = LLVMBuildShl(g->builder, extended_value, shift_amt_val, ""); - LLVMValueRef ored_value = LLVMBuildOr(g->builder, shifted_value, anded_containing_int, ""); - - gen_store(g, ored_value, int_ptr, ptr_type); -} - -static void gen_var_debug_decl(CodeGen *g, ZigVar *var) { - if (g->strip_debug_symbols) return; - assert(var->di_loc_var != nullptr); - AstNode *source_node = var->decl_node; - ZigLLVMDILocation *debug_loc = ZigLLVMGetDebugLoc((unsigned)source_node->line + 1, - (unsigned)source_node->column + 1, get_di_scope(g, var->parent_scope)); - ZigLLVMInsertDeclareAtEnd(g->dbuilder, var->value_ref, var->di_loc_var, debug_loc, - LLVMGetInsertBlock(g->builder)); -} - -static LLVMValueRef ir_llvm_value(CodeGen *g, IrInstGen *instruction) { - Error err; - - bool value_has_bits; - if ((err = type_has_bits2(g, instruction->value->type, &value_has_bits))) - codegen_report_errors_and_exit(g); - - if (!value_has_bits) - return nullptr; - - if (!instruction->llvm_value) { - if (instruction->id == IrInstGenIdAwait) { - IrInstGenAwait *await = reinterpret_cast(instruction); - if (await->result_loc != nullptr) { - return get_handle_value(g, ir_llvm_value(g, await->result_loc), - await->result_loc->value->type->data.pointer.child_type, await->result_loc->value->type); - } - } - if (instruction->spill != nullptr) { - ZigType *ptr_type = instruction->spill->value->type; - ir_assert(ptr_type->id == ZigTypeIdPointer, instruction); - return get_handle_value(g, ir_llvm_value(g, instruction->spill), - ptr_type->data.pointer.child_type, instruction->spill->value->type); - } - ir_assert(instruction->value->special != ConstValSpecialRuntime, instruction); - assert(instruction->value->type); - render_const_val(g, instruction->value, ""); - // we might have to do some pointer casting here due to the way union - // values are rendered with a type other than the one we expect - if (handle_is_ptr(g, instruction->value->type)) { - render_const_val_global(g, instruction->value, ""); - ZigType *ptr_type = get_pointer_to_type(g, instruction->value->type, true); - instruction->llvm_value = LLVMBuildBitCast(g->builder, instruction->value->llvm_global, get_llvm_type(g, ptr_type), ""); - } else { - instruction->llvm_value = LLVMBuildBitCast(g->builder, instruction->value->llvm_value, - get_llvm_type(g, instruction->value->type), ""); - } - assert(instruction->llvm_value); - } - return instruction->llvm_value; -} - -void codegen_report_errors_and_exit(CodeGen *g) { - // Clear progress indicator before printing errors - if (g->sub_progress_node != nullptr) { - stage2_progress_end(g->sub_progress_node); - g->sub_progress_node = nullptr; - } - if (g->main_progress_node != nullptr) { - stage2_progress_end(g->main_progress_node); - g->main_progress_node = nullptr; - } - - assert(g->errors.length != 0); - for (size_t i = 0; i < g->errors.length; i += 1) { - ErrorMsg *err = g->errors.at(i); - print_err_msg(err, g->err_color); - } - exit(1); -} - -static void report_errors_and_maybe_exit(CodeGen *g) { - if (g->errors.length != 0) { - codegen_report_errors_and_exit(g); - } -} - -ATTRIBUTE_NORETURN -static void give_up_with_c_abi_error(CodeGen *g, AstNode *source_node) { - ErrorMsg *msg = add_node_error(g, source_node, - buf_sprintf("TODO: support C ABI for more targets. https://github.com/ziglang/zig/issues/1481")); - add_error_note(g, msg, source_node, - buf_sprintf("pointers, integers, floats, bools, and enums work on all targets")); - codegen_report_errors_and_exit(g); -} - -static LLVMValueRef build_alloca(CodeGen *g, ZigType *type_entry, const char *name, uint32_t alignment) { - LLVMValueRef result = LLVMBuildAlloca(g->builder, get_llvm_type(g, type_entry), name); - LLVMSetAlignment(result, (alignment == 0) ? get_abi_alignment(g, type_entry) : alignment); - return result; -} - -static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk, size_t src_i) { - // Initialized from the type for some walks, but because of C var args, - // initialized based on callsite instructions for that one. - FnTypeParamInfo *param_info = nullptr; - ZigType *ty; - ZigType *dest_ty = nullptr; - AstNode *source_node = nullptr; - LLVMValueRef val; - LLVMValueRef llvm_fn; - unsigned di_arg_index; - ZigVar *var; - switch (fn_walk->id) { - case FnWalkIdAttrs: - if (src_i >= fn_type->data.fn.fn_type_id.param_count) - return false; - param_info = &fn_type->data.fn.fn_type_id.param_info[src_i]; - ty = param_info->type; - source_node = fn_walk->data.attrs.fn->proto_node; - llvm_fn = fn_walk->data.attrs.llvm_fn; - break; - case FnWalkIdCall: { - if (src_i >= fn_walk->data.call.inst->arg_count) - return false; - IrInstGen *arg = fn_walk->data.call.inst->args[src_i]; - ty = arg->value->type; - source_node = arg->base.source_node; - val = ir_llvm_value(g, arg); - break; - } - case FnWalkIdTypes: - if (src_i >= fn_type->data.fn.fn_type_id.param_count) - return false; - param_info = &fn_type->data.fn.fn_type_id.param_info[src_i]; - ty = param_info->type; - break; - case FnWalkIdVars: - assert(src_i < fn_type->data.fn.fn_type_id.param_count); - param_info = &fn_type->data.fn.fn_type_id.param_info[src_i]; - ty = param_info->type; - var = fn_walk->data.vars.var; - source_node = var->decl_node; - llvm_fn = fn_walk->data.vars.llvm_fn; - break; - case FnWalkIdInits: - if (src_i >= fn_type->data.fn.fn_type_id.param_count) - return false; - param_info = &fn_type->data.fn.fn_type_id.param_info[src_i]; - ty = param_info->type; - var = fn_walk->data.inits.fn->variable_list.at(src_i); - source_node = fn_walk->data.inits.fn->proto_node; - llvm_fn = fn_walk->data.inits.llvm_fn; - break; - } - - if (type_is_c_abi_int_bail(g, ty) || ty->id == ZigTypeIdFloat || ty->id == ZigTypeIdVector || - ty->id == ZigTypeIdInt // TODO investigate if we need to change this - ) { - switch (fn_walk->id) { - case FnWalkIdAttrs: { - ZigType *ptr_type = get_codegen_ptr_type_bail(g, ty); - if (ptr_type != nullptr) { - if (type_is_nonnull_ptr(g, ty)) { - addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "nonnull"); - } - if (ptr_type->id == ZigTypeIdPointer && ptr_type->data.pointer.is_const) { - addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "readonly"); - } - if (param_info->is_noalias) { - addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "noalias"); - } - } - fn_walk->data.attrs.gen_i += 1; - break; - } - case FnWalkIdCall: - fn_walk->data.call.gen_param_values->append(val); - break; - case FnWalkIdTypes: - fn_walk->data.types.gen_param_types->append(get_llvm_type(g, ty)); - fn_walk->data.types.param_di_types->append(get_llvm_di_type(g, ty)); - break; - case FnWalkIdVars: { - var->value_ref = build_alloca(g, ty, var->name, var->align_bytes); - di_arg_index = fn_walk->data.vars.gen_i; - fn_walk->data.vars.gen_i += 1; - dest_ty = ty; - goto var_ok; - } - case FnWalkIdInits: - clear_debug_source_node(g); - gen_store_untyped(g, LLVMGetParam(llvm_fn, fn_walk->data.inits.gen_i), var->value_ref, var->align_bytes, false); - if (var->decl_node) { - gen_var_debug_decl(g, var); - } - fn_walk->data.inits.gen_i += 1; - break; - } - return true; - } - - { - // Arrays are just pointers - if (ty->id == ZigTypeIdArray) { - assert(handle_is_ptr(g, ty)); - switch (fn_walk->id) { - case FnWalkIdAttrs: - // arrays passed to C ABI functions may not be at address 0 - addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "nonnull"); - addLLVMArgAttrInt(llvm_fn, fn_walk->data.attrs.gen_i, "align", get_abi_alignment(g, ty)); - fn_walk->data.attrs.gen_i += 1; - break; - case FnWalkIdCall: - fn_walk->data.call.gen_param_values->append(val); - break; - case FnWalkIdTypes: { - ZigType *gen_type = get_pointer_to_type(g, ty, true); - fn_walk->data.types.gen_param_types->append(get_llvm_type(g, gen_type)); - fn_walk->data.types.param_di_types->append(get_llvm_di_type(g, gen_type)); - break; - } - case FnWalkIdVars: { - var->value_ref = LLVMGetParam(llvm_fn, fn_walk->data.vars.gen_i); - di_arg_index = fn_walk->data.vars.gen_i; - dest_ty = get_pointer_to_type(g, ty, false); - fn_walk->data.vars.gen_i += 1; - goto var_ok; - } - case FnWalkIdInits: - if (var->decl_node) { - gen_var_debug_decl(g, var); - } - fn_walk->data.inits.gen_i += 1; - break; - } - return true; - } - - X64CABIClass abi_class = type_c_abi_x86_64_class(g, ty); - size_t ty_size = type_size(g, ty); - if (abi_class == X64CABIClass_MEMORY || abi_class == X64CABIClass_MEMORY_nobyval) { - assert(handle_is_ptr(g, ty)); - switch (fn_walk->id) { - case FnWalkIdAttrs: - if (abi_class != X64CABIClass_MEMORY_nobyval) { - ZigLLVMAddByValAttr(llvm_fn, fn_walk->data.attrs.gen_i + 1, get_llvm_type(g, ty)); - addLLVMArgAttrInt(llvm_fn, fn_walk->data.attrs.gen_i, "align", get_abi_alignment(g, ty)); - } else if (g->zig_target->arch == ZigLLVM_aarch64 || - g->zig_target->arch == ZigLLVM_aarch64_be) - { - // no attrs needed - } else { - if (source_node != nullptr) { - give_up_with_c_abi_error(g, source_node); - } - // otherwise allow codegen code to report a compile error - return false; - } - - // Byvalue parameters must not have address 0 - addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "nonnull"); - fn_walk->data.attrs.gen_i += 1; - break; - case FnWalkIdCall: - fn_walk->data.call.gen_param_values->append(val); - break; - case FnWalkIdTypes: { - ZigType *gen_type = get_pointer_to_type(g, ty, true); - fn_walk->data.types.gen_param_types->append(get_llvm_type(g, gen_type)); - fn_walk->data.types.param_di_types->append(get_llvm_di_type(g, gen_type)); - break; - } - case FnWalkIdVars: { - di_arg_index = fn_walk->data.vars.gen_i; - var->value_ref = LLVMGetParam(llvm_fn, fn_walk->data.vars.gen_i); - dest_ty = get_pointer_to_type(g, ty, false); - fn_walk->data.vars.gen_i += 1; - goto var_ok; - } - case FnWalkIdInits: - if (var->decl_node) { - gen_var_debug_decl(g, var); - } - fn_walk->data.inits.gen_i += 1; - break; - } - return true; - } else if (abi_class == X64CABIClass_INTEGER) { - switch (fn_walk->id) { - case FnWalkIdAttrs: - fn_walk->data.attrs.gen_i += 1; - break; - case FnWalkIdCall: { - LLVMTypeRef ptr_to_int_type_ref = LLVMPointerType(LLVMIntType((unsigned)ty_size * 8), 0); - LLVMValueRef bitcasted = LLVMBuildBitCast(g->builder, val, ptr_to_int_type_ref, ""); - LLVMValueRef loaded = LLVMBuildLoad(g->builder, bitcasted, ""); - fn_walk->data.call.gen_param_values->append(loaded); - break; - } - case FnWalkIdTypes: { - ZigType *gen_type = get_int_type(g, false, ty_size * 8); - fn_walk->data.types.gen_param_types->append(get_llvm_type(g, gen_type)); - fn_walk->data.types.param_di_types->append(get_llvm_di_type(g, gen_type)); - break; - } - case FnWalkIdVars: { - di_arg_index = fn_walk->data.vars.gen_i; - var->value_ref = build_alloca(g, ty, var->name, var->align_bytes); - fn_walk->data.vars.gen_i += 1; - dest_ty = ty; - goto var_ok; - } - case FnWalkIdInits: { - clear_debug_source_node(g); - if (!fn_is_async(fn_walk->data.inits.fn)) { - LLVMValueRef arg = LLVMGetParam(llvm_fn, fn_walk->data.inits.gen_i); - LLVMTypeRef ptr_to_int_type_ref = LLVMPointerType(LLVMIntType((unsigned)ty_size * 8), 0); - LLVMValueRef bitcasted = LLVMBuildBitCast(g->builder, var->value_ref, ptr_to_int_type_ref, ""); - gen_store_untyped(g, arg, bitcasted, var->align_bytes, false); - } - if (var->decl_node) { - gen_var_debug_decl(g, var); - } - fn_walk->data.inits.gen_i += 1; - break; - } - } - return true; - } else if (abi_class == X64CABIClass_SSE) { - // For now only handle structs with only floats/doubles in it. - if (ty->id != ZigTypeIdStruct) { - if (source_node != nullptr) { - give_up_with_c_abi_error(g, source_node); - } - // otherwise allow codegen code to report a compile error - return false; - } - - for (uint32_t i = 0; i < ty->data.structure.src_field_count; i += 1) { - if (ty->data.structure.fields[i]->type_entry->id != ZigTypeIdFloat) { - if (source_node != nullptr) { - give_up_with_c_abi_error(g, source_node); - } - // otherwise allow codegen code to report a compile error - return false; - } - } - - // The SystemV ABI says that we have to setup 1 FP register per f64. - // So two f32 can be passed in one f64, but 3 f32 have to be passed in 2 FP registers. - // To achieve this with LLVM API, we pass multiple f64 parameters to the LLVM function if - // the type is bigger than 8 bytes. - - // Example: - // extern struct { - // x: f32, - // y: f32, - // z: f32, - // }; - // const ptr = (*f64)*Struct; - // Register 1: ptr.* - // Register 2: (ptr + 1).* - - // One floating point register per f64 or 2 f32's - size_t number_of_fp_regs = (size_t)ceilf((float)ty_size / (float)8); - - switch (fn_walk->id) { - case FnWalkIdAttrs: { - fn_walk->data.attrs.gen_i += 1; - break; - } - case FnWalkIdCall: { - LLVMValueRef f64_ptr_to_struct = LLVMBuildBitCast(g->builder, val, LLVMPointerType(LLVMDoubleType(), 0), ""); - for (uint32_t i = 0; i < number_of_fp_regs; i += 1) { - LLVMValueRef index = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, i, false); - LLVMValueRef indices[] = { index }; - LLVMValueRef adjusted_ptr_to_struct = LLVMBuildInBoundsGEP(g->builder, f64_ptr_to_struct, indices, 1, ""); - LLVMValueRef loaded = LLVMBuildLoad(g->builder, adjusted_ptr_to_struct, ""); - fn_walk->data.call.gen_param_values->append(loaded); - } - break; - } - case FnWalkIdTypes: { - for (uint32_t i = 0; i < number_of_fp_regs; i += 1) { - fn_walk->data.types.gen_param_types->append(get_llvm_type(g, g->builtin_types.entry_f64)); - fn_walk->data.types.param_di_types->append(get_llvm_di_type(g, g->builtin_types.entry_f64)); - } - break; - } - case FnWalkIdVars: - case FnWalkIdInits: { - // TODO: Handle exporting functions - if (source_node != nullptr) { - give_up_with_c_abi_error(g, source_node); - } - // otherwise allow codegen code to report a compile error - return false; - } - } - return true; - } - } - if (source_node != nullptr) { - give_up_with_c_abi_error(g, source_node); - } - // otherwise allow codegen code to report a compile error - return false; - -var_ok: - if (dest_ty != nullptr && var->decl_node) { - // arg index + 1 because the 0 index is return value - var->di_loc_var = ZigLLVMCreateParameterVariable(g->dbuilder, get_di_scope(g, var->parent_scope), - var->name, fn_walk->data.vars.import->data.structure.root_struct->di_file, - (unsigned)(var->decl_node->line + 1), - get_llvm_di_type(g, dest_ty), !g->strip_debug_symbols, 0, di_arg_index + 1); - } - return true; -} - -void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk) { - CallingConvention cc = fn_type->data.fn.fn_type_id.cc; - if (!calling_convention_allows_zig_types(cc)) { - size_t src_i = 0; - for (;;) { - if (!iter_function_params_c_abi(g, fn_type, fn_walk, src_i)) - break; - src_i += 1; - } - return; - } - if (fn_walk->id == FnWalkIdCall) { - IrInstGenCall *instruction = fn_walk->data.call.inst; - bool is_var_args = fn_walk->data.call.is_var_args; - for (size_t call_i = 0; call_i < instruction->arg_count; call_i += 1) { - IrInstGen *param_instruction = instruction->args[call_i]; - ZigType *param_type = param_instruction->value->type; - if (is_var_args || type_has_bits(g, param_type)) { - LLVMValueRef param_value = ir_llvm_value(g, param_instruction); - assert(param_value); - fn_walk->data.call.gen_param_values->append(param_value); - fn_walk->data.call.gen_param_types->append(param_type); - } - } - return; - } - size_t next_var_i = 0; - for (size_t param_i = 0; param_i < fn_type->data.fn.fn_type_id.param_count; param_i += 1) { - FnGenParamInfo *gen_info = &fn_type->data.fn.gen_param_info[param_i]; - size_t gen_index = gen_info->gen_index; - - if (gen_index == SIZE_MAX) { - continue; - } - - switch (fn_walk->id) { - case FnWalkIdAttrs: { - LLVMValueRef llvm_fn = fn_walk->data.attrs.llvm_fn; - bool is_byval = gen_info->is_byval; - FnTypeParamInfo *param_info = &fn_type->data.fn.fn_type_id.param_info[param_i]; - - ZigType *param_type = gen_info->type; - if (param_info->is_noalias) { - addLLVMArgAttr(llvm_fn, (unsigned)gen_index, "noalias"); - } - if ((param_type->id == ZigTypeIdPointer && param_type->data.pointer.is_const) || is_byval) { - addLLVMArgAttr(llvm_fn, (unsigned)gen_index, "readonly"); - } - if (get_codegen_ptr_type_bail(g, param_type) != nullptr) { - addLLVMArgAttrInt(llvm_fn, (unsigned)gen_index, "align", get_ptr_align(g, param_type)); - } - if (type_is_nonnull_ptr(g, param_type)) { - addLLVMArgAttr(llvm_fn, (unsigned)gen_index, "nonnull"); - } - break; - } - case FnWalkIdInits: { - ZigFn *fn_table_entry = fn_walk->data.inits.fn; - LLVMValueRef llvm_fn = fn_table_entry->llvm_value; - ZigVar *variable = fn_table_entry->variable_list.at(next_var_i); - assert(variable->src_arg_index != SIZE_MAX); - next_var_i += 1; - - assert(variable); - assert(variable->value_ref); - - if (!handle_is_ptr(g, variable->var_type) && !fn_is_async(fn_walk->data.inits.fn)) { - clear_debug_source_node(g); - ZigType *fn_type = fn_table_entry->type_entry; - unsigned gen_arg_index = fn_type->data.fn.gen_param_info[variable->src_arg_index].gen_index; - gen_store_untyped(g, LLVMGetParam(llvm_fn, gen_arg_index), - variable->value_ref, variable->align_bytes, false); - } - - if (variable->decl_node) { - gen_var_debug_decl(g, variable); - } - break; - } - case FnWalkIdCall: - // handled before for loop - zig_unreachable(); - case FnWalkIdTypes: - // Not called for non-c-abi - zig_unreachable(); - case FnWalkIdVars: - // iter_function_params_c_abi is called directly for this one - zig_unreachable(); - } - } -} - -static LLVMValueRef get_merge_err_ret_traces_fn_val(CodeGen *g) { - if (g->merge_err_ret_traces_fn_val) - return g->merge_err_ret_traces_fn_val; - - assert(g->stack_trace_type != nullptr); - - LLVMTypeRef param_types[] = { - get_llvm_type(g, ptr_to_stack_trace_type(g)), - get_llvm_type(g, ptr_to_stack_trace_type(g)), - }; - LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), param_types, 2, false); - - const char *fn_name = get_mangled_name(g, "__zig_merge_error_return_traces"); - LLVMValueRef fn_val = LLVMAddFunction(g->module, fn_name, fn_type_ref); - LLVMSetLinkage(fn_val, LLVMInternalLinkage); - ZigLLVMFunctionSetCallingConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified)); - addLLVMFnAttr(fn_val, "nounwind"); - add_uwtable_attr(g, fn_val); - addLLVMArgAttr(fn_val, (unsigned)0, "noalias"); - addLLVMArgAttr(fn_val, (unsigned)0, "writeonly"); - - addLLVMArgAttr(fn_val, (unsigned)1, "noalias"); - addLLVMArgAttr(fn_val, (unsigned)1, "readonly"); - if (codegen_have_frame_pointer(g)) { - ZigLLVMAddFunctionAttr(fn_val, "frame-pointer", "all"); - } - - // this is above the ZigLLVMClearCurrentDebugLocation - LLVMValueRef add_error_return_trace_addr_fn_val = get_add_error_return_trace_addr_fn(g); - - LLVMBasicBlockRef entry_block = LLVMAppendBasicBlock(fn_val, "Entry"); - LLVMBasicBlockRef prev_block = LLVMGetInsertBlock(g->builder); - LLVMValueRef prev_debug_location = LLVMGetCurrentDebugLocation(g->builder); - LLVMPositionBuilderAtEnd(g->builder, entry_block); - ZigLLVMClearCurrentDebugLocation(g->builder); - - // if (dest_stack_trace == null or src_stack_trace == null) return; - // var frame_index: usize = undefined; - // var frames_left: usize = undefined; - // if (src_stack_trace.index < src_stack_trace.instruction_addresses.len) { - // frame_index = 0; - // frames_left = src_stack_trace.index; - // if (frames_left == 0) return; - // } else { - // frame_index = (src_stack_trace.index + 1) % src_stack_trace.instruction_addresses.len; - // frames_left = src_stack_trace.instruction_addresses.len; - // } - // while (true) { - // __zig_add_err_ret_trace_addr(dest_stack_trace, src_stack_trace.instruction_addresses[frame_index]); - // frames_left -= 1; - // if (frames_left == 0) return; - // frame_index = (frame_index + 1) % src_stack_trace.instruction_addresses.len; - // } - LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(fn_val, "Return"); - LLVMBasicBlockRef non_null_block = LLVMAppendBasicBlock(fn_val, "NonNull"); - - LLVMValueRef frame_index_ptr = LLVMBuildAlloca(g->builder, g->builtin_types.entry_usize->llvm_type, "frame_index"); - LLVMValueRef frames_left_ptr = LLVMBuildAlloca(g->builder, g->builtin_types.entry_usize->llvm_type, "frames_left"); - - LLVMValueRef dest_stack_trace_ptr = LLVMGetParam(fn_val, 0); - LLVMValueRef src_stack_trace_ptr = LLVMGetParam(fn_val, 1); - - LLVMValueRef null_dest_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, dest_stack_trace_ptr, - LLVMConstNull(LLVMTypeOf(dest_stack_trace_ptr)), ""); - LLVMValueRef null_src_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, src_stack_trace_ptr, - LLVMConstNull(LLVMTypeOf(src_stack_trace_ptr)), ""); - LLVMValueRef null_bit = LLVMBuildOr(g->builder, null_dest_bit, null_src_bit, ""); - LLVMBuildCondBr(g->builder, null_bit, return_block, non_null_block); - - LLVMPositionBuilderAtEnd(g->builder, non_null_block); - size_t src_index_field_index = g->stack_trace_type->data.structure.fields[0]->gen_index; - size_t src_addresses_field_index = g->stack_trace_type->data.structure.fields[1]->gen_index; - LLVMValueRef src_index_field_ptr = LLVMBuildStructGEP(g->builder, src_stack_trace_ptr, - (unsigned)src_index_field_index, ""); - LLVMValueRef src_addresses_field_ptr = LLVMBuildStructGEP(g->builder, src_stack_trace_ptr, - (unsigned)src_addresses_field_index, ""); - ZigType *slice_type = g->stack_trace_type->data.structure.fields[1]->type_entry; - size_t ptr_field_index = slice_type->data.structure.fields[slice_ptr_index]->gen_index; - LLVMValueRef src_ptr_field_ptr = LLVMBuildStructGEP(g->builder, src_addresses_field_ptr, (unsigned)ptr_field_index, ""); - size_t len_field_index = slice_type->data.structure.fields[slice_len_index]->gen_index; - LLVMValueRef src_len_field_ptr = LLVMBuildStructGEP(g->builder, src_addresses_field_ptr, (unsigned)len_field_index, ""); - LLVMValueRef src_index_val = LLVMBuildLoad(g->builder, src_index_field_ptr, ""); - LLVMValueRef src_ptr_val = LLVMBuildLoad(g->builder, src_ptr_field_ptr, ""); - LLVMValueRef src_len_val = LLVMBuildLoad(g->builder, src_len_field_ptr, ""); - LLVMValueRef no_wrap_bit = LLVMBuildICmp(g->builder, LLVMIntULT, src_index_val, src_len_val, ""); - LLVMBasicBlockRef no_wrap_block = LLVMAppendBasicBlock(fn_val, "NoWrap"); - LLVMBasicBlockRef yes_wrap_block = LLVMAppendBasicBlock(fn_val, "YesWrap"); - LLVMBasicBlockRef loop_block = LLVMAppendBasicBlock(fn_val, "Loop"); - LLVMBuildCondBr(g->builder, no_wrap_bit, no_wrap_block, yes_wrap_block); - - LLVMPositionBuilderAtEnd(g->builder, no_wrap_block); - LLVMValueRef usize_zero = LLVMConstNull(g->builtin_types.entry_usize->llvm_type); - LLVMBuildStore(g->builder, usize_zero, frame_index_ptr); - LLVMBuildStore(g->builder, src_index_val, frames_left_ptr); - LLVMValueRef frames_left_eq_zero_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, src_index_val, usize_zero, ""); - LLVMBuildCondBr(g->builder, frames_left_eq_zero_bit, return_block, loop_block); - - LLVMPositionBuilderAtEnd(g->builder, yes_wrap_block); - LLVMValueRef usize_one = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, 1, false); - LLVMValueRef plus_one = LLVMBuildNUWAdd(g->builder, src_index_val, usize_one, ""); - LLVMValueRef mod_len = LLVMBuildURem(g->builder, plus_one, src_len_val, ""); - LLVMBuildStore(g->builder, mod_len, frame_index_ptr); - LLVMBuildStore(g->builder, src_len_val, frames_left_ptr); - LLVMBuildBr(g->builder, loop_block); - - LLVMPositionBuilderAtEnd(g->builder, loop_block); - LLVMValueRef ptr_index = LLVMBuildLoad(g->builder, frame_index_ptr, ""); - LLVMValueRef addr_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr_val, &ptr_index, 1, ""); - LLVMValueRef this_addr_val = LLVMBuildLoad(g->builder, addr_ptr, ""); - LLVMValueRef args[] = {dest_stack_trace_ptr, this_addr_val}; - ZigLLVMBuildCall(g->builder, add_error_return_trace_addr_fn_val, args, 2, get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_CallAttrAlwaysInline, ""); - LLVMValueRef prev_frames_left = LLVMBuildLoad(g->builder, frames_left_ptr, ""); - LLVMValueRef new_frames_left = LLVMBuildNUWSub(g->builder, prev_frames_left, usize_one, ""); - LLVMValueRef done_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, new_frames_left, usize_zero, ""); - LLVMBasicBlockRef continue_block = LLVMAppendBasicBlock(fn_val, "Continue"); - LLVMBuildCondBr(g->builder, done_bit, return_block, continue_block); - - LLVMPositionBuilderAtEnd(g->builder, return_block); - LLVMBuildRetVoid(g->builder); - - LLVMPositionBuilderAtEnd(g->builder, continue_block); - LLVMBuildStore(g->builder, new_frames_left, frames_left_ptr); - LLVMValueRef prev_index = LLVMBuildLoad(g->builder, frame_index_ptr, ""); - LLVMValueRef index_plus_one = LLVMBuildNUWAdd(g->builder, prev_index, usize_one, ""); - LLVMValueRef index_mod_len = LLVMBuildURem(g->builder, index_plus_one, src_len_val, ""); - LLVMBuildStore(g->builder, index_mod_len, frame_index_ptr); - LLVMBuildBr(g->builder, loop_block); - - LLVMPositionBuilderAtEnd(g->builder, prev_block); - if (!g->strip_debug_symbols) { - LLVMSetCurrentDebugLocation(g->builder, prev_debug_location); - } - - g->merge_err_ret_traces_fn_val = fn_val; - return fn_val; - -} -static LLVMValueRef ir_render_save_err_ret_addr(CodeGen *g, IrExecutableGen *executable, - IrInstGenSaveErrRetAddr *save_err_ret_addr_instruction) -{ - assert(g->have_err_ret_tracing); - - LLVMValueRef return_err_fn = get_return_err_fn(g); - bool is_llvm_alloca; - LLVMValueRef my_err_trace_val = get_cur_err_ret_trace_val(g, save_err_ret_addr_instruction->base.base.scope, - &is_llvm_alloca); - ZigLLVMBuildCall(g->builder, return_err_fn, &my_err_trace_val, 1, - get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_CallAttrAuto, ""); - - ZigType *ret_type = g->cur_fn->type_entry->data.fn.fn_type_id.return_type; - if (fn_is_async(g->cur_fn) && codegen_fn_has_err_ret_tracing_arg(g, ret_type)) { - LLVMValueRef trace_ptr_ptr = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr, - frame_index_trace_arg(g, ret_type), ""); - LLVMBuildStore(g->builder, my_err_trace_val, trace_ptr_ptr); - } - - return nullptr; -} - -static void gen_assert_resume_id(CodeGen *g, IrInstGen *source_instr, ResumeId resume_id, PanicMsgId msg_id, - LLVMBasicBlockRef end_bb) -{ - LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; - - if (ir_want_runtime_safety(g, source_instr)) { - // Write a value to the resume index which indicates the function was resumed while not suspended. - LLVMBuildStore(g->builder, g->cur_bad_not_suspended_index, g->cur_async_resume_index_ptr); - } - - LLVMBasicBlockRef bad_resume_block = LLVMAppendBasicBlock(g->cur_fn_val, "BadResume"); - if (end_bb == nullptr) end_bb = LLVMAppendBasicBlock(g->cur_fn_val, "OkResume"); - LLVMValueRef expected_value = LLVMConstSub(LLVMConstAllOnes(usize_type_ref), - LLVMConstInt(usize_type_ref, resume_id, false)); - LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, LLVMGetParam(g->cur_fn_val, 1), expected_value, ""); - LLVMBuildCondBr(g->builder, ok_bit, end_bb, bad_resume_block); - - LLVMPositionBuilderAtEnd(g->builder, bad_resume_block); - gen_assertion(g, msg_id, source_instr); - - LLVMPositionBuilderAtEnd(g->builder, end_bb); -} - -static LLVMValueRef gen_resume(CodeGen *g, LLVMValueRef fn_val, LLVMValueRef target_frame_ptr, ResumeId resume_id) { - LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; - if (fn_val == nullptr) { - LLVMValueRef fn_ptr_ptr = LLVMBuildStructGEP(g->builder, target_frame_ptr, frame_fn_ptr_index, ""); - fn_val = LLVMBuildLoad(g->builder, fn_ptr_ptr, ""); - } - LLVMValueRef arg_val = LLVMConstSub(LLVMConstAllOnes(usize_type_ref), - LLVMConstInt(usize_type_ref, resume_id, false)); - LLVMValueRef args[] = {target_frame_ptr, arg_val}; - return ZigLLVMBuildCall(g->builder, fn_val, args, 2, ZigLLVM_Fast, ZigLLVM_CallAttrAuto, ""); -} - -static LLVMBasicBlockRef gen_suspend_begin(CodeGen *g, const char *name_hint) { - LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; - LLVMBasicBlockRef resume_bb = LLVMAppendBasicBlock(g->cur_fn_val, name_hint); - size_t new_block_index = g->cur_resume_block_count; - g->cur_resume_block_count += 1; - LLVMValueRef new_block_index_val = LLVMConstInt(usize_type_ref, new_block_index, false); - LLVMAddCase(g->cur_async_switch_instr, new_block_index_val, resume_bb); - LLVMBuildStore(g->builder, new_block_index_val, g->cur_async_resume_index_ptr); - return resume_bb; -} - -// Be careful setting tail call. According to LLVM lang ref, -// tail and musttail imply that the callee does not access allocas from the caller. -// This works for async functions since the locals are spilled. -// http://llvm.org/docs/LangRef.html#id320 -static void set_tail_call_if_appropriate(CodeGen *g, LLVMValueRef call_inst) { - LLVMSetTailCall(call_inst, true); -} - -static LLVMValueRef gen_maybe_atomic_op(CodeGen *g, LLVMAtomicRMWBinOp op, LLVMValueRef ptr, LLVMValueRef val, - LLVMAtomicOrdering order) -{ - if (g->is_single_threaded) { - LLVMValueRef loaded = LLVMBuildLoad(g->builder, ptr, ""); - LLVMValueRef modified; - switch (op) { - case LLVMAtomicRMWBinOpXchg: - modified = val; - break; - case LLVMAtomicRMWBinOpXor: - modified = LLVMBuildXor(g->builder, loaded, val, ""); - break; - default: - zig_unreachable(); - } - LLVMBuildStore(g->builder, modified, ptr); - return loaded; - } else { - return LLVMBuildAtomicRMW(g->builder, op, ptr, val, order, false); - } -} - -static void gen_async_return(CodeGen *g, IrInstGenReturn *instruction) { - LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; - - ZigType *operand_type = (instruction->operand != nullptr) ? instruction->operand->value->type : nullptr; - bool operand_has_bits = (operand_type != nullptr) && type_has_bits(g, operand_type); - ZigType *ret_type = g->cur_fn->type_entry->data.fn.fn_type_id.return_type; - bool ret_type_has_bits = type_has_bits(g, ret_type); - - if (operand_has_bits && instruction->operand != nullptr) { - bool need_store = instruction->operand->value->special != ConstValSpecialRuntime || !handle_is_ptr(g, ret_type); - if (need_store) { - // It didn't get written to the result ptr. We do that now. - ZigType *ret_ptr_type = get_pointer_to_type(g, ret_type, true); - gen_assign_raw(g, g->cur_ret_ptr, ret_ptr_type, ir_llvm_value(g, instruction->operand)); - } - } - - // Whether we tail resume the awaiter, or do an early return, we are done and will not be resumed. - if (ir_want_runtime_safety(g, &instruction->base)) { - LLVMValueRef new_resume_index = LLVMConstAllOnes(usize_type_ref); - LLVMBuildStore(g->builder, new_resume_index, g->cur_async_resume_index_ptr); - } - - LLVMValueRef zero = LLVMConstNull(usize_type_ref); - LLVMValueRef all_ones = LLVMConstAllOnes(usize_type_ref); - - LLVMValueRef prev_val = gen_maybe_atomic_op(g, LLVMAtomicRMWBinOpXor, g->cur_async_awaiter_ptr, - all_ones, LLVMAtomicOrderingAcquire); - - LLVMBasicBlockRef bad_return_block = LLVMAppendBasicBlock(g->cur_fn_val, "BadReturn"); - LLVMBasicBlockRef early_return_block = LLVMAppendBasicBlock(g->cur_fn_val, "EarlyReturn"); - LLVMBasicBlockRef resume_them_block = LLVMAppendBasicBlock(g->cur_fn_val, "ResumeThem"); - - LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, prev_val, resume_them_block, 2); - - LLVMAddCase(switch_instr, zero, early_return_block); - LLVMAddCase(switch_instr, all_ones, bad_return_block); - - // Something has gone horribly wrong, and this is an invalid second return. - LLVMPositionBuilderAtEnd(g->builder, bad_return_block); - gen_assertion(g, PanicMsgIdBadReturn, &instruction->base); - - // There is no awaiter yet, but we're completely done. - LLVMPositionBuilderAtEnd(g->builder, early_return_block); - LLVMBuildRetVoid(g->builder); - - // We need to resume the caller by tail calling them, - // but first write through the result pointer and possibly - // error return trace pointer. - LLVMPositionBuilderAtEnd(g->builder, resume_them_block); - - if (ret_type_has_bits) { - // If the awaiter result pointer is non-null, we need to copy the result to there. - LLVMBasicBlockRef copy_block = LLVMAppendBasicBlock(g->cur_fn_val, "CopyResult"); - LLVMBasicBlockRef copy_end_block = LLVMAppendBasicBlock(g->cur_fn_val, "CopyResultEnd"); - LLVMValueRef awaiter_ret_ptr_ptr = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr, frame_ret_start + 1, ""); - LLVMValueRef awaiter_ret_ptr = LLVMBuildLoad(g->builder, awaiter_ret_ptr_ptr, ""); - LLVMValueRef zero_ptr = LLVMConstNull(LLVMTypeOf(awaiter_ret_ptr)); - LLVMValueRef need_copy_bit = LLVMBuildICmp(g->builder, LLVMIntNE, awaiter_ret_ptr, zero_ptr, ""); - LLVMBuildCondBr(g->builder, need_copy_bit, copy_block, copy_end_block); - - LLVMPositionBuilderAtEnd(g->builder, copy_block); - LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0); - LLVMValueRef dest_ptr_casted = LLVMBuildBitCast(g->builder, awaiter_ret_ptr, ptr_u8, ""); - LLVMValueRef src_ptr_casted = LLVMBuildBitCast(g->builder, g->cur_ret_ptr, ptr_u8, ""); - bool is_volatile = false; - uint32_t abi_align = get_abi_alignment(g, ret_type); - LLVMValueRef byte_count_val = LLVMConstInt(usize_type_ref, type_size(g, ret_type), false); - ZigLLVMBuildMemCpy(g->builder, - dest_ptr_casted, abi_align, - src_ptr_casted, abi_align, byte_count_val, is_volatile); - LLVMBuildBr(g->builder, copy_end_block); - - LLVMPositionBuilderAtEnd(g->builder, copy_end_block); - if (codegen_fn_has_err_ret_tracing_arg(g, ret_type)) { - LLVMValueRef awaiter_trace_ptr_ptr = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr, - frame_index_trace_arg(g, ret_type) + 1, ""); - LLVMValueRef dest_trace_ptr = LLVMBuildLoad(g->builder, awaiter_trace_ptr_ptr, ""); - bool is_llvm_alloca; - LLVMValueRef my_err_trace_val = get_cur_err_ret_trace_val(g, instruction->base.base.scope, &is_llvm_alloca); - LLVMValueRef args[] = { dest_trace_ptr, my_err_trace_val }; - ZigLLVMBuildCall(g->builder, get_merge_err_ret_traces_fn_val(g), args, 2, - get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_CallAttrAuto, ""); - } - } - - // Resume the caller by tail calling them. - ZigType *any_frame_type = get_any_frame_type(g, ret_type); - LLVMValueRef their_frame_ptr = LLVMBuildIntToPtr(g->builder, prev_val, get_llvm_type(g, any_frame_type), ""); - LLVMValueRef call_inst = gen_resume(g, nullptr, their_frame_ptr, ResumeIdReturn); - set_tail_call_if_appropriate(g, call_inst); - LLVMBuildRetVoid(g->builder); -} - -static LLVMValueRef ir_render_return(CodeGen *g, IrExecutableGen *executable, IrInstGenReturn *instruction) { - if (fn_is_async(g->cur_fn)) { - gen_async_return(g, instruction); - return nullptr; - } - - if (want_first_arg_sret(g, &g->cur_fn->type_entry->data.fn.fn_type_id)) { - if (instruction->operand == nullptr) { - LLVMBuildRetVoid(g->builder); - return nullptr; - } - assert(g->cur_ret_ptr); - ir_assert(instruction->operand->value->special != ConstValSpecialRuntime, &instruction->base); - LLVMValueRef value = ir_llvm_value(g, instruction->operand); - ZigType *return_type = instruction->operand->value->type; - gen_assign_raw(g, g->cur_ret_ptr, get_pointer_to_type(g, return_type, false), value); - LLVMBuildRetVoid(g->builder); - } else if (g->cur_fn->type_entry->data.fn.fn_type_id.cc != CallingConventionAsync && - handle_is_ptr(g, g->cur_fn->type_entry->data.fn.fn_type_id.return_type)) - { - if (instruction->operand == nullptr) { - LLVMValueRef by_val_value = gen_load_untyped(g, g->cur_ret_ptr, 0, false, ""); - LLVMBuildRet(g->builder, by_val_value); - } else { - LLVMValueRef value = ir_llvm_value(g, instruction->operand); - LLVMValueRef by_val_value = gen_load_untyped(g, value, 0, false, ""); - LLVMBuildRet(g->builder, by_val_value); - } - } else if (instruction->operand == nullptr) { - if (g->cur_ret_ptr == nullptr) { - LLVMBuildRetVoid(g->builder); - } else { - LLVMValueRef by_val_value = gen_load_untyped(g, g->cur_ret_ptr, 0, false, ""); - LLVMBuildRet(g->builder, by_val_value); - } - } else { - LLVMValueRef value = ir_llvm_value(g, instruction->operand); - LLVMBuildRet(g->builder, value); - } - return nullptr; -} - -enum class ScalarizePredicate { - // Returns true iff all the elements in the vector are 1. - // Equivalent to folding all the bits with `and`. - All, - // Returns true iff there's at least one element in the vector that is 1. - // Equivalent to folding all the bits with `or`. - Any, -}; - -// Collapses a vector into a single i1 according to the given predicate -static LLVMValueRef scalarize_cmp_result(CodeGen *g, LLVMValueRef val, ScalarizePredicate predicate) { - assert(LLVMGetTypeKind(LLVMTypeOf(val)) == LLVMVectorTypeKind); - LLVMTypeRef scalar_type = LLVMIntType(LLVMGetVectorSize(LLVMTypeOf(val))); - LLVMValueRef casted = LLVMBuildBitCast(g->builder, val, scalar_type, ""); - - switch (predicate) { - case ScalarizePredicate::Any: { - LLVMValueRef all_zeros = LLVMConstNull(scalar_type); - return LLVMBuildICmp(g->builder, LLVMIntNE, casted, all_zeros, ""); - } - case ScalarizePredicate::All: { - LLVMValueRef all_ones = LLVMConstAllOnes(scalar_type); - return LLVMBuildICmp(g->builder, LLVMIntEQ, casted, all_ones, ""); - } - } - - zig_unreachable(); -} - - -static LLVMValueRef gen_overflow_shl_op(CodeGen *g, ZigType *operand_type, - LLVMValueRef val1, LLVMValueRef val2) -{ - // for unsigned left shifting, we do the lossy shift, then logically shift - // right the same number of bits - // if the values don't match, we have an overflow - // for signed left shifting we do the same except arithmetic shift right - ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? - operand_type->data.vector.elem_type : operand_type; - - assert(scalar_type->id == ZigTypeIdInt); - - LLVMValueRef result = LLVMBuildShl(g->builder, val1, val2, ""); - LLVMValueRef orig_val; - if (scalar_type->data.integral.is_signed) { - orig_val = LLVMBuildAShr(g->builder, result, val2, ""); - } else { - orig_val = LLVMBuildLShr(g->builder, result, val2, ""); - } - LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, val1, orig_val, ""); - - LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowOk"); - LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowFail"); - if (operand_type->id == ZigTypeIdVector) { - ok_bit = scalarize_cmp_result(g, ok_bit, ScalarizePredicate::All); - } - LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); - - LLVMPositionBuilderAtEnd(g->builder, fail_block); - gen_safety_crash(g, PanicMsgIdShlOverflowedBits); - - LLVMPositionBuilderAtEnd(g->builder, ok_block); - return result; -} - -static LLVMValueRef gen_overflow_shr_op(CodeGen *g, ZigType *operand_type, - LLVMValueRef val1, LLVMValueRef val2) -{ - ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? - operand_type->data.vector.elem_type : operand_type; - - assert(scalar_type->id == ZigTypeIdInt); - - LLVMValueRef result; - if (scalar_type->data.integral.is_signed) { - result = LLVMBuildAShr(g->builder, val1, val2, ""); - } else { - result = LLVMBuildLShr(g->builder, val1, val2, ""); - } - LLVMValueRef orig_val = LLVMBuildShl(g->builder, result, val2, ""); - LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, val1, orig_val, ""); - - LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowOk"); - LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowFail"); - if (operand_type->id == ZigTypeIdVector) { - ok_bit = scalarize_cmp_result(g, ok_bit, ScalarizePredicate::All); - } - LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); - - LLVMPositionBuilderAtEnd(g->builder, fail_block); - gen_safety_crash(g, PanicMsgIdShrOverflowedBits); - - LLVMPositionBuilderAtEnd(g->builder, ok_block); - return result; -} - -static LLVMValueRef gen_float_op(CodeGen *g, LLVMValueRef val, ZigType *type_entry, BuiltinFnId op) { - assert(type_entry->id == ZigTypeIdFloat || type_entry->id == ZigTypeIdVector); - LLVMValueRef floor_fn = get_float_fn(g, type_entry, ZigLLVMFnIdFloatOp, op); - return LLVMBuildCall(g->builder, floor_fn, &val, 1, ""); -} - -enum DivKind { - DivKindFloat, - DivKindTrunc, - DivKindFloor, - DivKindExact, -}; - -static LLVMValueRef bigint_to_llvm_const(LLVMTypeRef type_ref, BigInt *bigint) { - if (bigint->digit_count == 0) { - return LLVMConstNull(type_ref); - } - - if (LLVMGetTypeKind(type_ref) == LLVMVectorTypeKind) { - const unsigned vector_len = LLVMGetVectorSize(type_ref); - LLVMTypeRef elem_type = LLVMGetElementType(type_ref); - - LLVMValueRef *values = heap::c_allocator.allocate_nonzero(vector_len); - // Create a vector with all the elements having the same value - for (unsigned i = 0; i < vector_len; i++) { - values[i] = bigint_to_llvm_const(elem_type, bigint); - } - LLVMValueRef result = LLVMConstVector(values, vector_len); - heap::c_allocator.deallocate(values, vector_len); - return result; - } - - LLVMValueRef unsigned_val; - if (bigint->digit_count == 1) { - unsigned_val = LLVMConstInt(type_ref, bigint_ptr(bigint)[0], false); - } else { - unsigned_val = LLVMConstIntOfArbitraryPrecision(type_ref, bigint->digit_count, bigint_ptr(bigint)); - } - if (bigint->is_negative) { - return LLVMConstNeg(unsigned_val); - } else { - return unsigned_val; - } -} - -static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast_math, - LLVMValueRef val1, LLVMValueRef val2, ZigType *operand_type, DivKind div_kind) -{ - ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? - operand_type->data.vector.elem_type : operand_type; - - ZigLLVMSetFastMath(g->builder, want_fast_math); - - LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, operand_type)); - if (want_runtime_safety && (want_fast_math || scalar_type->id != ZigTypeIdFloat)) { - // Safety check: divisor != 0 - LLVMValueRef is_zero_bit; - if (scalar_type->id == ZigTypeIdInt) { - is_zero_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, zero, ""); - } else if (scalar_type->id == ZigTypeIdFloat) { - is_zero_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, val2, zero, ""); - } else { - zig_unreachable(); - } - - if (operand_type->id == ZigTypeIdVector) { - is_zero_bit = scalarize_cmp_result(g, is_zero_bit, ScalarizePredicate::Any); - } - - LLVMBasicBlockRef div_zero_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivZeroFail"); - LLVMBasicBlockRef div_zero_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivZeroOk"); - LLVMBuildCondBr(g->builder, is_zero_bit, div_zero_fail_block, div_zero_ok_block); - - LLVMPositionBuilderAtEnd(g->builder, div_zero_fail_block); - gen_safety_crash(g, PanicMsgIdDivisionByZero); - - LLVMPositionBuilderAtEnd(g->builder, div_zero_ok_block); - - // Safety check: check for overflow (dividend = minInt and divisor = -1) - if (scalar_type->id == ZigTypeIdInt && scalar_type->data.integral.is_signed) { - LLVMValueRef neg_1_value = LLVMConstAllOnes(get_llvm_type(g, operand_type)); - BigInt int_min_bi = {0}; - eval_min_max_value_int(g, scalar_type, &int_min_bi, false); - LLVMValueRef int_min_value = bigint_to_llvm_const(get_llvm_type(g, operand_type), &int_min_bi); - - LLVMBasicBlockRef overflow_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivOverflowFail"); - LLVMBasicBlockRef overflow_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivOverflowOk"); - LLVMValueRef num_is_int_min = LLVMBuildICmp(g->builder, LLVMIntEQ, val1, int_min_value, ""); - LLVMValueRef den_is_neg_1 = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, neg_1_value, ""); - LLVMValueRef overflow_fail_bit = LLVMBuildAnd(g->builder, num_is_int_min, den_is_neg_1, ""); - if (operand_type->id == ZigTypeIdVector) { - overflow_fail_bit = scalarize_cmp_result(g, overflow_fail_bit, ScalarizePredicate::Any); - } - LLVMBuildCondBr(g->builder, overflow_fail_bit, overflow_fail_block, overflow_ok_block); - - LLVMPositionBuilderAtEnd(g->builder, overflow_fail_block); - gen_safety_crash(g, PanicMsgIdIntegerOverflow); - - LLVMPositionBuilderAtEnd(g->builder, overflow_ok_block); - } - } - - if (scalar_type->id == ZigTypeIdFloat) { - LLVMValueRef result = LLVMBuildFDiv(g->builder, val1, val2, ""); - switch (div_kind) { - case DivKindFloat: - return result; - case DivKindExact: - if (want_runtime_safety) { - // Safety check: a / b == floor(a / b) - LLVMValueRef floored = gen_float_op(g, result, operand_type, BuiltinFnIdFloor); - - LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk"); - LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail"); - LLVMValueRef ok_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, floored, result, ""); - if (operand_type->id == ZigTypeIdVector) { - ok_bit = scalarize_cmp_result(g, ok_bit, ScalarizePredicate::All); - } - LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); - - LLVMPositionBuilderAtEnd(g->builder, fail_block); - gen_safety_crash(g, PanicMsgIdExactDivisionRemainder); - - LLVMPositionBuilderAtEnd(g->builder, ok_block); - } - return result; - case DivKindTrunc: - { - LLVMBasicBlockRef ltz_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivTruncLTZero"); - LLVMBasicBlockRef gez_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivTruncGEZero"); - LLVMBasicBlockRef end_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivTruncEnd"); - LLVMValueRef ltz = LLVMBuildFCmp(g->builder, LLVMRealOLT, val1, zero, ""); - if (operand_type->id == ZigTypeIdVector) { - ltz = scalarize_cmp_result(g, ltz, ScalarizePredicate::Any); - } - LLVMBuildCondBr(g->builder, ltz, ltz_block, gez_block); - - LLVMPositionBuilderAtEnd(g->builder, ltz_block); - LLVMValueRef ceiled = gen_float_op(g, result, operand_type, BuiltinFnIdCeil); - LLVMBasicBlockRef ceiled_end_block = LLVMGetInsertBlock(g->builder); - LLVMBuildBr(g->builder, end_block); - - LLVMPositionBuilderAtEnd(g->builder, gez_block); - LLVMValueRef floored = gen_float_op(g, result, operand_type, BuiltinFnIdFloor); - LLVMBasicBlockRef floored_end_block = LLVMGetInsertBlock(g->builder); - LLVMBuildBr(g->builder, end_block); - - LLVMPositionBuilderAtEnd(g->builder, end_block); - LLVMValueRef phi = LLVMBuildPhi(g->builder, get_llvm_type(g, operand_type), ""); - LLVMValueRef incoming_values[] = { ceiled, floored }; - LLVMBasicBlockRef incoming_blocks[] = { ceiled_end_block, floored_end_block }; - LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2); - return phi; - } - case DivKindFloor: - return gen_float_op(g, result, operand_type, BuiltinFnIdFloor); - } - zig_unreachable(); - } - - assert(scalar_type->id == ZigTypeIdInt); - - switch (div_kind) { - case DivKindFloat: - zig_unreachable(); - case DivKindTrunc: - if (scalar_type->data.integral.is_signed) { - return LLVMBuildSDiv(g->builder, val1, val2, ""); - } else { - return LLVMBuildUDiv(g->builder, val1, val2, ""); - } - case DivKindExact: - if (want_runtime_safety) { - // Safety check: a % b == 0 - LLVMValueRef remainder_val; - if (scalar_type->data.integral.is_signed) { - remainder_val = LLVMBuildSRem(g->builder, val1, val2, ""); - } else { - remainder_val = LLVMBuildURem(g->builder, val1, val2, ""); - } - - LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk"); - LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail"); - LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, remainder_val, zero, ""); - if (operand_type->id == ZigTypeIdVector) { - ok_bit = scalarize_cmp_result(g, ok_bit, ScalarizePredicate::All); - } - LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); - - LLVMPositionBuilderAtEnd(g->builder, fail_block); - gen_safety_crash(g, PanicMsgIdExactDivisionRemainder); - - LLVMPositionBuilderAtEnd(g->builder, ok_block); - } - if (scalar_type->data.integral.is_signed) { - return LLVMBuildExactSDiv(g->builder, val1, val2, ""); - } else { - return LLVMBuildExactUDiv(g->builder, val1, val2, ""); - } - case DivKindFloor: - { - if (!scalar_type->data.integral.is_signed) { - return LLVMBuildUDiv(g->builder, val1, val2, ""); - } - // const d = @divTrunc(a, b); - // const r = @rem(a, b); - // return if (r == 0) d else d - ((a < 0) ^ (b < 0)); - - LLVMValueRef div_trunc = LLVMBuildSDiv(g->builder, val1, val2, ""); - LLVMValueRef rem = LLVMBuildSRem(g->builder, val1, val2, ""); - LLVMValueRef rem_eq_0 = LLVMBuildICmp(g->builder, LLVMIntEQ, rem, zero, ""); - LLVMValueRef a_lt_0 = LLVMBuildICmp(g->builder, LLVMIntSLT, val1, zero, ""); - LLVMValueRef b_lt_0 = LLVMBuildICmp(g->builder, LLVMIntSLT, val2, zero, ""); - LLVMValueRef a_b_xor = LLVMBuildXor(g->builder, a_lt_0, b_lt_0, ""); - LLVMValueRef a_b_xor_ext = LLVMBuildZExt(g->builder, a_b_xor, LLVMTypeOf(div_trunc), ""); - LLVMValueRef d_sub_xor = LLVMBuildSub(g->builder, div_trunc, a_b_xor_ext, ""); - return LLVMBuildSelect(g->builder, rem_eq_0, div_trunc, d_sub_xor, ""); - } - } - zig_unreachable(); -} - -enum RemKind { - RemKindRem, - RemKindMod, -}; - -static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast_math, - LLVMValueRef val1, LLVMValueRef val2, ZigType *operand_type, RemKind rem_kind) -{ - ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? - operand_type->data.vector.elem_type : operand_type; - - ZigLLVMSetFastMath(g->builder, want_fast_math); - - LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, operand_type)); - if (want_runtime_safety) { - // Safety check: divisor != 0 - LLVMValueRef is_zero_bit; - if (scalar_type->id == ZigTypeIdInt) { - LLVMIntPredicate pred = scalar_type->data.integral.is_signed ? LLVMIntSLE : LLVMIntEQ; - is_zero_bit = LLVMBuildICmp(g->builder, pred, val2, zero, ""); - } else if (scalar_type->id == ZigTypeIdFloat) { - is_zero_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, val2, zero, ""); - } else { - zig_unreachable(); - } - - if (operand_type->id == ZigTypeIdVector) { - is_zero_bit = scalarize_cmp_result(g, is_zero_bit, ScalarizePredicate::Any); - } - - LLVMBasicBlockRef rem_zero_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "RemZeroOk"); - LLVMBasicBlockRef rem_zero_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "RemZeroFail"); - LLVMBuildCondBr(g->builder, is_zero_bit, rem_zero_fail_block, rem_zero_ok_block); - - LLVMPositionBuilderAtEnd(g->builder, rem_zero_fail_block); - gen_safety_crash(g, PanicMsgIdRemainderDivisionByZero); - - LLVMPositionBuilderAtEnd(g->builder, rem_zero_ok_block); - } - - if (scalar_type->id == ZigTypeIdFloat) { - if (rem_kind == RemKindRem) { - return LLVMBuildFRem(g->builder, val1, val2, ""); - } else { - LLVMValueRef a = LLVMBuildFRem(g->builder, val1, val2, ""); - LLVMValueRef b = LLVMBuildFAdd(g->builder, a, val2, ""); - LLVMValueRef c = LLVMBuildFRem(g->builder, b, val2, ""); - LLVMValueRef ltz = LLVMBuildFCmp(g->builder, LLVMRealOLT, val1, zero, ""); - return LLVMBuildSelect(g->builder, ltz, c, a, ""); - } - } else { - assert(scalar_type->id == ZigTypeIdInt); - if (scalar_type->data.integral.is_signed) { - if (rem_kind == RemKindRem) { - return LLVMBuildSRem(g->builder, val1, val2, ""); - } else { - LLVMValueRef a = LLVMBuildSRem(g->builder, val1, val2, ""); - LLVMValueRef b = LLVMBuildNSWAdd(g->builder, a, val2, ""); - LLVMValueRef c = LLVMBuildSRem(g->builder, b, val2, ""); - LLVMValueRef ltz = LLVMBuildICmp(g->builder, LLVMIntSLT, val1, zero, ""); - return LLVMBuildSelect(g->builder, ltz, c, a, ""); - } - } else { - return LLVMBuildURem(g->builder, val1, val2, ""); - } - } - -} - -static void gen_shift_rhs_check(CodeGen *g, ZigType *lhs_type, ZigType *rhs_type, LLVMValueRef value) { - // We only check if the rhs value of the shift expression is greater or - // equal to the number of bits of the lhs if it's not a power of two, - // otherwise the check is useful as the allowed values are limited by the - // operand type itself - if (!is_power_of_2(lhs_type->data.integral.bit_count)) { - BigInt bit_count_bi = {0}; - bigint_init_unsigned(&bit_count_bi, lhs_type->data.integral.bit_count); - LLVMValueRef bit_count_value = bigint_to_llvm_const(get_llvm_type(g, rhs_type), - &bit_count_bi); - - LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "CheckFail"); - LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "CheckOk"); - LLVMValueRef less_than_bit = LLVMBuildICmp(g->builder, LLVMIntULT, value, bit_count_value, ""); - if (rhs_type->id == ZigTypeIdVector) { - less_than_bit = scalarize_cmp_result(g, less_than_bit, ScalarizePredicate::Any); - } - LLVMBuildCondBr(g->builder, less_than_bit, ok_block, fail_block); - - LLVMPositionBuilderAtEnd(g->builder, fail_block); - gen_safety_crash(g, PanicMsgIdShxTooBigRhs); - - LLVMPositionBuilderAtEnd(g->builder, ok_block); - } -} - -static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutableGen *executable, - IrInstGenBinOp *bin_op_instruction) -{ - IrBinOp op_id = bin_op_instruction->op_id; - IrInstGen *op1 = bin_op_instruction->op1; - IrInstGen *op2 = bin_op_instruction->op2; - - ZigType *operand_type = op1->value->type; - ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? operand_type->data.vector.elem_type : operand_type; - - bool want_runtime_safety = bin_op_instruction->safety_check_on && - ir_want_runtime_safety(g, &bin_op_instruction->base); - - LLVMValueRef op1_value = ir_llvm_value(g, op1); - LLVMValueRef op2_value = ir_llvm_value(g, op2); - - - switch (op_id) { - case IrBinOpInvalid: - case IrBinOpArrayCat: - case IrBinOpArrayMult: - case IrBinOpRemUnspecified: - zig_unreachable(); - case IrBinOpBoolOr: - return LLVMBuildOr(g->builder, op1_value, op2_value, ""); - case IrBinOpBoolAnd: - return LLVMBuildAnd(g->builder, op1_value, op2_value, ""); - case IrBinOpCmpEq: - case IrBinOpCmpNotEq: - case IrBinOpCmpLessThan: - case IrBinOpCmpGreaterThan: - case IrBinOpCmpLessOrEq: - case IrBinOpCmpGreaterOrEq: - if (scalar_type->id == ZigTypeIdFloat) { - ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base)); - LLVMRealPredicate pred = cmp_op_to_real_predicate(op_id); - return LLVMBuildFCmp(g->builder, pred, op1_value, op2_value, ""); - } else if (scalar_type->id == ZigTypeIdInt) { - LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, scalar_type->data.integral.is_signed); - return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, ""); - } else if (scalar_type->id == ZigTypeIdEnum || - scalar_type->id == ZigTypeIdErrorSet || - scalar_type->id == ZigTypeIdBool || - get_codegen_ptr_type_bail(g, scalar_type) != nullptr) - { - LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, false); - return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, ""); - } else { - zig_unreachable(); - } - case IrBinOpMult: - case IrBinOpMultWrap: - case IrBinOpAdd: - case IrBinOpAddWrap: - case IrBinOpSub: - case IrBinOpSubWrap: { - bool is_wrapping = (op_id == IrBinOpSubWrap || op_id == IrBinOpAddWrap || op_id == IrBinOpMultWrap); - AddSubMul add_sub_mul = - op_id == IrBinOpAdd || op_id == IrBinOpAddWrap ? AddSubMulAdd : - op_id == IrBinOpSub || op_id == IrBinOpSubWrap ? AddSubMulSub : - AddSubMulMul; - - if (scalar_type->id == ZigTypeIdPointer) { - LLVMValueRef subscript_value; - if (operand_type->id == ZigTypeIdVector) - zig_panic("TODO: Implement vector operations on pointers."); - - switch (add_sub_mul) { - case AddSubMulAdd: - subscript_value = op2_value; - break; - case AddSubMulSub: - subscript_value = LLVMBuildNeg(g->builder, op2_value, ""); - break; - case AddSubMulMul: - zig_unreachable(); - } - - // TODO runtime safety - return LLVMBuildInBoundsGEP(g->builder, op1_value, &subscript_value, 1, ""); - } else if (scalar_type->id == ZigTypeIdFloat) { - ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base)); - return float_op[add_sub_mul](g->builder, op1_value, op2_value, ""); - } else if (scalar_type->id == ZigTypeIdInt) { - if (is_wrapping) { - return wrap_op[add_sub_mul](g->builder, op1_value, op2_value, ""); - } else if (want_runtime_safety) { - return gen_overflow_op(g, operand_type, add_sub_mul, op1_value, op2_value); - } else if (scalar_type->data.integral.is_signed) { - return signed_op[add_sub_mul](g->builder, op1_value, op2_value, ""); - } else { - return unsigned_op[add_sub_mul](g->builder, op1_value, op2_value, ""); - } - } else { - zig_unreachable(); - } - } - case IrBinOpBinOr: - return LLVMBuildOr(g->builder, op1_value, op2_value, ""); - case IrBinOpBinXor: - return LLVMBuildXor(g->builder, op1_value, op2_value, ""); - case IrBinOpBinAnd: - return LLVMBuildAnd(g->builder, op1_value, op2_value, ""); - case IrBinOpBitShiftLeftLossy: - case IrBinOpBitShiftLeftExact: - { - assert(scalar_type->id == ZigTypeIdInt); - LLVMValueRef op2_casted = LLVMBuildZExt(g->builder, op2_value, - LLVMTypeOf(op1_value), ""); - - if (want_runtime_safety) { - gen_shift_rhs_check(g, scalar_type, op2->value->type, op2_value); - } - - bool is_sloppy = (op_id == IrBinOpBitShiftLeftLossy); - if (is_sloppy) { - return LLVMBuildShl(g->builder, op1_value, op2_casted, ""); - } else if (want_runtime_safety) { - return gen_overflow_shl_op(g, operand_type, op1_value, op2_casted); - } else if (scalar_type->data.integral.is_signed) { - return ZigLLVMBuildNSWShl(g->builder, op1_value, op2_casted, ""); - } else { - return ZigLLVMBuildNUWShl(g->builder, op1_value, op2_casted, ""); - } - } - case IrBinOpBitShiftRightLossy: - case IrBinOpBitShiftRightExact: - { - assert(scalar_type->id == ZigTypeIdInt); - LLVMValueRef op2_casted = LLVMBuildZExt(g->builder, op2_value, - LLVMTypeOf(op1_value), ""); - - if (want_runtime_safety) { - gen_shift_rhs_check(g, scalar_type, op2->value->type, op2_value); - } - - bool is_sloppy = (op_id == IrBinOpBitShiftRightLossy); - if (is_sloppy) { - if (scalar_type->data.integral.is_signed) { - return LLVMBuildAShr(g->builder, op1_value, op2_casted, ""); - } else { - return LLVMBuildLShr(g->builder, op1_value, op2_casted, ""); - } - } else if (want_runtime_safety) { - return gen_overflow_shr_op(g, operand_type, op1_value, op2_casted); - } else if (scalar_type->data.integral.is_signed) { - return ZigLLVMBuildAShrExact(g->builder, op1_value, op2_casted, ""); - } else { - return ZigLLVMBuildLShrExact(g->builder, op1_value, op2_casted, ""); - } - } - case IrBinOpDivUnspecified: - return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), - op1_value, op2_value, operand_type, DivKindFloat); - case IrBinOpDivExact: - return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), - op1_value, op2_value, operand_type, DivKindExact); - case IrBinOpDivTrunc: - return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), - op1_value, op2_value, operand_type, DivKindTrunc); - case IrBinOpDivFloor: - return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), - op1_value, op2_value, operand_type, DivKindFloor); - case IrBinOpRemRem: - return gen_rem(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), - op1_value, op2_value, operand_type, RemKindRem); - case IrBinOpRemMod: - return gen_rem(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), - op1_value, op2_value, operand_type, RemKindMod); - } - zig_unreachable(); -} - -static void add_error_range_check(CodeGen *g, ZigType *err_set_type, ZigType *int_type, LLVMValueRef target_val) { - assert(err_set_type->id == ZigTypeIdErrorSet); - - if (type_is_global_error_set(err_set_type)) { - LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, int_type)); - LLVMValueRef neq_zero_bit = LLVMBuildICmp(g->builder, LLVMIntNE, target_val, zero, ""); - LLVMValueRef ok_bit; - - BigInt biggest_possible_err_val = {0}; - eval_min_max_value_int(g, int_type, &biggest_possible_err_val, true); - - if (bigint_fits_in_bits(&biggest_possible_err_val, 64, false) && - bigint_as_usize(&biggest_possible_err_val) < g->errors_by_index.length) - { - ok_bit = neq_zero_bit; - } else { - LLVMValueRef error_value_count = LLVMConstInt(get_llvm_type(g, int_type), g->errors_by_index.length, false); - LLVMValueRef in_bounds_bit = LLVMBuildICmp(g->builder, LLVMIntULT, target_val, error_value_count, ""); - ok_bit = LLVMBuildAnd(g->builder, neq_zero_bit, in_bounds_bit, ""); - } - - LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "IntToErrOk"); - LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "IntToErrFail"); - - LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); - - LLVMPositionBuilderAtEnd(g->builder, fail_block); - gen_safety_crash(g, PanicMsgIdInvalidErrorCode); - - LLVMPositionBuilderAtEnd(g->builder, ok_block); - } else { - LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "IntToErrOk"); - LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "IntToErrFail"); - - uint32_t err_count = err_set_type->data.error_set.err_count; - LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, target_val, fail_block, err_count); - for (uint32_t i = 0; i < err_count; i += 1) { - LLVMValueRef case_value = LLVMConstInt(get_llvm_type(g, g->err_tag_type), - err_set_type->data.error_set.errors[i]->value, false); - LLVMAddCase(switch_instr, case_value, ok_block); - } - - LLVMPositionBuilderAtEnd(g->builder, fail_block); - gen_safety_crash(g, PanicMsgIdInvalidErrorCode); - - LLVMPositionBuilderAtEnd(g->builder, ok_block); - } -} - -static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutableGen *executable, - IrInstGenCast *cast_instruction) -{ - Error err; - ZigType *actual_type = cast_instruction->value->value->type; - ZigType *wanted_type = cast_instruction->base.value->type; - bool wanted_type_has_bits; - if ((err = type_has_bits2(g, wanted_type, &wanted_type_has_bits))) - codegen_report_errors_and_exit(g); - if (!wanted_type_has_bits) - return nullptr; - LLVMValueRef expr_val = ir_llvm_value(g, cast_instruction->value); - ir_assert(expr_val, &cast_instruction->base); - - switch (cast_instruction->cast_op) { - case CastOpNoCast: - case CastOpNumLitToConcrete: - zig_unreachable(); - case CastOpNoop: - if (actual_type->id == ZigTypeIdPointer && wanted_type->id == ZigTypeIdPointer && - actual_type->data.pointer.child_type->id == ZigTypeIdArray && - wanted_type->data.pointer.child_type->id == ZigTypeIdArray) - { - return LLVMBuildBitCast(g->builder, expr_val, get_llvm_type(g, wanted_type), ""); - } else { - return expr_val; - } - case CastOpIntToFloat: - assert(actual_type->id == ZigTypeIdInt); - if (actual_type->data.integral.is_signed) { - return LLVMBuildSIToFP(g->builder, expr_val, get_llvm_type(g, wanted_type), ""); - } else { - return LLVMBuildUIToFP(g->builder, expr_val, get_llvm_type(g, wanted_type), ""); - } - case CastOpFloatToInt: { - assert(wanted_type->id == ZigTypeIdInt); - ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &cast_instruction->base)); - - bool want_safety = ir_want_runtime_safety(g, &cast_instruction->base); - - LLVMValueRef result; - if (wanted_type->data.integral.is_signed) { - result = LLVMBuildFPToSI(g->builder, expr_val, get_llvm_type(g, wanted_type), ""); - } else { - result = LLVMBuildFPToUI(g->builder, expr_val, get_llvm_type(g, wanted_type), ""); - } - - if (want_safety) { - LLVMValueRef back_to_float; - if (wanted_type->data.integral.is_signed) { - back_to_float = LLVMBuildSIToFP(g->builder, result, LLVMTypeOf(expr_val), ""); - } else { - back_to_float = LLVMBuildUIToFP(g->builder, result, LLVMTypeOf(expr_val), ""); - } - LLVMValueRef difference = LLVMBuildFSub(g->builder, expr_val, back_to_float, ""); - LLVMValueRef one_pos = LLVMConstReal(LLVMTypeOf(expr_val), 1.0f); - LLVMValueRef one_neg = LLVMConstReal(LLVMTypeOf(expr_val), -1.0f); - LLVMValueRef ok_bit_pos = LLVMBuildFCmp(g->builder, LLVMRealOLT, difference, one_pos, ""); - LLVMValueRef ok_bit_neg = LLVMBuildFCmp(g->builder, LLVMRealOGT, difference, one_neg, ""); - LLVMValueRef ok_bit = LLVMBuildAnd(g->builder, ok_bit_pos, ok_bit_neg, ""); - LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "FloatCheckOk"); - LLVMBasicBlockRef bad_block = LLVMAppendBasicBlock(g->cur_fn_val, "FloatCheckFail"); - LLVMBuildCondBr(g->builder, ok_bit, ok_block, bad_block); - LLVMPositionBuilderAtEnd(g->builder, bad_block); - gen_safety_crash(g, PanicMsgIdFloatToInt); - LLVMPositionBuilderAtEnd(g->builder, ok_block); - } - return result; - } - case CastOpBoolToInt: - assert(wanted_type->id == ZigTypeIdInt); - assert(actual_type->id == ZigTypeIdBool); - return LLVMBuildZExt(g->builder, expr_val, get_llvm_type(g, wanted_type), ""); - case CastOpErrSet: - if (ir_want_runtime_safety(g, &cast_instruction->base)) { - add_error_range_check(g, wanted_type, g->err_tag_type, expr_val); - } - return expr_val; - case CastOpBitCast: - return LLVMBuildBitCast(g->builder, expr_val, get_llvm_type(g, wanted_type), ""); - } - zig_unreachable(); -} - -static LLVMValueRef ir_render_ptr_of_array_to_slice(CodeGen *g, IrExecutableGen *executable, - IrInstGenPtrOfArrayToSlice *instruction) -{ - ZigType *actual_type = instruction->operand->value->type; - ZigType *slice_type = instruction->base.value->type; - ZigType *slice_ptr_type = slice_type->data.structure.fields[slice_ptr_index]->type_entry; - size_t ptr_index = slice_type->data.structure.fields[slice_ptr_index]->gen_index; - size_t len_index = slice_type->data.structure.fields[slice_len_index]->gen_index; - - LLVMValueRef result_loc = ir_llvm_value(g, instruction->result_loc); - - assert(actual_type->id == ZigTypeIdPointer); - ZigType *array_type = actual_type->data.pointer.child_type; - assert(array_type->id == ZigTypeIdArray); - - if (type_has_bits(g, actual_type)) { - LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, result_loc, ptr_index, ""); - LLVMValueRef indices[] = { - LLVMConstNull(g->builtin_types.entry_usize->llvm_type), - LLVMConstInt(g->builtin_types.entry_usize->llvm_type, 0, false), - }; - LLVMValueRef expr_val = ir_llvm_value(g, instruction->operand); - LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, expr_val, indices, 2, ""); - gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); - } else if (ir_want_runtime_safety(g, &instruction->base)) { - LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, result_loc, ptr_index, ""); - gen_undef_init(g, slice_ptr_type->abi_align, slice_ptr_type, ptr_field_ptr); - } - - LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, result_loc, len_index, ""); - LLVMValueRef len_value = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, - array_type->data.array.len, false); - gen_store_untyped(g, len_value, len_field_ptr, 0, false); - - return result_loc; -} - -static LLVMValueRef ir_render_ptr_cast(CodeGen *g, IrExecutableGen *executable, - IrInstGenPtrCast *instruction) -{ - ZigType *wanted_type = instruction->base.value->type; - if (!type_has_bits(g, wanted_type)) { - return nullptr; - } - LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr); - LLVMValueRef result_ptr = LLVMBuildBitCast(g->builder, ptr, get_llvm_type(g, wanted_type), ""); - bool want_safety_check = instruction->safety_check_on && ir_want_runtime_safety(g, &instruction->base); - if (!want_safety_check || ptr_allows_addr_zero(wanted_type)) - return result_ptr; - - LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(result_ptr)); - LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntNE, result_ptr, zero, ""); - LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "PtrCastFail"); - LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "PtrCastOk"); - LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); - - LLVMPositionBuilderAtEnd(g->builder, fail_block); - gen_safety_crash(g, PanicMsgIdPtrCastNull); - - LLVMPositionBuilderAtEnd(g->builder, ok_block); - return result_ptr; -} - -static LLVMValueRef ir_render_bit_cast(CodeGen *g, IrExecutableGen *executable, - IrInstGenBitCast *instruction) -{ - ZigType *wanted_type = instruction->base.value->type; - ZigType *actual_type = instruction->operand->value->type; - LLVMValueRef value = ir_llvm_value(g, instruction->operand); - - bool wanted_is_ptr = handle_is_ptr(g, wanted_type); - bool actual_is_ptr = handle_is_ptr(g, actual_type); - if (wanted_is_ptr == actual_is_ptr) { - // We either bitcast the value directly or bitcast the pointer which does a pointer cast - LLVMTypeRef wanted_type_ref = wanted_is_ptr ? - LLVMPointerType(get_llvm_type(g, wanted_type), 0) : get_llvm_type(g, wanted_type); - return LLVMBuildBitCast(g->builder, value, wanted_type_ref, ""); - } else if (actual_is_ptr) { - // A scalar is wanted but we got a pointer - LLVMTypeRef wanted_ptr_type_ref = LLVMPointerType(get_llvm_type(g, wanted_type), 0); - LLVMValueRef bitcasted_ptr = LLVMBuildBitCast(g->builder, value, wanted_ptr_type_ref, ""); - uint32_t alignment = get_abi_alignment(g, actual_type); - return gen_load_untyped(g, bitcasted_ptr, alignment, false, ""); - } else { - // A pointer is wanted but we got a scalar - assert(actual_type->id == ZigTypeIdPointer); - LLVMTypeRef wanted_ptr_type_ref = LLVMPointerType(get_llvm_type(g, wanted_type), 0); - return LLVMBuildBitCast(g->builder, value, wanted_ptr_type_ref, ""); - } -} - -static LLVMValueRef ir_render_widen_or_shorten(CodeGen *g, IrExecutableGen *executable, - IrInstGenWidenOrShorten *instruction) -{ - ZigType *actual_type = instruction->target->value->type; - // TODO instead of this logic, use the Noop instruction to change the type from - // enum_tag to the underlying int type - ZigType *int_type; - if (actual_type->id == ZigTypeIdEnum) { - int_type = actual_type->data.enumeration.tag_int_type; - } else { - int_type = actual_type; - } - LLVMValueRef target_val = ir_llvm_value(g, instruction->target); - return gen_widen_or_shorten(g, ir_want_runtime_safety(g, &instruction->base), int_type, - instruction->base.value->type, target_val); -} - -static LLVMValueRef ir_render_int_to_ptr(CodeGen *g, IrExecutableGen *executable, IrInstGenIntToPtr *instruction) { - ZigType *wanted_type = instruction->base.value->type; - LLVMValueRef target_val = ir_llvm_value(g, instruction->target); - const uint32_t align_bytes = get_ptr_align(g, wanted_type); - - if (ir_want_runtime_safety(g, &instruction->base) && align_bytes > 1) { - ZigType *usize = g->builtin_types.entry_usize; - LLVMValueRef zero = LLVMConstNull(usize->llvm_type); - - if (!ptr_allows_addr_zero(wanted_type)) { - LLVMValueRef is_zero_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, target_val, zero, ""); - LLVMBasicBlockRef bad_block = LLVMAppendBasicBlock(g->cur_fn_val, "PtrToIntBad"); - LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "PtrToIntOk"); - LLVMBuildCondBr(g->builder, is_zero_bit, bad_block, ok_block); - - LLVMPositionBuilderAtEnd(g->builder, bad_block); - gen_safety_crash(g, PanicMsgIdPtrCastNull); - - LLVMPositionBuilderAtEnd(g->builder, ok_block); - } - - { - LLVMValueRef alignment_minus_1 = LLVMConstInt(usize->llvm_type, align_bytes - 1, false); - LLVMValueRef anded_val = LLVMBuildAnd(g->builder, target_val, alignment_minus_1, ""); - LLVMValueRef is_ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, anded_val, zero, ""); - LLVMBasicBlockRef bad_block = LLVMAppendBasicBlock(g->cur_fn_val, "PtrToIntAlignBad"); - LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "PtrToIntAlignOk"); - LLVMBuildCondBr(g->builder, is_ok_bit, ok_block, bad_block); - - LLVMPositionBuilderAtEnd(g->builder, bad_block); - gen_safety_crash(g, PanicMsgIdIncorrectAlignment); - - LLVMPositionBuilderAtEnd(g->builder, ok_block); - } - } - return LLVMBuildIntToPtr(g->builder, target_val, get_llvm_type(g, wanted_type), ""); -} - -static LLVMValueRef ir_render_ptr_to_int(CodeGen *g, IrExecutableGen *executable, IrInstGenPtrToInt *instruction) { - ZigType *wanted_type = instruction->base.value->type; - LLVMValueRef target_val = ir_llvm_value(g, instruction->target); - return LLVMBuildPtrToInt(g->builder, target_val, get_llvm_type(g, wanted_type), ""); -} - -static LLVMValueRef ir_render_int_to_enum(CodeGen *g, IrExecutableGen *executable, IrInstGenIntToEnum *instruction) { - ZigType *wanted_type = instruction->base.value->type; - assert(wanted_type->id == ZigTypeIdEnum); - ZigType *tag_int_type = wanted_type->data.enumeration.tag_int_type; - - LLVMValueRef target_val = ir_llvm_value(g, instruction->target); - LLVMValueRef tag_int_value = gen_widen_or_shorten(g, ir_want_runtime_safety(g, &instruction->base), - instruction->target->value->type, tag_int_type, target_val); - - if (ir_want_runtime_safety(g, &instruction->base) && !wanted_type->data.enumeration.non_exhaustive) { - LLVMBasicBlockRef bad_value_block = LLVMAppendBasicBlock(g->cur_fn_val, "BadValue"); - LLVMBasicBlockRef ok_value_block = LLVMAppendBasicBlock(g->cur_fn_val, "OkValue"); - size_t field_count = wanted_type->data.enumeration.src_field_count; - LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, tag_int_value, bad_value_block, field_count); - - HashMap occupied_tag_values = {}; - occupied_tag_values.init(field_count); - - for (size_t field_i = 0; field_i < field_count; field_i += 1) { - TypeEnumField *type_enum_field = &wanted_type->data.enumeration.fields[field_i]; - - Buf *name = type_enum_field->name; - auto entry = occupied_tag_values.put_unique(type_enum_field->value, name); - if (entry != nullptr) { - continue; - } - - LLVMValueRef this_tag_int_value = bigint_to_llvm_const(get_llvm_type(g, tag_int_type), - &type_enum_field->value); - LLVMAddCase(switch_instr, this_tag_int_value, ok_value_block); - } - occupied_tag_values.deinit(); - LLVMPositionBuilderAtEnd(g->builder, bad_value_block); - gen_safety_crash(g, PanicMsgIdBadEnumValue); - - LLVMPositionBuilderAtEnd(g->builder, ok_value_block); - } - return tag_int_value; -} - -static LLVMValueRef ir_render_int_to_err(CodeGen *g, IrExecutableGen *executable, IrInstGenIntToErr *instruction) { - ZigType *wanted_type = instruction->base.value->type; - assert(wanted_type->id == ZigTypeIdErrorSet); - - ZigType *actual_type = instruction->target->value->type; - assert(actual_type->id == ZigTypeIdInt); - assert(!actual_type->data.integral.is_signed); - - LLVMValueRef target_val = ir_llvm_value(g, instruction->target); - - if (ir_want_runtime_safety(g, &instruction->base)) { - add_error_range_check(g, wanted_type, actual_type, target_val); - } - - return gen_widen_or_shorten(g, false, actual_type, g->err_tag_type, target_val); -} - -static LLVMValueRef ir_render_err_to_int(CodeGen *g, IrExecutableGen *executable, IrInstGenErrToInt *instruction) { - ZigType *wanted_type = instruction->base.value->type; - assert(wanted_type->id == ZigTypeIdInt); - assert(!wanted_type->data.integral.is_signed); - - ZigType *actual_type = instruction->target->value->type; - LLVMValueRef target_val = ir_llvm_value(g, instruction->target); - - if (actual_type->id == ZigTypeIdErrorSet) { - return gen_widen_or_shorten(g, ir_want_runtime_safety(g, &instruction->base), - g->err_tag_type, wanted_type, target_val); - } else if (actual_type->id == ZigTypeIdErrorUnion) { - // this should have been a compile time constant - assert(type_has_bits(g, actual_type->data.error_union.err_set_type)); - - if (!type_has_bits(g, actual_type->data.error_union.payload_type)) { - return gen_widen_or_shorten(g, ir_want_runtime_safety(g, &instruction->base), - g->err_tag_type, wanted_type, target_val); - } else { - zig_panic("TODO err to int when error union payload type not void"); - } - } else { - zig_unreachable(); - } -} - -static LLVMValueRef ir_render_unreachable(CodeGen *g, IrExecutableGen *executable, - IrInstGenUnreachable *unreachable_instruction) -{ - if (ir_want_runtime_safety(g, &unreachable_instruction->base)) { - gen_safety_crash(g, PanicMsgIdUnreachable); - } else { - LLVMBuildUnreachable(g->builder); - } - return nullptr; -} - -static LLVMValueRef ir_render_cond_br(CodeGen *g, IrExecutableGen *executable, - IrInstGenCondBr *cond_br_instruction) -{ - LLVMBuildCondBr(g->builder, - ir_llvm_value(g, cond_br_instruction->condition), - cond_br_instruction->then_block->llvm_block, - cond_br_instruction->else_block->llvm_block); - return nullptr; -} - -static LLVMValueRef ir_render_br(CodeGen *g, IrExecutableGen *executable, IrInstGenBr *br_instruction) { - LLVMBuildBr(g->builder, br_instruction->dest_block->llvm_block); - return nullptr; -} - -static LLVMValueRef ir_render_binary_not(CodeGen *g, IrExecutableGen *executable, - IrInstGenBinaryNot *inst) -{ - LLVMValueRef operand = ir_llvm_value(g, inst->operand); - return LLVMBuildNot(g->builder, operand, ""); -} - -static LLVMValueRef ir_gen_negation(CodeGen *g, IrInstGen *inst, IrInstGen *operand, bool wrapping) { - LLVMValueRef llvm_operand = ir_llvm_value(g, operand); - ZigType *operand_type = operand->value->type; - ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? - operand_type->data.vector.elem_type : operand_type; - - if (scalar_type->id == ZigTypeIdFloat) { - ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, inst)); - return LLVMBuildFNeg(g->builder, llvm_operand, ""); - } else if (scalar_type->id == ZigTypeIdInt) { - if (wrapping) { - return LLVMBuildNeg(g->builder, llvm_operand, ""); - } else if (ir_want_runtime_safety(g, inst)) { - LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(llvm_operand)); - return gen_overflow_op(g, operand_type, AddSubMulSub, zero, llvm_operand); - } else if (scalar_type->data.integral.is_signed) { - return LLVMBuildNSWNeg(g->builder, llvm_operand, ""); - } else { - zig_unreachable(); - } - } else { - zig_unreachable(); - } -} - -static LLVMValueRef ir_render_negation(CodeGen *g, IrExecutableGen *executable, - IrInstGenNegation *inst) -{ - return ir_gen_negation(g, &inst->base, inst->operand, false); -} - -static LLVMValueRef ir_render_negation_wrapping(CodeGen *g, IrExecutableGen *executable, - IrInstGenNegationWrapping *inst) -{ - return ir_gen_negation(g, &inst->base, inst->operand, true); -} - -static LLVMValueRef ir_render_bool_not(CodeGen *g, IrExecutableGen *executable, IrInstGenBoolNot *instruction) { - LLVMValueRef value = ir_llvm_value(g, instruction->value); - LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(value)); - return LLVMBuildICmp(g->builder, LLVMIntEQ, value, zero, ""); -} - -static void render_decl_var(CodeGen *g, ZigVar *var) { - if (!type_has_bits(g, var->var_type)) - return; - - var->value_ref = ir_llvm_value(g, var->ptr_instruction); - gen_var_debug_decl(g, var); -} - -static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutableGen *executable, IrInstGenDeclVar *instruction) { - instruction->var->ptr_instruction = instruction->var_ptr; - instruction->var->did_the_decl_codegen = true; - render_decl_var(g, instruction->var); - return nullptr; -} - -static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutableGen *executable, - IrInstGenLoadPtr *instruction) -{ - ZigType *child_type = instruction->base.value->type; - if (!type_has_bits(g, child_type)) - return nullptr; - - LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr); - ZigType *ptr_type = instruction->ptr->value->type; - assert(ptr_type->id == ZigTypeIdPointer); - - ir_assert(ptr_type->data.pointer.vector_index != VECTOR_INDEX_RUNTIME, &instruction->base); - if (ptr_type->data.pointer.vector_index != VECTOR_INDEX_NONE) { - LLVMValueRef index_val = LLVMConstInt(LLVMInt32Type(), - ptr_type->data.pointer.vector_index, false); - LLVMValueRef loaded_vector = LLVMBuildLoad(g->builder, ptr, ""); - return LLVMBuildExtractElement(g->builder, loaded_vector, index_val, ""); - } - - uint32_t host_int_bytes = ptr_type->data.pointer.host_int_bytes; - if (host_int_bytes == 0) - return get_handle_value(g, ptr, child_type, ptr_type); - - bool big_endian = g->is_big_endian; - - LLVMTypeRef int_ptr_ty = LLVMPointerType(LLVMIntType(host_int_bytes * 8), 0); - LLVMValueRef int_ptr = LLVMBuildBitCast(g->builder, ptr, int_ptr_ty, ""); - LLVMValueRef containing_int = gen_load(g, int_ptr, ptr_type, ""); - - uint32_t host_bit_count = LLVMGetIntTypeWidth(LLVMTypeOf(containing_int)); - assert(host_bit_count == host_int_bytes * 8); - uint32_t size_in_bits = type_size_bits(g, child_type); - - uint32_t bit_offset = ptr_type->data.pointer.bit_offset_in_host; - uint32_t shift_amt = big_endian ? host_bit_count - bit_offset - size_in_bits : bit_offset; - - LLVMValueRef shift_amt_val = LLVMConstInt(LLVMTypeOf(containing_int), shift_amt, false); - LLVMValueRef shifted_value = LLVMBuildLShr(g->builder, containing_int, shift_amt_val, ""); - - if (handle_is_ptr(g, child_type)) { - LLVMValueRef result_loc = ir_llvm_value(g, instruction->result_loc); - LLVMTypeRef same_size_int = LLVMIntType(size_in_bits); - LLVMValueRef truncated_int = LLVMBuildTrunc(g->builder, shifted_value, same_size_int, ""); - LLVMValueRef bitcasted_ptr = LLVMBuildBitCast(g->builder, result_loc, - LLVMPointerType(same_size_int, 0), ""); - LLVMBuildStore(g->builder, truncated_int, bitcasted_ptr); - return result_loc; - } - - if (child_type->id == ZigTypeIdFloat) { - LLVMTypeRef same_size_int = LLVMIntType(size_in_bits); - LLVMValueRef truncated_int = LLVMBuildTrunc(g->builder, shifted_value, same_size_int, ""); - return LLVMBuildBitCast(g->builder, truncated_int, get_llvm_type(g, child_type), ""); - } - - return LLVMBuildTrunc(g->builder, shifted_value, get_llvm_type(g, child_type), ""); -} - -static bool value_is_all_undef_array(CodeGen *g, ZigValue *const_val, size_t len) { - switch (const_val->data.x_array.special) { - case ConstArraySpecialUndef: - return true; - case ConstArraySpecialBuf: - return false; - case ConstArraySpecialNone: - for (size_t i = 0; i < len; i += 1) { - if (!value_is_all_undef(g, &const_val->data.x_array.data.s_none.elements[i])) - return false; - } - return true; - } - zig_unreachable(); -} - -static bool value_is_all_undef(CodeGen *g, ZigValue *const_val) { - Error err; - if (const_val->special == ConstValSpecialLazy && - (err = ir_resolve_lazy(g, nullptr, const_val))) - codegen_report_errors_and_exit(g); - - switch (const_val->special) { - case ConstValSpecialLazy: - zig_unreachable(); - case ConstValSpecialRuntime: - return false; - case ConstValSpecialUndef: - return true; - case ConstValSpecialStatic: - if (const_val->type->id == ZigTypeIdStruct) { - for (size_t i = 0; i < const_val->type->data.structure.src_field_count; i += 1) { - if (!value_is_all_undef(g, const_val->data.x_struct.fields[i])) - return false; - } - return true; - } else if (const_val->type->id == ZigTypeIdArray) { - return value_is_all_undef_array(g, const_val, const_val->type->data.array.len); - } else if (const_val->type->id == ZigTypeIdVector) { - return value_is_all_undef_array(g, const_val, const_val->type->data.vector.len); - } else { - return false; - } - } - zig_unreachable(); -} - -static LLVMValueRef gen_valgrind_client_request(CodeGen *g, LLVMValueRef default_value, LLVMValueRef request, - LLVMValueRef a1, LLVMValueRef a2, LLVMValueRef a3, LLVMValueRef a4, LLVMValueRef a5) -{ - if (!target_has_valgrind_support(g->zig_target)) { - return default_value; - } - LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; - bool asm_has_side_effects = true; - bool asm_is_alignstack = false; - if (g->zig_target->arch == ZigLLVM_x86_64) { - if (g->zig_target->os == OsLinux || target_os_is_darwin(g->zig_target->os) || g->zig_target->os == OsSolaris || - (g->zig_target->os == OsWindows && g->zig_target->abi != ZigLLVM_MSVC)) - { - if (g->cur_fn->valgrind_client_request_array == nullptr) { - LLVMBasicBlockRef prev_block = LLVMGetInsertBlock(g->builder); - LLVMBasicBlockRef entry_block = LLVMGetEntryBasicBlock(g->cur_fn->llvm_value); - LLVMValueRef first_inst = LLVMGetFirstInstruction(entry_block); - LLVMPositionBuilderBefore(g->builder, first_inst); - LLVMTypeRef array_type_ref = LLVMArrayType(usize_type_ref, 6); - g->cur_fn->valgrind_client_request_array = LLVMBuildAlloca(g->builder, array_type_ref, ""); - LLVMPositionBuilderAtEnd(g->builder, prev_block); - } - LLVMValueRef array_ptr = g->cur_fn->valgrind_client_request_array; - LLVMValueRef array_elements[] = {request, a1, a2, a3, a4, a5}; - LLVMValueRef zero = LLVMConstInt(usize_type_ref, 0, false); - for (unsigned i = 0; i < 6; i += 1) { - LLVMValueRef indexes[] = { - zero, - LLVMConstInt(usize_type_ref, i, false), - }; - LLVMValueRef elem_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indexes, 2, ""); - LLVMBuildStore(g->builder, array_elements[i], elem_ptr); - } - - Buf *asm_template = buf_create_from_str( - "rolq $$3, %rdi ; rolq $$13, %rdi\n" - "rolq $$61, %rdi ; rolq $$51, %rdi\n" - "xchgq %rbx,%rbx\n" - ); - Buf *asm_constraints = buf_create_from_str( - "={rdx},{rax},0,~{cc},~{memory}" - ); - unsigned input_and_output_count = 2; - LLVMValueRef array_ptr_as_usize = LLVMBuildPtrToInt(g->builder, array_ptr, usize_type_ref, ""); - LLVMValueRef param_values[] = { array_ptr_as_usize, default_value }; - LLVMTypeRef param_types[] = {usize_type_ref, usize_type_ref}; - LLVMTypeRef function_type = LLVMFunctionType(usize_type_ref, param_types, - input_and_output_count, false); - LLVMValueRef asm_fn = LLVMGetInlineAsm(function_type, buf_ptr(asm_template), buf_len(asm_template), - buf_ptr(asm_constraints), buf_len(asm_constraints), asm_has_side_effects, asm_is_alignstack, - LLVMInlineAsmDialectATT); - return LLVMBuildCall(g->builder, asm_fn, param_values, input_and_output_count, ""); - } - } - zig_unreachable(); -} - -static bool want_valgrind_support(CodeGen *g) { - if (!target_has_valgrind_support(g->zig_target)) - return false; - switch (g->valgrind_support) { - case ValgrindSupportDisabled: - return false; - case ValgrindSupportEnabled: - return true; - case ValgrindSupportAuto: - return g->build_mode == BuildModeDebug; - } - zig_unreachable(); -} - -static void gen_valgrind_undef(CodeGen *g, LLVMValueRef dest_ptr, LLVMValueRef byte_count) { - static const uint32_t VG_USERREQ__MAKE_MEM_UNDEFINED = 1296236545; - ZigType *usize = g->builtin_types.entry_usize; - LLVMValueRef zero = LLVMConstInt(usize->llvm_type, 0, false); - LLVMValueRef req = LLVMConstInt(usize->llvm_type, VG_USERREQ__MAKE_MEM_UNDEFINED, false); - LLVMValueRef ptr_as_usize = LLVMBuildPtrToInt(g->builder, dest_ptr, usize->llvm_type, ""); - gen_valgrind_client_request(g, zero, req, ptr_as_usize, byte_count, zero, zero, zero); -} - -static void gen_undef_init(CodeGen *g, uint32_t ptr_align_bytes, ZigType *value_type, LLVMValueRef ptr) { - assert(type_has_bits(g, value_type)); - uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, get_llvm_type(g, value_type)); - assert(size_bytes > 0); - assert(ptr_align_bytes > 0); - // memset uninitialized memory to 0xaa - LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0); - LLVMValueRef fill_char = LLVMConstInt(LLVMInt8Type(), 0xaa, false); - LLVMValueRef dest_ptr = LLVMBuildBitCast(g->builder, ptr, ptr_u8, ""); - ZigType *usize = g->builtin_types.entry_usize; - LLVMValueRef byte_count = LLVMConstInt(usize->llvm_type, size_bytes, false); - ZigLLVMBuildMemSet(g->builder, dest_ptr, fill_char, byte_count, ptr_align_bytes, false); - // then tell valgrind that the memory is undefined even though we just memset it - if (want_valgrind_support(g)) { - gen_valgrind_undef(g, dest_ptr, byte_count); - } -} - -static LLVMValueRef ir_render_store_ptr(CodeGen *g, IrExecutableGen *executable, IrInstGenStorePtr *instruction) { - Error err; - - ZigType *ptr_type = instruction->ptr->value->type; - assert(ptr_type->id == ZigTypeIdPointer); - bool ptr_type_has_bits; - if ((err = type_has_bits2(g, ptr_type, &ptr_type_has_bits))) - codegen_report_errors_and_exit(g); - if (!ptr_type_has_bits) - return nullptr; - if (instruction->ptr->base.ref_count == 0) { - // In this case, this StorePtr instruction should be elided. Something happened like this: - // var t = true; - // const x = if (t) Num.Two else unreachable; - // The if condition is a runtime value, so the StorePtr for `x = Num.Two` got generated - // (this instruction being rendered) but because of `else unreachable` the result ended - // up being a comptime const value. - return nullptr; - } - - bool have_init_expr = !value_is_all_undef(g, instruction->value->value); - if (have_init_expr) { - LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr); - LLVMValueRef value = ir_llvm_value(g, instruction->value); - gen_assign_raw(g, ptr, ptr_type, value); - } else if (ir_want_runtime_safety(g, &instruction->base)) { - gen_undef_init(g, get_ptr_align(g, ptr_type), instruction->value->value->type, - ir_llvm_value(g, instruction->ptr)); - } - return nullptr; -} - -static LLVMValueRef ir_render_vector_store_elem(CodeGen *g, IrExecutableGen *executable, - IrInstGenVectorStoreElem *instruction) -{ - LLVMValueRef vector_ptr = ir_llvm_value(g, instruction->vector_ptr); - LLVMValueRef index = ir_llvm_value(g, instruction->index); - LLVMValueRef value = ir_llvm_value(g, instruction->value); - - LLVMValueRef loaded_vector = gen_load(g, vector_ptr, instruction->vector_ptr->value->type, ""); - LLVMValueRef modified_vector = LLVMBuildInsertElement(g->builder, loaded_vector, value, index, ""); - gen_store(g, modified_vector, vector_ptr, instruction->vector_ptr->value->type); - return nullptr; -} - -static LLVMValueRef ir_render_var_ptr(CodeGen *g, IrExecutableGen *executable, IrInstGenVarPtr *instruction) { - if (instruction->base.value->special != ConstValSpecialRuntime) - return ir_llvm_value(g, &instruction->base); - ZigVar *var = instruction->var; - if (type_has_bits(g, var->var_type)) { - assert(var->value_ref); - return var->value_ref; - } else { - return nullptr; - } -} - -static LLVMValueRef ir_render_return_ptr(CodeGen *g, IrExecutableGen *executable, - IrInstGenReturnPtr *instruction) -{ - if (!type_has_bits(g, instruction->base.value->type)) - return nullptr; - ir_assert(g->cur_ret_ptr != nullptr, &instruction->base); - return g->cur_ret_ptr; -} - -static LLVMValueRef ir_render_elem_ptr(CodeGen *g, IrExecutableGen *executable, IrInstGenElemPtr *instruction) { - LLVMValueRef array_ptr_ptr = ir_llvm_value(g, instruction->array_ptr); - ZigType *array_ptr_type = instruction->array_ptr->value->type; - assert(array_ptr_type->id == ZigTypeIdPointer); - ZigType *array_type = array_ptr_type->data.pointer.child_type; - LLVMValueRef subscript_value = ir_llvm_value(g, instruction->elem_index); - assert(subscript_value); - - if (!type_has_bits(g, array_type)) - return nullptr; - - bool safety_check_on = ir_want_runtime_safety(g, &instruction->base) && instruction->safety_check_on; - - if (array_type->id == ZigTypeIdArray || - (array_type->id == ZigTypeIdPointer && array_type->data.pointer.ptr_len == PtrLenSingle)) - { - LLVMValueRef array_ptr = get_handle_value(g, array_ptr_ptr, array_type, array_ptr_type); - if (array_type->id == ZigTypeIdPointer) { - assert(array_type->data.pointer.child_type->id == ZigTypeIdArray); - array_type = array_type->data.pointer.child_type; - } - - assert(array_type->data.array.len != 0 || array_type->data.array.sentinel != nullptr); - - if (safety_check_on) { - uint64_t extra_len_from_sentinel = (array_type->data.array.sentinel != nullptr) ? 1 : 0; - uint64_t full_len = array_type->data.array.len + extra_len_from_sentinel; - LLVMValueRef end = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, full_len, false); - add_bounds_check(g, subscript_value, LLVMIntEQ, nullptr, LLVMIntULT, end); - } - if (array_ptr_type->data.pointer.host_int_bytes != 0) { - return array_ptr_ptr; - } - ZigType *child_type = array_type->data.array.child_type; - if (child_type->id == ZigTypeIdStruct && - child_type->data.structure.layout == ContainerLayoutPacked) - { - ZigType *ptr_type = instruction->base.value->type; - size_t host_int_bytes = ptr_type->data.pointer.host_int_bytes; - if (host_int_bytes != 0) { - uint32_t size_in_bits = type_size_bits(g, ptr_type->data.pointer.child_type); - LLVMTypeRef ptr_u8_type_ref = LLVMPointerType(LLVMInt8Type(), 0); - LLVMValueRef u8_array_ptr = LLVMBuildBitCast(g->builder, array_ptr, ptr_u8_type_ref, ""); - assert(size_in_bits % 8 == 0); - LLVMValueRef elem_size_bytes = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, - size_in_bits / 8, false); - LLVMValueRef byte_offset = LLVMBuildNUWMul(g->builder, subscript_value, elem_size_bytes, ""); - LLVMValueRef indices[] = { - byte_offset - }; - LLVMValueRef elem_byte_ptr = LLVMBuildInBoundsGEP(g->builder, u8_array_ptr, indices, 1, ""); - return LLVMBuildBitCast(g->builder, elem_byte_ptr, LLVMPointerType(get_llvm_type(g, child_type), 0), ""); - } - } - LLVMValueRef indices[] = { - LLVMConstNull(g->builtin_types.entry_usize->llvm_type), - subscript_value - }; - return LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, ""); - } else if (array_type->id == ZigTypeIdPointer) { - LLVMValueRef array_ptr = get_handle_value(g, array_ptr_ptr, array_type, array_ptr_type); - assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind); - LLVMValueRef indices[] = { - subscript_value - }; - return LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 1, ""); - } else if (array_type->id == ZigTypeIdStruct) { - LLVMValueRef array_ptr = get_handle_value(g, array_ptr_ptr, array_type, array_ptr_type); - assert(array_type->data.structure.special == StructSpecialSlice); - - ZigType *ptr_type = array_type->data.structure.fields[slice_ptr_index]->type_entry; - if (!type_has_bits(g, ptr_type)) { - if (safety_check_on) { - assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMIntegerTypeKind); - add_bounds_check(g, subscript_value, LLVMIntEQ, nullptr, LLVMIntULT, array_ptr); - } - return nullptr; - } - - assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind); - assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind); - - if (safety_check_on) { - size_t len_index = array_type->data.structure.fields[slice_len_index]->gen_index; - assert(len_index != SIZE_MAX); - LLVMValueRef len_ptr = LLVMBuildStructGEP(g->builder, array_ptr, (unsigned)len_index, ""); - LLVMValueRef len = gen_load_untyped(g, len_ptr, 0, false, ""); - LLVMIntPredicate upper_op = (ptr_type->data.pointer.sentinel != nullptr) ? LLVMIntULE : LLVMIntULT; - add_bounds_check(g, subscript_value, LLVMIntEQ, nullptr, upper_op, len); - } - - size_t ptr_index = array_type->data.structure.fields[slice_ptr_index]->gen_index; - assert(ptr_index != SIZE_MAX); - LLVMValueRef ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, (unsigned)ptr_index, ""); - LLVMValueRef ptr = gen_load_untyped(g, ptr_ptr, 0, false, ""); - return LLVMBuildInBoundsGEP(g->builder, ptr, &subscript_value, 1, ""); - } else if (array_type->id == ZigTypeIdVector) { - return array_ptr_ptr; - } else { - zig_unreachable(); - } -} - -static LLVMValueRef get_new_stack_addr(CodeGen *g, LLVMValueRef new_stack) { - LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, new_stack, (unsigned)slice_ptr_index, ""); - LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, new_stack, (unsigned)slice_len_index, ""); - - LLVMValueRef ptr_value = gen_load_untyped(g, ptr_field_ptr, 0, false, ""); - LLVMValueRef len_value = gen_load_untyped(g, len_field_ptr, 0, false, ""); - - LLVMValueRef ptr_addr = LLVMBuildPtrToInt(g->builder, ptr_value, LLVMTypeOf(len_value), ""); - LLVMValueRef end_addr = LLVMBuildNUWAdd(g->builder, ptr_addr, len_value, ""); - const unsigned alignment_factor = ZigLLVMDataLayoutGetStackAlignment(g->target_data_ref); - LLVMValueRef align_amt = LLVMConstInt(LLVMTypeOf(end_addr), alignment_factor, false); - LLVMValueRef align_adj = LLVMBuildURem(g->builder, end_addr, align_amt, ""); - return LLVMBuildNUWSub(g->builder, end_addr, align_adj, ""); -} - -static void gen_set_stack_pointer(CodeGen *g, LLVMValueRef aligned_end_addr) { - LLVMValueRef write_register_fn_val = get_write_register_fn_val(g); - - if (g->sp_md_node == nullptr) { - Buf *sp_reg_name = buf_create_from_str(arch_stack_pointer_register_name(g->zig_target->arch)); - LLVMValueRef str_node = LLVMMDString(buf_ptr(sp_reg_name), buf_len(sp_reg_name) + 1); - g->sp_md_node = LLVMMDNode(&str_node, 1); - } - - LLVMValueRef params[] = { - g->sp_md_node, - aligned_end_addr, - }; - - LLVMBuildCall(g->builder, write_register_fn_val, params, 2, ""); -} - -static void set_call_instr_sret(CodeGen *g, LLVMValueRef call_instr) { - unsigned attr_kind_id = LLVMGetEnumAttributeKindForName("sret", 4); - LLVMAttributeRef sret_attr = LLVMCreateEnumAttribute(LLVMGetGlobalContext(), attr_kind_id, 0); - LLVMAddCallSiteAttribute(call_instr, 1, sret_attr); -} - -static void render_async_spills(CodeGen *g) { - ZigType *fn_type = g->cur_fn->type_entry; - ZigType *import = get_scope_import(&g->cur_fn->fndef_scope->base); - - CalcLLVMFieldIndex arg_calc = {0}; - frame_index_arg_calc(g, &arg_calc, fn_type->data.fn.fn_type_id.return_type); - for (size_t var_i = 0; var_i < g->cur_fn->variable_list.length; var_i += 1) { - ZigVar *var = g->cur_fn->variable_list.at(var_i); - - if (!type_has_bits(g, var->var_type)) { - continue; - } - if (ir_get_var_is_comptime(var)) - continue; - switch (type_requires_comptime(g, var->var_type)) { - case ReqCompTimeInvalid: - zig_unreachable(); - case ReqCompTimeYes: - continue; - case ReqCompTimeNo: - break; - } - if (var->src_arg_index == SIZE_MAX) { - continue; - } - - calc_llvm_field_index_add(g, &arg_calc, var->var_type); - var->value_ref = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr, arg_calc.field_index - 1, var->name); - if (var->decl_node) { - var->di_loc_var = ZigLLVMCreateAutoVariable(g->dbuilder, get_di_scope(g, var->parent_scope), - var->name, import->data.structure.root_struct->di_file, - (unsigned)(var->decl_node->line + 1), - get_llvm_di_type(g, var->var_type), !g->strip_debug_symbols, 0); - gen_var_debug_decl(g, var); - } - } - - ZigType *frame_type = g->cur_fn->frame_type->data.frame.locals_struct; - - for (size_t alloca_i = 0; alloca_i < g->cur_fn->alloca_gen_list.length; alloca_i += 1) { - IrInstGenAlloca *instruction = g->cur_fn->alloca_gen_list.at(alloca_i); - if (instruction->field_index == SIZE_MAX) - continue; - - size_t gen_index = frame_type->data.structure.fields[instruction->field_index]->gen_index; - instruction->base.llvm_value = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr, gen_index, - instruction->name_hint); - } -} - -static void render_async_var_decls(CodeGen *g, Scope *scope) { - for (;;) { - switch (scope->id) { - case ScopeIdCImport: - zig_unreachable(); - case ScopeIdFnDef: - return; - case ScopeIdVarDecl: { - ZigVar *var = reinterpret_cast(scope)->var; - if (var->did_the_decl_codegen) { - render_decl_var(g, var); - } - } - ZIG_FALLTHROUGH; - - case ScopeIdDecls: - case ScopeIdBlock: - case ScopeIdDefer: - case ScopeIdDeferExpr: - case ScopeIdLoop: - case ScopeIdSuspend: - case ScopeIdCompTime: - case ScopeIdNoSuspend: - case ScopeIdRuntime: - case ScopeIdTypeOf: - case ScopeIdExpr: - scope = scope->parent; - continue; - } - } -} - -static LLVMValueRef gen_frame_size(CodeGen *g, LLVMValueRef fn_val) { - assert(g->need_frame_size_prefix_data); - LLVMTypeRef usize_llvm_type = g->builtin_types.entry_usize->llvm_type; - LLVMTypeRef ptr_usize_llvm_type = LLVMPointerType(usize_llvm_type, 0); - LLVMValueRef casted_fn_val = LLVMBuildBitCast(g->builder, fn_val, ptr_usize_llvm_type, ""); - LLVMValueRef negative_one = LLVMConstInt(LLVMInt32Type(), -1, true); - LLVMValueRef prefix_ptr = LLVMBuildInBoundsGEP(g->builder, casted_fn_val, &negative_one, 1, ""); - return LLVMBuildLoad(g->builder, prefix_ptr, ""); -} - -static void gen_init_stack_trace(CodeGen *g, LLVMValueRef trace_field_ptr, LLVMValueRef addrs_field_ptr) { - LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; - LLVMValueRef zero = LLVMConstNull(usize_type_ref); - - LLVMValueRef index_ptr = LLVMBuildStructGEP(g->builder, trace_field_ptr, 0, ""); - LLVMBuildStore(g->builder, zero, index_ptr); - - LLVMValueRef addrs_slice_ptr = LLVMBuildStructGEP(g->builder, trace_field_ptr, 1, ""); - LLVMValueRef addrs_ptr_ptr = LLVMBuildStructGEP(g->builder, addrs_slice_ptr, slice_ptr_index, ""); - LLVMValueRef indices[] = { LLVMConstNull(usize_type_ref), LLVMConstNull(usize_type_ref) }; - LLVMValueRef trace_field_addrs_as_ptr = LLVMBuildInBoundsGEP(g->builder, addrs_field_ptr, indices, 2, ""); - LLVMBuildStore(g->builder, trace_field_addrs_as_ptr, addrs_ptr_ptr); - - LLVMValueRef addrs_len_ptr = LLVMBuildStructGEP(g->builder, addrs_slice_ptr, slice_len_index, ""); - LLVMBuildStore(g->builder, LLVMConstInt(usize_type_ref, stack_trace_ptr_count, false), addrs_len_ptr); -} - -static LLVMValueRef ir_render_call(CodeGen *g, IrExecutableGen *executable, IrInstGenCall *instruction) { - Error err; - - LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; - - LLVMValueRef fn_val; - ZigType *fn_type; - bool callee_is_async; - if (instruction->fn_entry) { - fn_val = fn_llvm_value(g, instruction->fn_entry); - fn_type = instruction->fn_entry->type_entry; - callee_is_async = fn_is_async(instruction->fn_entry); - } else { - assert(instruction->fn_ref); - fn_val = ir_llvm_value(g, instruction->fn_ref); - fn_type = instruction->fn_ref->value->type; - callee_is_async = fn_type->data.fn.fn_type_id.cc == CallingConventionAsync; - } - - FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; - - ZigType *src_return_type = fn_type_id->return_type; - bool ret_has_bits = type_has_bits(g, src_return_type); - - CallingConvention cc = fn_type->data.fn.fn_type_id.cc; - - bool first_arg_ret = ret_has_bits && want_first_arg_sret(g, fn_type_id); - bool prefix_arg_err_ret_stack = codegen_fn_has_err_ret_tracing_arg(g, fn_type_id->return_type); - bool is_var_args = fn_type_id->is_var_args; - ZigList gen_param_values = {}; - ZigList gen_param_types = {}; - LLVMValueRef result_loc = instruction->result_loc ? ir_llvm_value(g, instruction->result_loc) : nullptr; - LLVMValueRef zero = LLVMConstNull(usize_type_ref); - bool need_frame_ptr_ptr_spill = false; - ZigType *anyframe_type = nullptr; - LLVMValueRef frame_result_loc_uncasted = nullptr; - LLVMValueRef frame_result_loc; - LLVMValueRef awaiter_init_val; - LLVMValueRef ret_ptr; - if (callee_is_async) { - if (instruction->new_stack == nullptr) { - if (instruction->modifier == CallModifierAsync) { - frame_result_loc = result_loc; - } else { - ir_assert(instruction->frame_result_loc != nullptr, &instruction->base); - frame_result_loc_uncasted = ir_llvm_value(g, instruction->frame_result_loc); - ir_assert(instruction->fn_entry != nullptr, &instruction->base); - frame_result_loc = LLVMBuildBitCast(g->builder, frame_result_loc_uncasted, - LLVMPointerType(get_llvm_type(g, instruction->fn_entry->frame_type), 0), ""); - } - } else { - if (instruction->new_stack->value->type->id == ZigTypeIdPointer && - instruction->new_stack->value->type->data.pointer.child_type->id == ZigTypeIdFnFrame) - { - frame_result_loc = ir_llvm_value(g, instruction->new_stack); - } else { - LLVMValueRef frame_slice_ptr = ir_llvm_value(g, instruction->new_stack); - if (ir_want_runtime_safety(g, &instruction->base)) { - LLVMValueRef given_len_ptr = LLVMBuildStructGEP(g->builder, frame_slice_ptr, slice_len_index, ""); - LLVMValueRef given_frame_len = LLVMBuildLoad(g->builder, given_len_ptr, ""); - LLVMValueRef actual_frame_len = gen_frame_size(g, fn_val); - - LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "FrameSizeCheckFail"); - LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "FrameSizeCheckOk"); - - LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntUGE, given_frame_len, actual_frame_len, ""); - LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); - - LLVMPositionBuilderAtEnd(g->builder, fail_block); - gen_safety_crash(g, PanicMsgIdFrameTooSmall); - - LLVMPositionBuilderAtEnd(g->builder, ok_block); - } - need_frame_ptr_ptr_spill = true; - LLVMValueRef frame_ptr_ptr = LLVMBuildStructGEP(g->builder, frame_slice_ptr, slice_ptr_index, ""); - LLVMValueRef frame_ptr = LLVMBuildLoad(g->builder, frame_ptr_ptr, ""); - if (instruction->fn_entry == nullptr) { - anyframe_type = get_any_frame_type(g, src_return_type); - frame_result_loc = LLVMBuildBitCast(g->builder, frame_ptr, get_llvm_type(g, anyframe_type), ""); - } else { - ZigType *frame_type = get_fn_frame_type(g, instruction->fn_entry); - if ((err = type_resolve(g, frame_type, ResolveStatusLLVMFull))) - codegen_report_errors_and_exit(g); - ZigType *ptr_frame_type = get_pointer_to_type(g, frame_type, false); - frame_result_loc = LLVMBuildBitCast(g->builder, frame_ptr, - get_llvm_type(g, ptr_frame_type), ""); - } - } - } - if (instruction->modifier == CallModifierAsync) { - if (instruction->new_stack == nullptr) { - awaiter_init_val = zero; - - if (ret_has_bits) { - // Use the result location which is inside the frame if this is an async call. - ret_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, frame_ret_start + 2, ""); - } - } else { - awaiter_init_val = zero; - - if (ret_has_bits) { - if (result_loc != nullptr) { - // Use the result location provided to the @asyncCall builtin - ret_ptr = result_loc; - } else { - // no result location provided to @asyncCall - use the one inside the frame. - ret_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, frame_ret_start + 2, ""); - } - } - } - - // even if prefix_arg_err_ret_stack is true, let the async function do its own - // initialization. - } else { - if (instruction->modifier == CallModifierNoSuspend && !fn_is_async(g->cur_fn)) { - // Async function called as a normal function, and calling function is not async. - // This is allowed because it was called with `nosuspend` which asserts that it will - // never suspend. - awaiter_init_val = zero; - } else { - // async function called as a normal function - awaiter_init_val = LLVMBuildPtrToInt(g->builder, g->cur_frame_ptr, usize_type_ref, ""); // caller's own frame pointer - } - if (ret_has_bits) { - if (result_loc == nullptr) { - // return type is a scalar, but we still need a pointer to it. Use the async fn frame. - ret_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, frame_ret_start + 2, ""); - } else { - // Use the call instruction's result location. - ret_ptr = result_loc; - } - - // Store a zero in the awaiter's result ptr to indicate we do not need a copy made. - LLVMValueRef awaiter_ret_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, frame_ret_start + 1, ""); - LLVMValueRef zero_ptr = LLVMConstNull(LLVMGetElementType(LLVMTypeOf(awaiter_ret_ptr))); - LLVMBuildStore(g->builder, zero_ptr, awaiter_ret_ptr); - } - - if (prefix_arg_err_ret_stack) { - LLVMValueRef err_ret_trace_ptr_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, - frame_index_trace_arg(g, src_return_type) + 1, ""); - bool is_llvm_alloca; - LLVMValueRef my_err_ret_trace_val = get_cur_err_ret_trace_val(g, instruction->base.base.scope, - &is_llvm_alloca); - LLVMBuildStore(g->builder, my_err_ret_trace_val, err_ret_trace_ptr_ptr); - } - } - - assert(frame_result_loc != nullptr); - - LLVMValueRef fn_ptr_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, frame_fn_ptr_index, ""); - LLVMValueRef bitcasted_fn_val = LLVMBuildBitCast(g->builder, fn_val, - LLVMGetElementType(LLVMTypeOf(fn_ptr_ptr)), ""); - LLVMBuildStore(g->builder, bitcasted_fn_val, fn_ptr_ptr); - - LLVMValueRef resume_index_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, frame_resume_index, ""); - LLVMBuildStore(g->builder, zero, resume_index_ptr); - - LLVMValueRef awaiter_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, frame_awaiter_index, ""); - LLVMBuildStore(g->builder, awaiter_init_val, awaiter_ptr); - - if (ret_has_bits) { - LLVMValueRef ret_ptr_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, frame_ret_start, ""); - LLVMBuildStore(g->builder, ret_ptr, ret_ptr_ptr); - } - } else if (instruction->modifier == CallModifierAsync) { - // Async call of blocking function - if (instruction->new_stack != nullptr) { - zig_panic("TODO @asyncCall of non-async function"); - } - frame_result_loc = result_loc; - awaiter_init_val = LLVMConstAllOnes(usize_type_ref); - - LLVMValueRef awaiter_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, frame_awaiter_index, ""); - LLVMBuildStore(g->builder, awaiter_init_val, awaiter_ptr); - - if (ret_has_bits) { - ret_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, frame_ret_start + 2, ""); - LLVMValueRef ret_ptr_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, frame_ret_start, ""); - LLVMBuildStore(g->builder, ret_ptr, ret_ptr_ptr); - - if (first_arg_ret) { - gen_param_values.append(ret_ptr); - } - if (prefix_arg_err_ret_stack) { - // Set up the callee stack trace pointer pointing into the frame. - // Then we have to wire up the StackTrace pointers. - // Await is responsible for merging error return traces. - uint32_t trace_field_index_start = frame_index_trace_arg(g, src_return_type); - LLVMValueRef callee_trace_ptr_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, - trace_field_index_start, ""); - LLVMValueRef trace_field_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, - trace_field_index_start + 2, ""); - LLVMValueRef addrs_field_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, - trace_field_index_start + 3, ""); - - LLVMBuildStore(g->builder, trace_field_ptr, callee_trace_ptr_ptr); - - gen_init_stack_trace(g, trace_field_ptr, addrs_field_ptr); - - bool is_llvm_alloca; - gen_param_values.append(get_cur_err_ret_trace_val(g, instruction->base.base.scope, &is_llvm_alloca)); - } - } - } else { - if (first_arg_ret) { - gen_param_values.append(result_loc); - } - if (prefix_arg_err_ret_stack) { - bool is_llvm_alloca; - gen_param_values.append(get_cur_err_ret_trace_val(g, instruction->base.base.scope, &is_llvm_alloca)); - } - } - FnWalk fn_walk = {}; - fn_walk.id = FnWalkIdCall; - fn_walk.data.call.inst = instruction; - fn_walk.data.call.is_var_args = is_var_args; - fn_walk.data.call.gen_param_values = &gen_param_values; - fn_walk.data.call.gen_param_types = &gen_param_types; - walk_function_params(g, fn_type, &fn_walk); - - ZigLLVM_CallAttr call_attr; - switch (instruction->modifier) { - case CallModifierBuiltin: - case CallModifierCompileTime: - zig_unreachable(); - case CallModifierNone: - case CallModifierNoSuspend: - case CallModifierAsync: - call_attr = ZigLLVM_CallAttrAuto; - break; - case CallModifierNeverTail: - call_attr = ZigLLVM_CallAttrNeverTail; - break; - case CallModifierNeverInline: - call_attr = ZigLLVM_CallAttrNeverInline; - break; - case CallModifierAlwaysTail: - call_attr = ZigLLVM_CallAttrAlwaysTail; - break; - case CallModifierAlwaysInline: - ir_assert(instruction->fn_entry != nullptr, &instruction->base); - call_attr = ZigLLVM_CallAttrAlwaysInline; - break; - } - - ZigLLVM_CallingConv llvm_cc = get_llvm_cc(g, cc); - LLVMValueRef result; - - if (callee_is_async) { - CalcLLVMFieldIndex arg_calc_start = {0}; - frame_index_arg_calc(g, &arg_calc_start, fn_type->data.fn.fn_type_id.return_type); - - LLVMValueRef casted_frame; - if (instruction->new_stack != nullptr && instruction->fn_entry == nullptr) { - // We need the frame type to be a pointer to a struct that includes the args - - // Count ahead to determine how many llvm struct fields we need. - CalcLLVMFieldIndex arg_calc = arg_calc_start; - for (size_t i = 0; i < gen_param_types.length; i += 1) { - calc_llvm_field_index_add(g, &arg_calc, gen_param_types.at(i)); - } - size_t field_count = arg_calc.field_index; - - LLVMTypeRef *field_types = heap::c_allocator.allocate_nonzero(field_count); - LLVMGetStructElementTypes(LLVMGetElementType(LLVMTypeOf(frame_result_loc)), field_types); - assert(LLVMCountStructElementTypes(LLVMGetElementType(LLVMTypeOf(frame_result_loc))) == arg_calc_start.field_index); - - arg_calc = arg_calc_start; - for (size_t arg_i = 0; arg_i < gen_param_values.length; arg_i += 1) { - CalcLLVMFieldIndex prev = arg_calc; - calc_llvm_field_index_add(g, &arg_calc, gen_param_types.at(arg_i)); - field_types[arg_calc.field_index - 1] = LLVMTypeOf(gen_param_values.at(arg_i)); - if (arg_calc.field_index - prev.field_index > 1) { - // Padding field - uint32_t pad_bytes = arg_calc.offset - prev.offset - gen_param_types.at(arg_i)->abi_size; - LLVMTypeRef pad_llvm_type = LLVMArrayType(LLVMInt8Type(), pad_bytes); - field_types[arg_calc.field_index - 2] = pad_llvm_type; - } - } - LLVMTypeRef frame_with_args_type = LLVMStructType(field_types, field_count, false); - LLVMTypeRef ptr_frame_with_args_type = LLVMPointerType(frame_with_args_type, 0); - - casted_frame = LLVMBuildBitCast(g->builder, frame_result_loc, ptr_frame_with_args_type, ""); - } else { - casted_frame = frame_result_loc; - } - - CalcLLVMFieldIndex arg_calc = arg_calc_start; - for (size_t arg_i = 0; arg_i < gen_param_values.length; arg_i += 1) { - calc_llvm_field_index_add(g, &arg_calc, gen_param_types.at(arg_i)); - LLVMValueRef arg_ptr = LLVMBuildStructGEP(g->builder, casted_frame, arg_calc.field_index - 1, ""); - gen_assign_raw(g, arg_ptr, get_pointer_to_type(g, gen_param_types.at(arg_i), true), - gen_param_values.at(arg_i)); - } - - if (instruction->modifier == CallModifierAsync) { - gen_resume(g, fn_val, frame_result_loc, ResumeIdCall); - if (instruction->new_stack != nullptr) { - return LLVMBuildBitCast(g->builder, frame_result_loc, - get_llvm_type(g, instruction->base.value->type), ""); - } - return nullptr; - } else if (instruction->modifier == CallModifierNoSuspend && !fn_is_async(g->cur_fn)) { - gen_resume(g, fn_val, frame_result_loc, ResumeIdCall); - - if (ir_want_runtime_safety(g, &instruction->base)) { - LLVMValueRef awaiter_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, - frame_awaiter_index, ""); - LLVMValueRef all_ones = LLVMConstAllOnes(usize_type_ref); - LLVMValueRef prev_val = gen_maybe_atomic_op(g, LLVMAtomicRMWBinOpXchg, awaiter_ptr, - all_ones, LLVMAtomicOrderingRelease); - LLVMValueRef ok_val = LLVMBuildICmp(g->builder, LLVMIntEQ, prev_val, all_ones, ""); - - LLVMBasicBlockRef bad_block = LLVMAppendBasicBlock(g->cur_fn_val, "NoSuspendPanic"); - LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "NoSuspendOk"); - LLVMBuildCondBr(g->builder, ok_val, ok_block, bad_block); - - // The async function suspended, but this nosuspend call asserted it wouldn't. - LLVMPositionBuilderAtEnd(g->builder, bad_block); - gen_safety_crash(g, PanicMsgIdBadNoSuspendCall); - - LLVMPositionBuilderAtEnd(g->builder, ok_block); - } - - ZigType *result_type = instruction->base.value->type; - ZigType *ptr_result_type = get_pointer_to_type(g, result_type, true); - return gen_await_early_return(g, &instruction->base, frame_result_loc, - result_type, ptr_result_type, result_loc, true); - } else { - ZigType *ptr_result_type = get_pointer_to_type(g, src_return_type, true); - - LLVMBasicBlockRef call_bb = gen_suspend_begin(g, "CallResume"); - - LLVMValueRef call_inst = gen_resume(g, fn_val, frame_result_loc, ResumeIdCall); - set_tail_call_if_appropriate(g, call_inst); - LLVMBuildRetVoid(g->builder); - - LLVMPositionBuilderAtEnd(g->builder, call_bb); - gen_assert_resume_id(g, &instruction->base, ResumeIdReturn, PanicMsgIdResumedAnAwaitingFn, nullptr); - render_async_var_decls(g, instruction->base.base.scope); - - if (!type_has_bits(g, src_return_type)) - return nullptr; - - if (result_loc != nullptr) { - if (instruction->result_loc->id == IrInstGenIdReturnPtr) { - instruction->base.spill = nullptr; - return g->cur_ret_ptr; - } else { - return get_handle_value(g, result_loc, src_return_type, ptr_result_type); - } - } - - if (need_frame_ptr_ptr_spill) { - LLVMValueRef frame_slice_ptr = ir_llvm_value(g, instruction->new_stack); - LLVMValueRef frame_ptr_ptr = LLVMBuildStructGEP(g->builder, frame_slice_ptr, slice_ptr_index, ""); - frame_result_loc_uncasted = LLVMBuildLoad(g->builder, frame_ptr_ptr, ""); - } - if (frame_result_loc_uncasted != nullptr) { - if (instruction->fn_entry != nullptr) { - frame_result_loc = LLVMBuildBitCast(g->builder, frame_result_loc_uncasted, - LLVMPointerType(get_llvm_type(g, instruction->fn_entry->frame_type), 0), ""); - } else { - frame_result_loc = LLVMBuildBitCast(g->builder, frame_result_loc_uncasted, - get_llvm_type(g, anyframe_type), ""); - } - } - - LLVMValueRef result_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, frame_ret_start + 2, ""); - return LLVMBuildLoad(g->builder, result_ptr, ""); - } - } - - if (instruction->new_stack == nullptr || instruction->is_async_call_builtin) { - result = ZigLLVMBuildCall(g->builder, fn_val, - gen_param_values.items, (unsigned)gen_param_values.length, llvm_cc, call_attr, ""); - } else if (instruction->modifier == CallModifierAsync) { - zig_panic("TODO @asyncCall of non-async function"); - } else { - LLVMValueRef new_stack_addr = get_new_stack_addr(g, ir_llvm_value(g, instruction->new_stack)); - LLVMValueRef old_stack_ref; - if (src_return_type->id != ZigTypeIdUnreachable) { - LLVMValueRef stacksave_fn_val = get_stacksave_fn_val(g); - old_stack_ref = LLVMBuildCall(g->builder, stacksave_fn_val, nullptr, 0, ""); - } - gen_set_stack_pointer(g, new_stack_addr); - result = ZigLLVMBuildCall(g->builder, fn_val, - gen_param_values.items, (unsigned)gen_param_values.length, llvm_cc, call_attr, ""); - if (src_return_type->id != ZigTypeIdUnreachable) { - LLVMValueRef stackrestore_fn_val = get_stackrestore_fn_val(g); - LLVMBuildCall(g->builder, stackrestore_fn_val, &old_stack_ref, 1, ""); - } - } - - if (src_return_type->id == ZigTypeIdUnreachable) { - return LLVMBuildUnreachable(g->builder); - } else if (!ret_has_bits) { - return nullptr; - } else if (first_arg_ret) { - set_call_instr_sret(g, result); - return result_loc; - } else if (handle_is_ptr(g, src_return_type)) { - LLVMValueRef store_instr = LLVMBuildStore(g->builder, result, result_loc); - LLVMSetAlignment(store_instr, get_ptr_align(g, instruction->result_loc->value->type)); - return result_loc; - } else if (!callee_is_async && instruction->modifier == CallModifierAsync) { - LLVMBuildStore(g->builder, result, ret_ptr); - return result_loc; - } else { - return result; - } -} - -static LLVMValueRef ir_render_struct_field_ptr(CodeGen *g, IrExecutableGen *executable, - IrInstGenStructFieldPtr *instruction) -{ - Error err; - - if (instruction->base.value->special != ConstValSpecialRuntime) - return nullptr; - - LLVMValueRef struct_ptr = ir_llvm_value(g, instruction->struct_ptr); - // not necessarily a pointer. could be ZigTypeIdStruct - ZigType *struct_ptr_type = instruction->struct_ptr->value->type; - TypeStructField *field = instruction->field; - - if (!type_has_bits(g, field->type_entry)) - return nullptr; - - if (struct_ptr_type->id == ZigTypeIdPointer && - struct_ptr_type->data.pointer.host_int_bytes != 0) - { - return struct_ptr; - } - - ZigType *struct_type; - if (struct_ptr_type->id == ZigTypeIdPointer) { - if (struct_ptr_type->data.pointer.inferred_struct_field != nullptr) { - struct_type = struct_ptr_type->data.pointer.inferred_struct_field->inferred_struct_type; - } else { - struct_type = struct_ptr_type->data.pointer.child_type; - } - } else { - struct_type = struct_ptr_type; - } - - if ((err = type_resolve(g, struct_type, ResolveStatusLLVMFull))) - codegen_report_errors_and_exit(g); - - ir_assert(field->gen_index != SIZE_MAX, &instruction->base); - LLVMValueRef field_ptr_val = LLVMBuildStructGEP(g->builder, struct_ptr, (unsigned)field->gen_index, ""); - ZigType *res_type = instruction->base.value->type; - ir_assert(res_type->id == ZigTypeIdPointer, &instruction->base); - if (res_type->data.pointer.host_int_bytes != 0) { - // We generate packed structs with get_llvm_type_of_n_bytes, which is - // u8 for 1 byte or [n]u8 for multiple bytes. But the pointer to the type - // is supposed to be a pointer to the integer. So we bitcast it here. - LLVMTypeRef int_elem_type = LLVMIntType(8*res_type->data.pointer.host_int_bytes); - LLVMTypeRef integer_ptr_type = LLVMPointerType(int_elem_type, 0); - return LLVMBuildBitCast(g->builder, field_ptr_val, integer_ptr_type, ""); - } - return field_ptr_val; -} - -static LLVMValueRef ir_render_union_field_ptr(CodeGen *g, IrExecutableGen *executable, - IrInstGenUnionFieldPtr *instruction) -{ - if (instruction->base.value->special != ConstValSpecialRuntime) - return nullptr; - - ZigType *union_ptr_type = instruction->union_ptr->value->type; - assert(union_ptr_type->id == ZigTypeIdPointer); - ZigType *union_type = union_ptr_type->data.pointer.child_type; - assert(union_type->id == ZigTypeIdUnion); - - TypeUnionField *field = instruction->field; - - if (!type_has_bits(g, field->type_entry)) { - ZigType *tag_type = union_type->data.unionation.tag_type; - if (!instruction->initializing || tag_type == nullptr || !type_has_bits(g, tag_type)) - return nullptr; - - // The field has no bits but we still have to change the discriminant - // value here - LLVMValueRef union_ptr = ir_llvm_value(g, instruction->union_ptr); - - LLVMTypeRef tag_type_ref = get_llvm_type(g, tag_type); - LLVMValueRef tag_field_ptr = nullptr; - if (union_type->data.unionation.gen_field_count == 0) { - assert(union_type->data.unionation.gen_tag_index == SIZE_MAX); - // The whole union is collapsed into the discriminant - tag_field_ptr = LLVMBuildBitCast(g->builder, union_ptr, - LLVMPointerType(tag_type_ref, 0), ""); - } else { - assert(union_type->data.unionation.gen_tag_index != SIZE_MAX); - tag_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, - union_type->data.unionation.gen_tag_index, ""); - } - - LLVMValueRef tag_value = bigint_to_llvm_const(tag_type_ref, - &field->enum_field->value); - assert(tag_field_ptr != nullptr); - gen_store_untyped(g, tag_value, tag_field_ptr, 0, false); - - return nullptr; - } - - LLVMValueRef union_ptr = ir_llvm_value(g, instruction->union_ptr); - LLVMTypeRef field_type_ref = LLVMPointerType(get_llvm_type(g, field->type_entry), 0); - - if (union_type->data.unionation.gen_tag_index == SIZE_MAX) { - LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, 0, ""); - LLVMValueRef bitcasted_union_field_ptr = LLVMBuildBitCast(g->builder, union_field_ptr, field_type_ref, ""); - return bitcasted_union_field_ptr; - } - - if (instruction->initializing) { - LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, union_type->data.unionation.gen_tag_index, ""); - LLVMValueRef tag_value = bigint_to_llvm_const(get_llvm_type(g, union_type->data.unionation.tag_type), - &field->enum_field->value); - gen_store_untyped(g, tag_value, tag_field_ptr, 0, false); - } else if (instruction->safety_check_on && ir_want_runtime_safety(g, &instruction->base)) { - LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, union_type->data.unionation.gen_tag_index, ""); - LLVMValueRef tag_value = gen_load_untyped(g, tag_field_ptr, 0, false, ""); - - - LLVMValueRef expected_tag_value = bigint_to_llvm_const(get_llvm_type(g, union_type->data.unionation.tag_type), - &field->enum_field->value); - LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnionCheckOk"); - LLVMBasicBlockRef bad_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnionCheckFail"); - LLVMValueRef ok_val = LLVMBuildICmp(g->builder, LLVMIntEQ, tag_value, expected_tag_value, ""); - LLVMBuildCondBr(g->builder, ok_val, ok_block, bad_block); - - LLVMPositionBuilderAtEnd(g->builder, bad_block); - gen_safety_crash(g, PanicMsgIdBadUnionField); - - LLVMPositionBuilderAtEnd(g->builder, ok_block); - } - - LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, - union_type->data.unionation.gen_union_index, ""); - LLVMValueRef bitcasted_union_field_ptr = LLVMBuildBitCast(g->builder, union_field_ptr, field_type_ref, ""); - return bitcasted_union_field_ptr; -} - -static size_t find_asm_index(CodeGen *g, AstNode *node, AsmToken *tok, Buf *src_template) { - const char *ptr = buf_ptr(src_template) + tok->start + 2; - size_t len = tok->end - tok->start - 2; - size_t result = 0; - for (size_t i = 0; i < node->data.asm_expr.output_list.length; i += 1, result += 1) { - AsmOutput *asm_output = node->data.asm_expr.output_list.at(i); - if (buf_eql_mem(asm_output->asm_symbolic_name, ptr, len)) { - return result; - } - } - for (size_t i = 0; i < node->data.asm_expr.input_list.length; i += 1, result += 1) { - AsmInput *asm_input = node->data.asm_expr.input_list.at(i); - if (buf_eql_mem(asm_input->asm_symbolic_name, ptr, len)) { - return result; - } - } - return SIZE_MAX; -} - -static LLVMValueRef ir_render_asm_gen(CodeGen *g, IrExecutableGen *executable, IrInstGenAsm *instruction) { - AstNode *asm_node = instruction->base.base.source_node; - assert(asm_node->type == NodeTypeAsmExpr); - AstNodeAsmExpr *asm_expr = &asm_node->data.asm_expr; - - Buf *src_template = instruction->asm_template; - - Buf llvm_template = BUF_INIT; - buf_resize(&llvm_template, 0); - - for (size_t token_i = 0; token_i < instruction->token_list_len; token_i += 1) { - AsmToken *asm_token = &instruction->token_list[token_i]; - switch (asm_token->id) { - case AsmTokenIdTemplate: - for (size_t offset = asm_token->start; offset < asm_token->end; offset += 1) { - uint8_t c = *((uint8_t*)(buf_ptr(src_template) + offset)); - if (c == '$') { - buf_append_str(&llvm_template, "$$"); - } else { - buf_append_char(&llvm_template, c); - } - } - break; - case AsmTokenIdPercent: - buf_append_char(&llvm_template, '%'); - break; - case AsmTokenIdVar: - { - size_t index = find_asm_index(g, asm_node, asm_token, src_template); - assert(index < SIZE_MAX); - buf_appendf(&llvm_template, "$%" ZIG_PRI_usize "", index); - break; - } - case AsmTokenIdUniqueId: - buf_append_str(&llvm_template, "${:uid}"); - break; - } - } - - Buf constraint_buf = BUF_INIT; - buf_resize(&constraint_buf, 0); - - assert(instruction->return_count == 0 || instruction->return_count == 1); - - size_t total_constraint_count = asm_expr->output_list.length + - asm_expr->input_list.length + - asm_expr->clobber_list.length; - size_t input_and_output_count = asm_expr->output_list.length + - asm_expr->input_list.length - - instruction->return_count; - size_t total_index = 0; - size_t param_index = 0; - LLVMTypeRef *param_types = heap::c_allocator.allocate(input_and_output_count); - LLVMValueRef *param_values = heap::c_allocator.allocate(input_and_output_count); - for (size_t i = 0; i < asm_expr->output_list.length; i += 1, total_index += 1) { - AsmOutput *asm_output = asm_expr->output_list.at(i); - bool is_return = (asm_output->return_type != nullptr); - assert(*buf_ptr(asm_output->constraint) == '='); - // LLVM uses commas internally to separate different constraints, - // alternative constraints are achieved with pipes. - // We still allow the user to use commas in a way that is similar - // to GCC's inline assembly. - // http://llvm.org/docs/LangRef.html#constraint-codes - buf_replace(asm_output->constraint, ',', '|'); - - if (is_return) { - buf_appendf(&constraint_buf, "=%s", buf_ptr(asm_output->constraint) + 1); - } else { - buf_appendf(&constraint_buf, "=*%s", buf_ptr(asm_output->constraint) + 1); - } - if (total_index + 1 < total_constraint_count) { - buf_append_char(&constraint_buf, ','); - } - - if (!is_return) { - ZigVar *variable = instruction->output_vars[i]; - assert(variable); - param_types[param_index] = LLVMTypeOf(variable->value_ref); - param_values[param_index] = variable->value_ref; - param_index += 1; - } - } - for (size_t i = 0; i < asm_expr->input_list.length; i += 1, total_index += 1, param_index += 1) { - AsmInput *asm_input = asm_expr->input_list.at(i); - buf_replace(asm_input->constraint, ',', '|'); - IrInstGen *ir_input = instruction->input_list[i]; - buf_append_buf(&constraint_buf, asm_input->constraint); - if (total_index + 1 < total_constraint_count) { - buf_append_char(&constraint_buf, ','); - } - - ZigType *const type = ir_input->value->type; - LLVMTypeRef type_ref = get_llvm_type(g, type); - LLVMValueRef value_ref = ir_llvm_value(g, ir_input); - // Handle integers of non pot bitsize by widening them. - if (type->id == ZigTypeIdInt) { - const size_t bitsize = type->data.integral.bit_count; - if (bitsize < 8 || !is_power_of_2(bitsize)) { - const bool is_signed = type->data.integral.is_signed; - const size_t wider_bitsize = bitsize < 8 ? 8 : round_to_next_power_of_2(bitsize); - ZigType *const wider_type = get_int_type(g, is_signed, wider_bitsize); - type_ref = get_llvm_type(g, wider_type); - value_ref = gen_widen_or_shorten(g, false, type, wider_type, value_ref); - } - } - - param_types[param_index] = type_ref; - param_values[param_index] = value_ref; - } - for (size_t i = 0; i < asm_expr->clobber_list.length; i += 1, total_index += 1) { - Buf *clobber_buf = asm_expr->clobber_list.at(i); - buf_appendf(&constraint_buf, "~{%s}", buf_ptr(clobber_buf)); - if (total_index + 1 < total_constraint_count) { - buf_append_char(&constraint_buf, ','); - } - } - - LLVMTypeRef ret_type; - if (instruction->return_count == 0) { - ret_type = LLVMVoidType(); - } else { - ret_type = get_llvm_type(g, instruction->base.value->type); - } - LLVMTypeRef function_type = LLVMFunctionType(ret_type, param_types, (unsigned)input_and_output_count, false); - - bool is_volatile = instruction->has_side_effects || (asm_expr->output_list.length == 0); - LLVMValueRef asm_fn = LLVMGetInlineAsm(function_type, buf_ptr(&llvm_template), buf_len(&llvm_template), - buf_ptr(&constraint_buf), buf_len(&constraint_buf), is_volatile, false, LLVMInlineAsmDialectATT); - - return LLVMBuildCall(g->builder, asm_fn, param_values, (unsigned)input_and_output_count, ""); -} - -static LLVMValueRef gen_non_null_bit(CodeGen *g, ZigType *maybe_type, LLVMValueRef maybe_handle) { - assert(maybe_type->id == ZigTypeIdOptional || - (maybe_type->id == ZigTypeIdPointer && maybe_type->data.pointer.allow_zero)); - - ZigType *child_type = maybe_type->data.maybe.child_type; - if (!type_has_bits(g, child_type)) - return maybe_handle; - - bool is_scalar = !handle_is_ptr(g, maybe_type); - if (is_scalar) - return LLVMBuildICmp(g->builder, LLVMIntNE, maybe_handle, LLVMConstNull(get_llvm_type(g, maybe_type)), ""); - - LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_handle, maybe_null_index, ""); - return gen_load_untyped(g, maybe_field_ptr, 0, false, ""); -} - -static LLVMValueRef ir_render_test_non_null(CodeGen *g, IrExecutableGen *executable, - IrInstGenTestNonNull *instruction) -{ - return gen_non_null_bit(g, instruction->value->value->type, ir_llvm_value(g, instruction->value)); -} - -static LLVMValueRef ir_render_optional_unwrap_ptr(CodeGen *g, IrExecutableGen *executable, - IrInstGenOptionalUnwrapPtr *instruction) -{ - if (instruction->base.value->special != ConstValSpecialRuntime) - return nullptr; - - ZigType *ptr_type = instruction->base_ptr->value->type; - assert(ptr_type->id == ZigTypeIdPointer); - ZigType *maybe_type = ptr_type->data.pointer.child_type; - assert(maybe_type->id == ZigTypeIdOptional); - ZigType *child_type = maybe_type->data.maybe.child_type; - LLVMValueRef base_ptr = ir_llvm_value(g, instruction->base_ptr); - if (instruction->safety_check_on && ir_want_runtime_safety(g, &instruction->base)) { - LLVMValueRef maybe_handle = get_handle_value(g, base_ptr, maybe_type, ptr_type); - LLVMValueRef non_null_bit = gen_non_null_bit(g, maybe_type, maybe_handle); - LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapOptionalFail"); - LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapOptionalOk"); - LLVMBuildCondBr(g->builder, non_null_bit, ok_block, fail_block); - - LLVMPositionBuilderAtEnd(g->builder, fail_block); - gen_safety_crash(g, PanicMsgIdUnwrapOptionalFail); - - LLVMPositionBuilderAtEnd(g->builder, ok_block); - } - if (!type_has_bits(g, child_type)) { - if (instruction->initializing) { - LLVMValueRef non_null_bit = LLVMConstInt(LLVMInt1Type(), 1, false); - gen_store_untyped(g, non_null_bit, base_ptr, 0, false); - } - return nullptr; - } else { - bool is_scalar = !handle_is_ptr(g, maybe_type); - if (is_scalar) { - return base_ptr; - } else { - LLVMValueRef optional_struct_ref = get_handle_value(g, base_ptr, maybe_type, ptr_type); - if (instruction->initializing) { - LLVMValueRef non_null_bit_ptr = LLVMBuildStructGEP(g->builder, optional_struct_ref, - maybe_null_index, ""); - LLVMValueRef non_null_bit = LLVMConstInt(LLVMInt1Type(), 1, false); - gen_store_untyped(g, non_null_bit, non_null_bit_ptr, 0, false); - } - return LLVMBuildStructGEP(g->builder, optional_struct_ref, maybe_child_index, ""); - } - } -} - -static LLVMValueRef get_int_builtin_fn(CodeGen *g, ZigType *expr_type, BuiltinFnId fn_id) { - bool is_vector = expr_type->id == ZigTypeIdVector; - ZigType *int_type = is_vector ? expr_type->data.vector.elem_type : expr_type; - assert(int_type->id == ZigTypeIdInt); - uint32_t vector_len = is_vector ? expr_type->data.vector.len : 0; - ZigLLVMFnKey key = {}; - const char *fn_name; - uint32_t n_args; - if (fn_id == BuiltinFnIdCtz) { - fn_name = "cttz"; - n_args = 2; - key.id = ZigLLVMFnIdCtz; - key.data.ctz.bit_count = (uint32_t)int_type->data.integral.bit_count; - } else if (fn_id == BuiltinFnIdClz) { - fn_name = "ctlz"; - n_args = 2; - key.id = ZigLLVMFnIdClz; - key.data.clz.bit_count = (uint32_t)int_type->data.integral.bit_count; - } else if (fn_id == BuiltinFnIdPopCount) { - fn_name = "ctpop"; - n_args = 1; - key.id = ZigLLVMFnIdPopCount; - key.data.pop_count.bit_count = (uint32_t)int_type->data.integral.bit_count; - } else if (fn_id == BuiltinFnIdBswap) { - fn_name = "bswap"; - n_args = 1; - key.id = ZigLLVMFnIdBswap; - key.data.bswap.bit_count = (uint32_t)int_type->data.integral.bit_count; - key.data.bswap.vector_len = vector_len; - } else if (fn_id == BuiltinFnIdBitReverse) { - fn_name = "bitreverse"; - n_args = 1; - key.id = ZigLLVMFnIdBitReverse; - key.data.bit_reverse.bit_count = (uint32_t)int_type->data.integral.bit_count; - } else { - zig_unreachable(); - } - - auto existing_entry = g->llvm_fn_table.maybe_get(key); - if (existing_entry) - return existing_entry->value; - - char llvm_name[64]; - if (is_vector) - sprintf(llvm_name, "llvm.%s.v%" PRIu32 "i%" PRIu32, fn_name, vector_len, int_type->data.integral.bit_count); - else - sprintf(llvm_name, "llvm.%s.i%" PRIu32, fn_name, int_type->data.integral.bit_count); - LLVMTypeRef param_types[] = { - get_llvm_type(g, expr_type), - LLVMInt1Type(), - }; - LLVMTypeRef fn_type = LLVMFunctionType(get_llvm_type(g, expr_type), param_types, n_args, false); - LLVMValueRef fn_val = LLVMAddFunction(g->module, llvm_name, fn_type); - assert(LLVMGetIntrinsicID(fn_val)); - - g->llvm_fn_table.put(key, fn_val); - - return fn_val; -} - -static LLVMValueRef ir_render_clz(CodeGen *g, IrExecutableGen *executable, IrInstGenClz *instruction) { - ZigType *int_type = instruction->op->value->type; - LLVMValueRef fn_val = get_int_builtin_fn(g, int_type, BuiltinFnIdClz); - LLVMValueRef operand = ir_llvm_value(g, instruction->op); - LLVMValueRef params[] { - operand, - LLVMConstNull(LLVMInt1Type()), - }; - LLVMValueRef wrong_size_int = LLVMBuildCall(g->builder, fn_val, params, 2, ""); - return gen_widen_or_shorten(g, false, int_type, instruction->base.value->type, wrong_size_int); -} - -static LLVMValueRef ir_render_ctz(CodeGen *g, IrExecutableGen *executable, IrInstGenCtz *instruction) { - ZigType *int_type = instruction->op->value->type; - LLVMValueRef fn_val = get_int_builtin_fn(g, int_type, BuiltinFnIdCtz); - LLVMValueRef operand = ir_llvm_value(g, instruction->op); - LLVMValueRef params[] { - operand, - LLVMConstNull(LLVMInt1Type()), - }; - LLVMValueRef wrong_size_int = LLVMBuildCall(g->builder, fn_val, params, 2, ""); - return gen_widen_or_shorten(g, false, int_type, instruction->base.value->type, wrong_size_int); -} - -static LLVMValueRef ir_render_shuffle_vector(CodeGen *g, IrExecutableGen *executable, IrInstGenShuffleVector *instruction) { - uint64_t len_a = instruction->a->value->type->data.vector.len; - uint64_t len_mask = instruction->mask->value->type->data.vector.len; - - // LLVM uses integers larger than the length of the first array to - // index into the second array. This was deemed unnecessarily fragile - // when changing code, so Zig uses negative numbers to index the - // second vector. These start at -1 and go down, and are easiest to use - // with the ~ operator. Here we convert between the two formats. - IrInstGen *mask = instruction->mask; - LLVMValueRef *values = heap::c_allocator.allocate(len_mask); - for (uint64_t i = 0; i < len_mask; i++) { - if (mask->value->data.x_array.data.s_none.elements[i].special == ConstValSpecialUndef) { - values[i] = LLVMGetUndef(LLVMInt32Type()); - } else { - int32_t v = bigint_as_signed(&mask->value->data.x_array.data.s_none.elements[i].data.x_bigint); - uint32_t index_val = (v >= 0) ? (uint32_t)v : (uint32_t)~v + (uint32_t)len_a; - values[i] = LLVMConstInt(LLVMInt32Type(), index_val, false); - } - } - - LLVMValueRef llvm_mask_value = LLVMConstVector(values, len_mask); - heap::c_allocator.deallocate(values, len_mask); - - return LLVMBuildShuffleVector(g->builder, - ir_llvm_value(g, instruction->a), - ir_llvm_value(g, instruction->b), - llvm_mask_value, ""); -} - -static LLVMValueRef ir_render_splat(CodeGen *g, IrExecutableGen *executable, IrInstGenSplat *instruction) { - ZigType *result_type = instruction->base.value->type; - ir_assert(result_type->id == ZigTypeIdVector, &instruction->base); - uint32_t len = result_type->data.vector.len; - LLVMTypeRef op_llvm_type = LLVMVectorType(get_llvm_type(g, instruction->scalar->value->type), 1); - LLVMTypeRef mask_llvm_type = LLVMVectorType(LLVMInt32Type(), len); - LLVMValueRef undef_vector = LLVMGetUndef(op_llvm_type); - LLVMValueRef op_vector = LLVMBuildInsertElement(g->builder, undef_vector, - ir_llvm_value(g, instruction->scalar), LLVMConstInt(LLVMInt32Type(), 0, false), ""); - return LLVMBuildShuffleVector(g->builder, op_vector, undef_vector, LLVMConstNull(mask_llvm_type), ""); -} - -static LLVMValueRef ir_render_pop_count(CodeGen *g, IrExecutableGen *executable, IrInstGenPopCount *instruction) { - ZigType *int_type = instruction->op->value->type; - LLVMValueRef fn_val = get_int_builtin_fn(g, int_type, BuiltinFnIdPopCount); - LLVMValueRef operand = ir_llvm_value(g, instruction->op); - LLVMValueRef wrong_size_int = LLVMBuildCall(g->builder, fn_val, &operand, 1, ""); - return gen_widen_or_shorten(g, false, int_type, instruction->base.value->type, wrong_size_int); -} - -static LLVMValueRef ir_render_switch_br(CodeGen *g, IrExecutableGen *executable, IrInstGenSwitchBr *instruction) { - ZigType *target_type = instruction->target_value->value->type; - LLVMBasicBlockRef else_block = instruction->else_block->llvm_block; - - LLVMValueRef target_value = ir_llvm_value(g, instruction->target_value); - if (target_type->id == ZigTypeIdPointer) { - const ZigType *usize = g->builtin_types.entry_usize; - target_value = LLVMBuildPtrToInt(g->builder, target_value, usize->llvm_type, ""); - } - - LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, target_value, else_block, - (unsigned)instruction->case_count); - - for (size_t i = 0; i < instruction->case_count; i += 1) { - IrInstGenSwitchBrCase *this_case = &instruction->cases[i]; - - LLVMValueRef case_value = ir_llvm_value(g, this_case->value); - if (target_type->id == ZigTypeIdPointer) { - const ZigType *usize = g->builtin_types.entry_usize; - case_value = LLVMBuildPtrToInt(g->builder, case_value, usize->llvm_type, ""); - } - - LLVMAddCase(switch_instr, case_value, this_case->block->llvm_block); - } - - return nullptr; -} - -static LLVMValueRef ir_render_phi(CodeGen *g, IrExecutableGen *executable, IrInstGenPhi *instruction) { - if (!type_has_bits(g, instruction->base.value->type)) - return nullptr; - - LLVMTypeRef phi_type; - if (handle_is_ptr(g, instruction->base.value->type)) { - phi_type = LLVMPointerType(get_llvm_type(g,instruction->base.value->type), 0); - } else { - phi_type = get_llvm_type(g, instruction->base.value->type); - } - - LLVMValueRef phi = LLVMBuildPhi(g->builder, phi_type, ""); - LLVMValueRef *incoming_values = heap::c_allocator.allocate(instruction->incoming_count); - LLVMBasicBlockRef *incoming_blocks = heap::c_allocator.allocate(instruction->incoming_count); - for (size_t i = 0; i < instruction->incoming_count; i += 1) { - incoming_values[i] = ir_llvm_value(g, instruction->incoming_values[i]); - incoming_blocks[i] = instruction->incoming_blocks[i]->llvm_exit_block; - } - LLVMAddIncoming(phi, incoming_values, incoming_blocks, (unsigned)instruction->incoming_count); - return phi; -} - -static LLVMValueRef ir_render_ref(CodeGen *g, IrExecutableGen *executable, IrInstGenRef *instruction) { - if (!type_has_bits(g, instruction->base.value->type)) { - return nullptr; - } - if (instruction->operand->id == IrInstGenIdCall) { - IrInstGenCall *call = reinterpret_cast(instruction->operand); - if (call->result_loc != nullptr) { - return ir_llvm_value(g, call->result_loc); - } - } - LLVMValueRef value = ir_llvm_value(g, instruction->operand); - if (handle_is_ptr(g, instruction->operand->value->type)) { - return value; - } else { - LLVMValueRef result_loc = ir_llvm_value(g, instruction->result_loc); - gen_store_untyped(g, value, result_loc, 0, false); - return result_loc; - } -} - -static LLVMValueRef ir_render_err_name(CodeGen *g, IrExecutableGen *executable, IrInstGenErrName *instruction) { - assert(g->generate_error_name_table); - - if (g->errors_by_index.length == 1) { - LLVMBuildUnreachable(g->builder); - return nullptr; - } - - LLVMValueRef err_val = ir_llvm_value(g, instruction->value); - if (ir_want_runtime_safety(g, &instruction->base)) { - LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(err_val)); - LLVMValueRef end_val = LLVMConstInt(LLVMTypeOf(err_val), g->errors_by_index.length, false); - add_bounds_check(g, err_val, LLVMIntNE, zero, LLVMIntULT, end_val); - } - - LLVMValueRef indices[] = { - LLVMConstNull(g->builtin_types.entry_usize->llvm_type), - err_val, - }; - return LLVMBuildInBoundsGEP(g->builder, g->err_name_table, indices, 2, ""); -} - -static LLVMValueRef get_enum_tag_name_function(CodeGen *g, ZigType *enum_type) { - assert(enum_type->id == ZigTypeIdEnum); - if (enum_type->data.enumeration.name_function) - return enum_type->data.enumeration.name_function; - - ZigType *u8_ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, false, false, - PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0, false); - ZigType *u8_slice_type = get_slice_type(g, u8_ptr_type); - ZigType *tag_int_type = enum_type->data.enumeration.tag_int_type; - - LLVMTypeRef tag_int_llvm_type = get_llvm_type(g, tag_int_type); - LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMPointerType(get_llvm_type(g, u8_slice_type), 0), - &tag_int_llvm_type, 1, false); - - const char *fn_name = get_mangled_name(g, - buf_ptr(buf_sprintf("__zig_tag_name_%s", buf_ptr(&enum_type->name)))); - LLVMValueRef fn_val = LLVMAddFunction(g->module, fn_name, fn_type_ref); - LLVMSetLinkage(fn_val, LLVMInternalLinkage); - ZigLLVMFunctionSetCallingConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified)); - addLLVMFnAttr(fn_val, "nounwind"); - add_uwtable_attr(g, fn_val); - if (codegen_have_frame_pointer(g)) { - ZigLLVMAddFunctionAttr(fn_val, "frame-pointer", "all"); - } - - LLVMBasicBlockRef prev_block = LLVMGetInsertBlock(g->builder); - LLVMValueRef prev_debug_location = LLVMGetCurrentDebugLocation(g->builder); - ZigFn *prev_cur_fn = g->cur_fn; - LLVMValueRef prev_cur_fn_val = g->cur_fn_val; - - LLVMBasicBlockRef entry_block = LLVMAppendBasicBlock(fn_val, "Entry"); - LLVMPositionBuilderAtEnd(g->builder, entry_block); - ZigLLVMClearCurrentDebugLocation(g->builder); - g->cur_fn = nullptr; - g->cur_fn_val = fn_val; - - size_t field_count = enum_type->data.enumeration.src_field_count; - LLVMBasicBlockRef bad_value_block = LLVMAppendBasicBlock(g->cur_fn_val, "BadValue"); - LLVMValueRef tag_int_value = LLVMGetParam(fn_val, 0); - LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, tag_int_value, bad_value_block, field_count); - - - ZigType *usize = g->builtin_types.entry_usize; - LLVMValueRef array_ptr_indices[] = { - LLVMConstNull(usize->llvm_type), - LLVMConstNull(usize->llvm_type), - }; - - HashMap occupied_tag_values = {}; - occupied_tag_values.init(field_count); - - for (size_t field_i = 0; field_i < field_count; field_i += 1) { - TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_i]; - - Buf *name = type_enum_field->name; - auto entry = occupied_tag_values.put_unique(type_enum_field->value, name); - if (entry != nullptr) { - continue; - } - - LLVMValueRef str_init = LLVMConstString(buf_ptr(name), (unsigned)buf_len(name), true); - LLVMValueRef str_global = LLVMAddGlobal(g->module, LLVMTypeOf(str_init), ""); - LLVMSetInitializer(str_global, str_init); - LLVMSetLinkage(str_global, LLVMPrivateLinkage); - LLVMSetGlobalConstant(str_global, true); - LLVMSetUnnamedAddr(str_global, true); - LLVMSetAlignment(str_global, LLVMABIAlignmentOfType(g->target_data_ref, LLVMTypeOf(str_init))); - - LLVMValueRef fields[] = { - LLVMConstGEP(str_global, array_ptr_indices, 2), - LLVMConstInt(g->builtin_types.entry_usize->llvm_type, buf_len(name), false), - }; - LLVMValueRef slice_init_value = LLVMConstNamedStruct(get_llvm_type(g, u8_slice_type), fields, 2); - - LLVMValueRef slice_global = LLVMAddGlobal(g->module, LLVMTypeOf(slice_init_value), ""); - LLVMSetInitializer(slice_global, slice_init_value); - LLVMSetLinkage(slice_global, LLVMPrivateLinkage); - LLVMSetGlobalConstant(slice_global, true); - LLVMSetUnnamedAddr(slice_global, true); - LLVMSetAlignment(slice_global, LLVMABIAlignmentOfType(g->target_data_ref, LLVMTypeOf(slice_init_value))); - - LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(g->cur_fn_val, "Name"); - LLVMValueRef this_tag_int_value = bigint_to_llvm_const(get_llvm_type(g, tag_int_type), - &enum_type->data.enumeration.fields[field_i].value); - LLVMAddCase(switch_instr, this_tag_int_value, return_block); - - LLVMPositionBuilderAtEnd(g->builder, return_block); - LLVMBuildRet(g->builder, slice_global); - } - occupied_tag_values.deinit(); - - LLVMPositionBuilderAtEnd(g->builder, bad_value_block); - if (g->build_mode == BuildModeDebug || g->build_mode == BuildModeSafeRelease) { - gen_safety_crash(g, PanicMsgIdBadEnumValue); - } else { - LLVMBuildUnreachable(g->builder); - } - - g->cur_fn = prev_cur_fn; - g->cur_fn_val = prev_cur_fn_val; - LLVMPositionBuilderAtEnd(g->builder, prev_block); - if (!g->strip_debug_symbols) { - LLVMSetCurrentDebugLocation(g->builder, prev_debug_location); - } - - enum_type->data.enumeration.name_function = fn_val; - return fn_val; -} - -static LLVMValueRef ir_render_enum_tag_name(CodeGen *g, IrExecutableGen *executable, - IrInstGenTagName *instruction) -{ - ZigType *enum_type = instruction->target->value->type; - assert(enum_type->id == ZigTypeIdEnum); - - LLVMValueRef enum_name_function = get_enum_tag_name_function(g, enum_type); - - LLVMValueRef enum_tag_value = ir_llvm_value(g, instruction->target); - return ZigLLVMBuildCall(g->builder, enum_name_function, &enum_tag_value, 1, - get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_CallAttrAuto, ""); -} - -static LLVMValueRef ir_render_field_parent_ptr(CodeGen *g, IrExecutableGen *executable, - IrInstGenFieldParentPtr *instruction) -{ - ZigType *container_ptr_type = instruction->base.value->type; - assert(container_ptr_type->id == ZigTypeIdPointer); - - ZigType *container_type = container_ptr_type->data.pointer.child_type; - - size_t byte_offset = LLVMOffsetOfElement(g->target_data_ref, - get_llvm_type(g, container_type), instruction->field->gen_index); - - LLVMValueRef field_ptr_val = ir_llvm_value(g, instruction->field_ptr); - - if (byte_offset == 0) { - return LLVMBuildBitCast(g->builder, field_ptr_val, get_llvm_type(g, container_ptr_type), ""); - } else { - ZigType *usize = g->builtin_types.entry_usize; - - LLVMValueRef field_ptr_int = LLVMBuildPtrToInt(g->builder, field_ptr_val, usize->llvm_type, ""); - - LLVMValueRef base_ptr_int = LLVMBuildNUWSub(g->builder, field_ptr_int, - LLVMConstInt(usize->llvm_type, byte_offset, false), ""); - - return LLVMBuildIntToPtr(g->builder, base_ptr_int, get_llvm_type(g, container_ptr_type), ""); - } -} - -static LLVMValueRef ir_render_align_cast(CodeGen *g, IrExecutableGen *executable, IrInstGenAlignCast *instruction) { - LLVMValueRef target_val = ir_llvm_value(g, instruction->target); - assert(target_val); - - bool want_runtime_safety = ir_want_runtime_safety(g, &instruction->base); - if (!want_runtime_safety) { - return target_val; - } - - ZigType *target_type = instruction->base.value->type; - uint32_t align_bytes; - LLVMValueRef ptr_val; - - if (target_type->id == ZigTypeIdPointer) { - align_bytes = get_ptr_align(g, target_type); - ptr_val = target_val; - } else if (target_type->id == ZigTypeIdFn) { - align_bytes = target_type->data.fn.fn_type_id.alignment; - ptr_val = target_val; - } else if (target_type->id == ZigTypeIdOptional && - target_type->data.maybe.child_type->id == ZigTypeIdPointer) - { - align_bytes = get_ptr_align(g, target_type->data.maybe.child_type); - ptr_val = target_val; - } else if (target_type->id == ZigTypeIdOptional && - target_type->data.maybe.child_type->id == ZigTypeIdFn) - { - align_bytes = target_type->data.maybe.child_type->data.fn.fn_type_id.alignment; - ptr_val = target_val; - } else if (target_type->id == ZigTypeIdStruct && - target_type->data.structure.special == StructSpecialSlice) - { - ZigType *slice_ptr_type = target_type->data.structure.fields[slice_ptr_index]->type_entry; - align_bytes = get_ptr_align(g, slice_ptr_type); - - size_t ptr_index = target_type->data.structure.fields[slice_ptr_index]->gen_index; - LLVMValueRef ptr_val_ptr = LLVMBuildStructGEP(g->builder, target_val, (unsigned)ptr_index, ""); - ptr_val = gen_load_untyped(g, ptr_val_ptr, 0, false, ""); - } else { - zig_unreachable(); - } - - assert(align_bytes != 1); - - ZigType *usize = g->builtin_types.entry_usize; - LLVMValueRef ptr_as_int_val = LLVMBuildPtrToInt(g->builder, ptr_val, usize->llvm_type, ""); - LLVMValueRef alignment_minus_1 = LLVMConstInt(usize->llvm_type, align_bytes - 1, false); - LLVMValueRef anded_val = LLVMBuildAnd(g->builder, ptr_as_int_val, alignment_minus_1, ""); - LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, anded_val, LLVMConstNull(usize->llvm_type), ""); - - LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "AlignCastOk"); - LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "AlignCastFail"); - - LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); - - LLVMPositionBuilderAtEnd(g->builder, fail_block); - gen_safety_crash(g, PanicMsgIdIncorrectAlignment); - - LLVMPositionBuilderAtEnd(g->builder, ok_block); - - return target_val; -} - -static LLVMValueRef ir_render_error_return_trace(CodeGen *g, IrExecutableGen *executable, - IrInstGenErrorReturnTrace *instruction) -{ - bool is_llvm_alloca; - LLVMValueRef cur_err_ret_trace_val = get_cur_err_ret_trace_val(g, instruction->base.base.scope, &is_llvm_alloca); - if (cur_err_ret_trace_val == nullptr) { - return LLVMConstNull(get_llvm_type(g, ptr_to_stack_trace_type(g))); - } - return cur_err_ret_trace_val; -} - -static LLVMAtomicOrdering to_LLVMAtomicOrdering(AtomicOrder atomic_order) { - switch (atomic_order) { - case AtomicOrderUnordered: return LLVMAtomicOrderingUnordered; - case AtomicOrderMonotonic: return LLVMAtomicOrderingMonotonic; - case AtomicOrderAcquire: return LLVMAtomicOrderingAcquire; - case AtomicOrderRelease: return LLVMAtomicOrderingRelease; - case AtomicOrderAcqRel: return LLVMAtomicOrderingAcquireRelease; - case AtomicOrderSeqCst: return LLVMAtomicOrderingSequentiallyConsistent; - } - zig_unreachable(); -} - -static enum ZigLLVM_AtomicRMWBinOp to_ZigLLVMAtomicRMWBinOp(AtomicRmwOp op, bool is_signed, bool is_float) { - switch (op) { - case AtomicRmwOp_xchg: return ZigLLVMAtomicRMWBinOpXchg; - case AtomicRmwOp_add: - return is_float ? ZigLLVMAtomicRMWBinOpFAdd : ZigLLVMAtomicRMWBinOpAdd; - case AtomicRmwOp_sub: - return is_float ? ZigLLVMAtomicRMWBinOpFSub : ZigLLVMAtomicRMWBinOpSub; - case AtomicRmwOp_and: return ZigLLVMAtomicRMWBinOpAnd; - case AtomicRmwOp_nand: return ZigLLVMAtomicRMWBinOpNand; - case AtomicRmwOp_or: return ZigLLVMAtomicRMWBinOpOr; - case AtomicRmwOp_xor: return ZigLLVMAtomicRMWBinOpXor; - case AtomicRmwOp_max: - return is_signed ? ZigLLVMAtomicRMWBinOpMax : ZigLLVMAtomicRMWBinOpUMax; - case AtomicRmwOp_min: - return is_signed ? ZigLLVMAtomicRMWBinOpMin : ZigLLVMAtomicRMWBinOpUMin; - } - zig_unreachable(); -} - -static LLVMTypeRef get_atomic_abi_type(CodeGen *g, IrInstGen *instruction) { - // If the operand type of an atomic operation is not a power of two sized - // we need to widen it before using it and then truncate the result. - - ir_assert(instruction->value->type->id == ZigTypeIdPointer, instruction); - ZigType *operand_type = instruction->value->type->data.pointer.child_type; - if (operand_type->id == ZigTypeIdInt || operand_type->id == ZigTypeIdEnum) { - if (operand_type->id == ZigTypeIdEnum) { - operand_type = operand_type->data.enumeration.tag_int_type; - } - auto bit_count = operand_type->data.integral.bit_count; - bool is_signed = operand_type->data.integral.is_signed; - - ir_assert(bit_count != 0, instruction); - if (bit_count == 1 || !is_power_of_2(bit_count)) { - return get_llvm_type(g, get_int_type(g, is_signed, operand_type->abi_size * 8)); - } else { - return nullptr; - } - } else if (operand_type->id == ZigTypeIdFloat) { - return nullptr; - } else if (operand_type->id == ZigTypeIdBool) { - return g->builtin_types.entry_u8->llvm_type; - } else { - ir_assert(get_codegen_ptr_type_bail(g, operand_type) != nullptr, instruction); - return nullptr; - } -} - -static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutableGen *executable, IrInstGenCmpxchg *instruction) { - LLVMValueRef ptr_val = ir_llvm_value(g, instruction->ptr); - LLVMValueRef cmp_val = ir_llvm_value(g, instruction->cmp_value); - LLVMValueRef new_val = ir_llvm_value(g, instruction->new_value); - - ZigType *operand_type = instruction->new_value->value->type; - LLVMTypeRef actual_abi_type = get_atomic_abi_type(g, instruction->ptr); - if (actual_abi_type != nullptr) { - // operand needs widening and truncating - ptr_val = LLVMBuildBitCast(g->builder, ptr_val, - LLVMPointerType(actual_abi_type, 0), ""); - if (operand_type->data.integral.is_signed) { - cmp_val = LLVMBuildSExt(g->builder, cmp_val, actual_abi_type, ""); - new_val = LLVMBuildSExt(g->builder, new_val, actual_abi_type, ""); - } else { - cmp_val = LLVMBuildZExt(g->builder, cmp_val, actual_abi_type, ""); - new_val = LLVMBuildZExt(g->builder, new_val, actual_abi_type, ""); - } - } - - LLVMAtomicOrdering success_order = to_LLVMAtomicOrdering(instruction->success_order); - LLVMAtomicOrdering failure_order = to_LLVMAtomicOrdering(instruction->failure_order); - - LLVMValueRef result_val = ZigLLVMBuildCmpXchg(g->builder, ptr_val, cmp_val, new_val, - success_order, failure_order, instruction->is_weak); - - ZigType *optional_type = instruction->base.value->type; - assert(optional_type->id == ZigTypeIdOptional); - ZigType *child_type = optional_type->data.maybe.child_type; - - if (!handle_is_ptr(g, optional_type)) { - LLVMValueRef payload_val = LLVMBuildExtractValue(g->builder, result_val, 0, ""); - if (actual_abi_type != nullptr) { - payload_val = LLVMBuildTrunc(g->builder, payload_val, get_llvm_type(g, operand_type), ""); - } - LLVMValueRef success_bit = LLVMBuildExtractValue(g->builder, result_val, 1, ""); - return LLVMBuildSelect(g->builder, success_bit, LLVMConstNull(get_llvm_type(g, child_type)), payload_val, ""); - } - - // When the cmpxchg is discarded, the result location will have no bits. - if (!type_has_bits(g, instruction->result_loc->value->type)) { - return nullptr; - } - - LLVMValueRef result_loc = ir_llvm_value(g, instruction->result_loc); - ir_assert(result_loc != nullptr, &instruction->base); - ir_assert(type_has_bits(g, child_type), &instruction->base); - - LLVMValueRef payload_val = LLVMBuildExtractValue(g->builder, result_val, 0, ""); - if (actual_abi_type != nullptr) { - payload_val = LLVMBuildTrunc(g->builder, payload_val, get_llvm_type(g, operand_type), ""); - } - LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, result_loc, maybe_child_index, ""); - gen_assign_raw(g, val_ptr, get_pointer_to_type(g, child_type, false), payload_val); - - LLVMValueRef success_bit = LLVMBuildExtractValue(g->builder, result_val, 1, ""); - LLVMValueRef nonnull_bit = LLVMBuildNot(g->builder, success_bit, ""); - LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, result_loc, maybe_null_index, ""); - gen_store_untyped(g, nonnull_bit, maybe_ptr, 0, false); - return result_loc; -} - -static LLVMValueRef ir_render_fence(CodeGen *g, IrExecutableGen *executable, IrInstGenFence *instruction) { - LLVMAtomicOrdering atomic_order = to_LLVMAtomicOrdering(instruction->order); - LLVMBuildFence(g->builder, atomic_order, false, ""); - return nullptr; -} - -static LLVMValueRef ir_render_truncate(CodeGen *g, IrExecutableGen *executable, IrInstGenTruncate *instruction) { - LLVMValueRef target_val = ir_llvm_value(g, instruction->target); - ZigType *dest_type = instruction->base.value->type; - ZigType *src_type = instruction->target->value->type; - if (dest_type == src_type) { - // no-op - return target_val; - } if (src_type->data.integral.bit_count == dest_type->data.integral.bit_count) { - return LLVMBuildBitCast(g->builder, target_val, get_llvm_type(g, dest_type), ""); - } else { - LLVMValueRef target_val = ir_llvm_value(g, instruction->target); - return LLVMBuildTrunc(g->builder, target_val, get_llvm_type(g, dest_type), ""); - } -} - -static LLVMValueRef ir_render_memset(CodeGen *g, IrExecutableGen *executable, IrInstGenMemset *instruction) { - LLVMValueRef dest_ptr = ir_llvm_value(g, instruction->dest_ptr); - LLVMValueRef len_val = ir_llvm_value(g, instruction->count); - - LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0); - LLVMValueRef dest_ptr_casted = LLVMBuildBitCast(g->builder, dest_ptr, ptr_u8, ""); - - ZigType *ptr_type = instruction->dest_ptr->value->type; - assert(ptr_type->id == ZigTypeIdPointer); - - bool val_is_undef = value_is_all_undef(g, instruction->byte->value); - LLVMValueRef fill_char; - if (val_is_undef) { - if (ir_want_runtime_safety_scope(g, instruction->base.base.scope)) { - fill_char = LLVMConstInt(LLVMInt8Type(), 0xaa, false); - } else { - return nullptr; - } - } else { - fill_char = ir_llvm_value(g, instruction->byte); - } - ZigLLVMBuildMemSet(g->builder, dest_ptr_casted, fill_char, len_val, get_ptr_align(g, ptr_type), - ptr_type->data.pointer.is_volatile); - - if (val_is_undef && want_valgrind_support(g)) { - gen_valgrind_undef(g, dest_ptr_casted, len_val); - } - return nullptr; -} - -static LLVMValueRef ir_render_memcpy(CodeGen *g, IrExecutableGen *executable, IrInstGenMemcpy *instruction) { - LLVMValueRef dest_ptr = ir_llvm_value(g, instruction->dest_ptr); - LLVMValueRef src_ptr = ir_llvm_value(g, instruction->src_ptr); - LLVMValueRef len_val = ir_llvm_value(g, instruction->count); - - LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0); - - LLVMValueRef dest_ptr_casted = LLVMBuildBitCast(g->builder, dest_ptr, ptr_u8, ""); - LLVMValueRef src_ptr_casted = LLVMBuildBitCast(g->builder, src_ptr, ptr_u8, ""); - - ZigType *dest_ptr_type = instruction->dest_ptr->value->type; - ZigType *src_ptr_type = instruction->src_ptr->value->type; - - assert(dest_ptr_type->id == ZigTypeIdPointer); - assert(src_ptr_type->id == ZigTypeIdPointer); - - bool is_volatile = (dest_ptr_type->data.pointer.is_volatile || src_ptr_type->data.pointer.is_volatile); - ZigLLVMBuildMemCpy(g->builder, dest_ptr_casted, get_ptr_align(g, dest_ptr_type), - src_ptr_casted, get_ptr_align(g, src_ptr_type), len_val, is_volatile); - return nullptr; -} - -static LLVMValueRef ir_render_wasm_memory_size(CodeGen *g, IrExecutableGen *executable, IrInstGenWasmMemorySize *instruction) { - // TODO adjust for wasm64 - LLVMValueRef param = ir_llvm_value(g, instruction->index); - LLVMValueRef val = LLVMBuildCall(g->builder, gen_wasm_memory_size(g), ¶m, 1, ""); - return val; -} - -static LLVMValueRef ir_render_wasm_memory_grow(CodeGen *g, IrExecutableGen *executable, IrInstGenWasmMemoryGrow *instruction) { - // TODO adjust for wasm64 - LLVMValueRef params[] = { - ir_llvm_value(g, instruction->index), - ir_llvm_value(g, instruction->delta), - }; - LLVMValueRef val = LLVMBuildCall(g->builder, gen_wasm_memory_grow(g), params, 2, ""); - return val; -} - -static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrInstGenSlice *instruction) { - Error err; - - LLVMValueRef array_ptr_ptr = ir_llvm_value(g, instruction->ptr); - ZigType *array_ptr_type = instruction->ptr->value->type; - assert(array_ptr_type->id == ZigTypeIdPointer); - ZigType *array_type = array_ptr_type->data.pointer.child_type; - LLVMValueRef array_ptr = get_handle_value(g, array_ptr_ptr, array_type, array_ptr_type); - - bool want_runtime_safety = instruction->safety_check_on && ir_want_runtime_safety(g, &instruction->base); - - // The result is either a slice or a pointer to an array - ZigType *result_type = instruction->base.value->type; - - // This is not whether the result type has a sentinel, but whether there should be a sentinel check, - // e.g. if they used [a..b :s] syntax. - ZigValue *sentinel = instruction->sentinel; - - LLVMValueRef slice_start_ptr = nullptr; - LLVMValueRef len_value = nullptr; - - if (array_type->id == ZigTypeIdArray || - (array_type->id == ZigTypeIdPointer && array_type->data.pointer.ptr_len == PtrLenSingle)) - { - if (array_type->id == ZigTypeIdPointer) { - array_type = array_type->data.pointer.child_type; - } - LLVMValueRef start_val = ir_llvm_value(g, instruction->start); - LLVMValueRef end_val; - if (instruction->end) { - end_val = ir_llvm_value(g, instruction->end); - } else { - end_val = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, array_type->data.array.len, false); - } - - if (want_runtime_safety) { - // Safety check: start <= end - if (instruction->start->value->special == ConstValSpecialRuntime || instruction->end) { - add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val); - } - - // Safety check: the last element of the slice (the sentinel if - // requested) must be inside the array - // XXX: Overflow is not checked here... - const size_t full_len = array_type->data.array.len + - (array_type->data.array.sentinel != nullptr); - LLVMValueRef array_end = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, - full_len, false); - - LLVMValueRef check_end_val = end_val; - if (sentinel != nullptr) { - LLVMValueRef usize_one = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, 1, false); - check_end_val = LLVMBuildNUWAdd(g->builder, end_val, usize_one, ""); - } - add_bounds_check(g, check_end_val, LLVMIntEQ, nullptr, LLVMIntULE, array_end); - } - - bool value_has_bits; - if ((err = type_has_bits2(g, array_type, &value_has_bits))) - codegen_report_errors_and_exit(g); - - if (value_has_bits) { - if (want_runtime_safety && sentinel != nullptr) { - LLVMValueRef indices[] = { - LLVMConstNull(g->builtin_types.entry_usize->llvm_type), - end_val, - }; - LLVMValueRef sentinel_elem_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, ""); - add_sentinel_check(g, sentinel_elem_ptr, sentinel); - } - - LLVMValueRef indices[] = { - LLVMConstNull(g->builtin_types.entry_usize->llvm_type), - start_val, - }; - slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, ""); - } - - len_value = LLVMBuildNUWSub(g->builder, end_val, start_val, ""); - } else if (array_type->id == ZigTypeIdPointer) { - assert(array_type->data.pointer.ptr_len != PtrLenSingle); - LLVMValueRef start_val = ir_llvm_value(g, instruction->start); - LLVMValueRef end_val = ir_llvm_value(g, instruction->end); - - if (want_runtime_safety) { - // Safety check: start <= end - add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val); - } - - bool value_has_bits; - if ((err = type_has_bits2(g, array_type, &value_has_bits))) - codegen_report_errors_and_exit(g); - - if (value_has_bits) { - if (want_runtime_safety && sentinel != nullptr) { - LLVMValueRef sentinel_elem_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &end_val, 1, ""); - add_sentinel_check(g, sentinel_elem_ptr, sentinel); - } - - slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &start_val, 1, ""); - } - - len_value = LLVMBuildNUWSub(g->builder, end_val, start_val, ""); - } else if (array_type->id == ZigTypeIdStruct) { - assert(array_type->data.structure.special == StructSpecialSlice); - assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind); - assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind); - - const size_t gen_len_index = array_type->data.structure.fields[slice_len_index]->gen_index; - assert(gen_len_index != SIZE_MAX); - - LLVMValueRef prev_end = nullptr; - if (!instruction->end || want_runtime_safety) { - LLVMValueRef src_len_ptr = LLVMBuildStructGEP(g->builder, array_ptr, gen_len_index, ""); - prev_end = gen_load_untyped(g, src_len_ptr, 0, false, ""); - } - - LLVMValueRef start_val = ir_llvm_value(g, instruction->start); - LLVMValueRef end_val; - if (instruction->end) { - end_val = ir_llvm_value(g, instruction->end); - } else { - end_val = prev_end; - } - - ZigType *ptr_field_type = array_type->data.structure.fields[slice_ptr_index]->type_entry; - - if (want_runtime_safety) { - assert(prev_end); - // Safety check: start <= end - add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val); - - // Safety check: the sentinel counts as one more element - // XXX: Overflow is not checked here... - LLVMValueRef check_prev_end = prev_end; - if (ptr_field_type->data.pointer.sentinel != nullptr) { - LLVMValueRef usize_one = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, 1, false); - check_prev_end = LLVMBuildNUWAdd(g->builder, prev_end, usize_one, ""); - } - LLVMValueRef check_end_val = end_val; - if (sentinel != nullptr) { - LLVMValueRef usize_one = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, 1, false); - check_end_val = LLVMBuildNUWAdd(g->builder, end_val, usize_one, ""); - } - - add_bounds_check(g, check_end_val, LLVMIntEQ, nullptr, LLVMIntULE, check_prev_end); - } - - bool ptr_has_bits; - if ((err = type_has_bits2(g, ptr_field_type, &ptr_has_bits))) - codegen_report_errors_and_exit(g); - - if (ptr_has_bits) { - const size_t gen_ptr_index = array_type->data.structure.fields[slice_ptr_index]->gen_index; - assert(gen_ptr_index != SIZE_MAX); - - LLVMValueRef src_ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, gen_ptr_index, ""); - LLVMValueRef src_ptr = gen_load_untyped(g, src_ptr_ptr, 0, false, ""); - - if (sentinel != nullptr) { - LLVMValueRef sentinel_elem_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr, &end_val, 1, ""); - add_sentinel_check(g, sentinel_elem_ptr, sentinel); - } - - slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr, &start_val, 1, ""); - } - - len_value = LLVMBuildNUWSub(g->builder, end_val, start_val, ""); - } else { - zig_unreachable(); - } - - bool result_has_bits; - if ((err = type_has_bits2(g, result_type, &result_has_bits))) - codegen_report_errors_and_exit(g); - - // Nothing to do, we're only interested in the bound checks emitted above - if (!result_has_bits) - return nullptr; - - // The starting pointer for the slice may be null in case of zero-sized - // arrays, the length value is always defined. - assert(len_value != nullptr); - - // The slice decays into a pointer to an array, the size is tracked in the - // type itself - if (result_type->id == ZigTypeIdPointer) { - ir_assert(instruction->result_loc == nullptr, &instruction->base); - LLVMTypeRef result_ptr_type = get_llvm_type(g, result_type); - - if (slice_start_ptr != nullptr) { - return LLVMBuildBitCast(g->builder, slice_start_ptr, result_ptr_type, ""); - } - - return LLVMGetUndef(result_ptr_type); - } - - ir_assert(instruction->result_loc != nullptr, &instruction->base); - // Create a new slice - LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc); - - ZigType *slice_ptr_type = result_type->data.structure.fields[slice_ptr_index]->type_entry; - - // The slice may not have a pointer at all if it points to a zero-sized type - const size_t gen_ptr_index = result_type->data.structure.fields[slice_ptr_index]->gen_index; - if (gen_ptr_index != SIZE_MAX) { - LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_ptr_index, ""); - if (slice_start_ptr != nullptr) { - gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); - } else if (want_runtime_safety) { - gen_undef_init(g, slice_ptr_type->abi_align, slice_ptr_type, ptr_field_ptr); - } else { - gen_store_untyped(g, LLVMGetUndef(get_llvm_type(g, slice_ptr_type)), ptr_field_ptr, 0, false); - } - } - - const size_t gen_len_index = result_type->data.structure.fields[slice_len_index]->gen_index; - assert(gen_len_index != SIZE_MAX); - - LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_len_index, ""); - gen_store_untyped(g, len_value, len_field_ptr, 0, false); - - return tmp_struct_ptr; -} - -static LLVMValueRef get_trap_fn_val(CodeGen *g) { - if (g->trap_fn_val) - return g->trap_fn_val; - - LLVMTypeRef fn_type = LLVMFunctionType(LLVMVoidType(), nullptr, 0, false); - g->trap_fn_val = LLVMAddFunction(g->module, "llvm.debugtrap", fn_type); - assert(LLVMGetIntrinsicID(g->trap_fn_val)); - - return g->trap_fn_val; -} - - -static LLVMValueRef ir_render_breakpoint(CodeGen *g, IrExecutableGen *executable, IrInstGenBreakpoint *instruction) { - LLVMBuildCall(g->builder, get_trap_fn_val(g), nullptr, 0, ""); - return nullptr; -} - -static LLVMValueRef ir_render_return_address(CodeGen *g, IrExecutableGen *executable, - IrInstGenReturnAddress *instruction) -{ - if (target_is_wasm(g->zig_target) && g->zig_target->os != OsEmscripten) { - // I got this error from LLVM 10: - // "Non-Emscripten WebAssembly hasn't implemented __builtin_return_address" - return LLVMConstNull(get_llvm_type(g, instruction->base.value->type)); - } - - LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_i32->llvm_type); - LLVMValueRef ptr_val = LLVMBuildCall(g->builder, get_return_address_fn_val(g), &zero, 1, ""); - return LLVMBuildPtrToInt(g->builder, ptr_val, g->builtin_types.entry_usize->llvm_type, ""); -} - -static LLVMValueRef get_frame_address_fn_val(CodeGen *g) { - if (g->frame_address_fn_val) - return g->frame_address_fn_val; - - ZigType *return_type = get_pointer_to_type(g, g->builtin_types.entry_u8, true); - - LLVMTypeRef fn_type = LLVMFunctionType(get_llvm_type(g, return_type), - &g->builtin_types.entry_i32->llvm_type, 1, false); - g->frame_address_fn_val = LLVMAddFunction(g->module, "llvm.frameaddress.p0i8", fn_type); - assert(LLVMGetIntrinsicID(g->frame_address_fn_val)); - - return g->frame_address_fn_val; -} - -static LLVMValueRef ir_render_frame_address(CodeGen *g, IrExecutableGen *executable, - IrInstGenFrameAddress *instruction) -{ - LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_i32->llvm_type); - LLVMValueRef ptr_val = LLVMBuildCall(g->builder, get_frame_address_fn_val(g), &zero, 1, ""); - return LLVMBuildPtrToInt(g->builder, ptr_val, g->builtin_types.entry_usize->llvm_type, ""); -} - -static LLVMValueRef ir_render_handle(CodeGen *g, IrExecutableGen *executable, IrInstGenFrameHandle *instruction) { - return g->cur_frame_ptr; -} - -static LLVMValueRef render_shl_with_overflow(CodeGen *g, IrInstGenOverflowOp *instruction) { - ZigType *int_type = instruction->result_ptr_type; - assert(int_type->id == ZigTypeIdInt); - - LLVMValueRef op1 = ir_llvm_value(g, instruction->op1); - LLVMValueRef op2 = ir_llvm_value(g, instruction->op2); - LLVMValueRef ptr_result = ir_llvm_value(g, instruction->result_ptr); - - LLVMValueRef op2_casted = gen_widen_or_shorten(g, false, instruction->op2->value->type, - instruction->op1->value->type, op2); - - LLVMValueRef result = LLVMBuildShl(g->builder, op1, op2_casted, ""); - LLVMValueRef orig_val; - if (int_type->data.integral.is_signed) { - orig_val = LLVMBuildAShr(g->builder, result, op2_casted, ""); - } else { - orig_val = LLVMBuildLShr(g->builder, result, op2_casted, ""); - } - LLVMValueRef overflow_bit = LLVMBuildICmp(g->builder, LLVMIntNE, op1, orig_val, ""); - - gen_store(g, result, ptr_result, instruction->result_ptr->value->type); - - return overflow_bit; -} - -static LLVMValueRef ir_render_overflow_op(CodeGen *g, IrExecutableGen *executable, IrInstGenOverflowOp *instruction) { - AddSubMul add_sub_mul; - switch (instruction->op) { - case IrOverflowOpAdd: - add_sub_mul = AddSubMulAdd; - break; - case IrOverflowOpSub: - add_sub_mul = AddSubMulSub; - break; - case IrOverflowOpMul: - add_sub_mul = AddSubMulMul; - break; - case IrOverflowOpShl: - return render_shl_with_overflow(g, instruction); - } - - ZigType *int_type = instruction->result_ptr_type; - assert(int_type->id == ZigTypeIdInt); - - LLVMValueRef fn_val = get_int_overflow_fn(g, int_type, add_sub_mul); - - LLVMValueRef op1 = ir_llvm_value(g, instruction->op1); - LLVMValueRef op2 = ir_llvm_value(g, instruction->op2); - LLVMValueRef ptr_result = ir_llvm_value(g, instruction->result_ptr); - - LLVMValueRef params[] = { - op1, - op2, - }; - - LLVMValueRef result_struct = LLVMBuildCall(g->builder, fn_val, params, 2, ""); - LLVMValueRef result = LLVMBuildExtractValue(g->builder, result_struct, 0, ""); - LLVMValueRef overflow_bit = LLVMBuildExtractValue(g->builder, result_struct, 1, ""); - gen_store(g, result, ptr_result, instruction->result_ptr->value->type); - - return overflow_bit; -} - -static LLVMValueRef ir_render_test_err(CodeGen *g, IrExecutableGen *executable, IrInstGenTestErr *instruction) { - ZigType *err_union_type = instruction->err_union->value->type; - ZigType *payload_type = err_union_type->data.error_union.payload_type; - LLVMValueRef err_union_handle = ir_llvm_value(g, instruction->err_union); - - LLVMValueRef err_val; - if (type_has_bits(g, payload_type)) { - LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, err_union_handle, err_union_err_index, ""); - err_val = gen_load_untyped(g, err_val_ptr, 0, false, ""); - } else { - err_val = err_union_handle; - } - - LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, g->err_tag_type)); - return LLVMBuildICmp(g->builder, LLVMIntNE, err_val, zero, ""); -} - -static LLVMValueRef ir_render_unwrap_err_code(CodeGen *g, IrExecutableGen *executable, - IrInstGenUnwrapErrCode *instruction) -{ - if (instruction->base.value->special != ConstValSpecialRuntime) - return nullptr; - - ZigType *ptr_type = instruction->err_union_ptr->value->type; - assert(ptr_type->id == ZigTypeIdPointer); - ZigType *err_union_type = ptr_type->data.pointer.child_type; - ZigType *payload_type = err_union_type->data.error_union.payload_type; - LLVMValueRef err_union_ptr = ir_llvm_value(g, instruction->err_union_ptr); - if (!type_has_bits(g, payload_type)) { - return err_union_ptr; - } else { - // TODO assign undef to the payload - LLVMValueRef err_union_handle = get_handle_value(g, err_union_ptr, err_union_type, ptr_type); - return LLVMBuildStructGEP(g->builder, err_union_handle, err_union_err_index, ""); - } -} - -static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutableGen *executable, - IrInstGenUnwrapErrPayload *instruction) -{ - Error err; - - if (instruction->base.value->special != ConstValSpecialRuntime) - return nullptr; - - bool want_safety = instruction->safety_check_on && ir_want_runtime_safety(g, &instruction->base) && - g->errors_by_index.length > 1; - - ZigType *ptr_type = instruction->value->value->type; - assert(ptr_type->id == ZigTypeIdPointer); - ZigType *err_union_type = ptr_type->data.pointer.child_type; - ZigType *payload_type = err_union_type->data.error_union.payload_type; - LLVMValueRef err_union_ptr = ir_llvm_value(g, instruction->value); - - LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, g->err_tag_type)); - bool value_has_bits; - if ((err = type_has_bits2(g, instruction->base.value->type, &value_has_bits))) - codegen_report_errors_and_exit(g); - if (!want_safety && !value_has_bits) { - if (instruction->initializing) { - gen_store_untyped(g, zero, err_union_ptr, 0, false); - } - return nullptr; - } - - - LLVMValueRef err_union_handle = get_handle_value(g, err_union_ptr, err_union_type, ptr_type); - - if (!type_has_bits(g, err_union_type->data.error_union.err_set_type)) { - return err_union_handle; - } - - if (want_safety) { - LLVMValueRef err_val; - if (type_has_bits(g, payload_type)) { - LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, err_union_handle, err_union_err_index, ""); - err_val = gen_load_untyped(g, err_val_ptr, 0, false, ""); - } else { - err_val = err_union_handle; - } - LLVMValueRef cond_val = LLVMBuildICmp(g->builder, LLVMIntEQ, err_val, zero, ""); - LLVMBasicBlockRef err_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapErrError"); - LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapErrOk"); - LLVMBuildCondBr(g->builder, cond_val, ok_block, err_block); - - LLVMPositionBuilderAtEnd(g->builder, err_block); - gen_safety_crash_for_err(g, err_val, instruction->base.base.scope); - - LLVMPositionBuilderAtEnd(g->builder, ok_block); - } - - if (type_has_bits(g, payload_type)) { - if (instruction->initializing) { - LLVMValueRef err_tag_ptr = LLVMBuildStructGEP(g->builder, err_union_handle, err_union_err_index, ""); - LLVMValueRef ok_err_val = LLVMConstNull(get_llvm_type(g, g->err_tag_type)); - gen_store_untyped(g, ok_err_val, err_tag_ptr, 0, false); - } - return LLVMBuildStructGEP(g->builder, err_union_handle, err_union_payload_index, ""); - } else { - if (instruction->initializing) { - gen_store_untyped(g, zero, err_union_ptr, 0, false); - } - return nullptr; - } -} - -static LLVMValueRef ir_render_optional_wrap(CodeGen *g, IrExecutableGen *executable, IrInstGenOptionalWrap *instruction) { - ZigType *wanted_type = instruction->base.value->type; - - assert(wanted_type->id == ZigTypeIdOptional); - - ZigType *child_type = wanted_type->data.maybe.child_type; - - if (!type_has_bits(g, child_type)) { - LLVMValueRef result = LLVMConstAllOnes(LLVMInt1Type()); - if (instruction->result_loc != nullptr) { - LLVMValueRef result_loc = ir_llvm_value(g, instruction->result_loc); - gen_store_untyped(g, result, result_loc, 0, false); - } - return result; - } - - LLVMValueRef payload_val = ir_llvm_value(g, instruction->operand); - if (!handle_is_ptr(g, wanted_type)) { - if (instruction->result_loc != nullptr) { - LLVMValueRef result_loc = ir_llvm_value(g, instruction->result_loc); - gen_store_untyped(g, payload_val, result_loc, 0, false); - } - return payload_val; - } - - LLVMValueRef result_loc = ir_llvm_value(g, instruction->result_loc); - - LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, result_loc, maybe_child_index, ""); - // child_type and instruction->value->value->type may differ by constness - gen_assign_raw(g, val_ptr, get_pointer_to_type(g, child_type, false), payload_val); - LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, result_loc, maybe_null_index, ""); - gen_store_untyped(g, LLVMConstAllOnes(LLVMInt1Type()), maybe_ptr, 0, false); - - return result_loc; -} - -static LLVMValueRef ir_render_err_wrap_code(CodeGen *g, IrExecutableGen *executable, IrInstGenErrWrapCode *instruction) { - ZigType *wanted_type = instruction->base.value->type; - - assert(wanted_type->id == ZigTypeIdErrorUnion); - - LLVMValueRef err_val = ir_llvm_value(g, instruction->operand); - - if (!handle_is_ptr(g, wanted_type)) - return err_val; - - LLVMValueRef result_loc = ir_llvm_value(g, instruction->result_loc); - - LLVMValueRef err_tag_ptr = LLVMBuildStructGEP(g->builder, result_loc, err_union_err_index, ""); - gen_store_untyped(g, err_val, err_tag_ptr, 0, false); - - // TODO store undef to the payload - - return result_loc; -} - -static LLVMValueRef ir_render_err_wrap_payload(CodeGen *g, IrExecutableGen *executable, IrInstGenErrWrapPayload *instruction) { - ZigType *wanted_type = instruction->base.value->type; - - assert(wanted_type->id == ZigTypeIdErrorUnion); - - ZigType *payload_type = wanted_type->data.error_union.payload_type; - ZigType *err_set_type = wanted_type->data.error_union.err_set_type; - - if (!type_has_bits(g, err_set_type)) { - return ir_llvm_value(g, instruction->operand); - } - - LLVMValueRef ok_err_val = LLVMConstNull(get_llvm_type(g, g->err_tag_type)); - - if (!type_has_bits(g, payload_type)) - return ok_err_val; - - - LLVMValueRef result_loc = ir_llvm_value(g, instruction->result_loc); - - LLVMValueRef payload_val = ir_llvm_value(g, instruction->operand); - - LLVMValueRef err_tag_ptr = LLVMBuildStructGEP(g->builder, result_loc, err_union_err_index, ""); - gen_store_untyped(g, ok_err_val, err_tag_ptr, 0, false); - - LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, result_loc, err_union_payload_index, ""); - gen_assign_raw(g, payload_ptr, get_pointer_to_type(g, payload_type, false), payload_val); - - return result_loc; -} - -static LLVMValueRef ir_render_union_tag(CodeGen *g, IrExecutableGen *executable, IrInstGenUnionTag *instruction) { - ZigType *union_type = instruction->value->value->type; - - ZigType *tag_type = union_type->data.unionation.tag_type; - if (!type_has_bits(g, tag_type)) - return nullptr; - - LLVMValueRef union_val = ir_llvm_value(g, instruction->value); - if (union_type->data.unionation.gen_field_count == 0) - return union_val; - - assert(union_type->data.unionation.gen_tag_index != SIZE_MAX); - LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, union_val, - union_type->data.unionation.gen_tag_index, ""); - ZigType *ptr_type = get_pointer_to_type(g, tag_type, false); - return get_handle_value(g, tag_field_ptr, tag_type, ptr_type); -} - -static LLVMValueRef ir_render_panic(CodeGen *g, IrExecutableGen *executable, IrInstGenPanic *instruction) { - bool is_llvm_alloca; - LLVMValueRef err_ret_trace_val = get_cur_err_ret_trace_val(g, instruction->base.base.scope, &is_llvm_alloca); - gen_panic(g, ir_llvm_value(g, instruction->msg), err_ret_trace_val, is_llvm_alloca); - return nullptr; -} - -static LLVMValueRef ir_render_atomic_rmw(CodeGen *g, IrExecutableGen *executable, - IrInstGenAtomicRmw *instruction) -{ - bool is_signed; - ZigType *operand_type = instruction->operand->value->type; - bool is_float = operand_type->id == ZigTypeIdFloat; - if (operand_type->id == ZigTypeIdInt) { - is_signed = operand_type->data.integral.is_signed; - } else { - is_signed = false; - } - enum ZigLLVM_AtomicRMWBinOp op = to_ZigLLVMAtomicRMWBinOp(instruction->op, is_signed, is_float); - LLVMAtomicOrdering ordering = to_LLVMAtomicOrdering(instruction->ordering); - LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr); - LLVMValueRef operand = ir_llvm_value(g, instruction->operand); - - LLVMTypeRef actual_abi_type = get_atomic_abi_type(g, instruction->ptr); - if (actual_abi_type != nullptr) { - // operand needs widening and truncating - LLVMValueRef casted_ptr = LLVMBuildBitCast(g->builder, ptr, - LLVMPointerType(actual_abi_type, 0), ""); - LLVMValueRef casted_operand; - if (operand_type->data.integral.is_signed) { - casted_operand = LLVMBuildSExt(g->builder, operand, actual_abi_type, ""); - } else { - casted_operand = LLVMBuildZExt(g->builder, operand, actual_abi_type, ""); - } - LLVMValueRef uncasted_result = ZigLLVMBuildAtomicRMW(g->builder, op, casted_ptr, casted_operand, ordering, - g->is_single_threaded); - return LLVMBuildTrunc(g->builder, uncasted_result, get_llvm_type(g, operand_type), ""); - } - - if (get_codegen_ptr_type_bail(g, operand_type) == nullptr) { - return ZigLLVMBuildAtomicRMW(g->builder, op, ptr, operand, ordering, g->is_single_threaded); - } - - // it's a pointer but we need to treat it as an int - LLVMValueRef casted_ptr = LLVMBuildBitCast(g->builder, ptr, - LLVMPointerType(g->builtin_types.entry_usize->llvm_type, 0), ""); - LLVMValueRef casted_operand = LLVMBuildPtrToInt(g->builder, operand, g->builtin_types.entry_usize->llvm_type, ""); - LLVMValueRef uncasted_result = ZigLLVMBuildAtomicRMW(g->builder, op, casted_ptr, casted_operand, ordering, - g->is_single_threaded); - return LLVMBuildIntToPtr(g->builder, uncasted_result, get_llvm_type(g, operand_type), ""); -} - -static LLVMValueRef ir_render_atomic_load(CodeGen *g, IrExecutableGen *executable, - IrInstGenAtomicLoad *instruction) -{ - LLVMAtomicOrdering ordering = to_LLVMAtomicOrdering(instruction->ordering); - LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr); - - ZigType *operand_type = instruction->ptr->value->type->data.pointer.child_type; - LLVMTypeRef actual_abi_type = get_atomic_abi_type(g, instruction->ptr); - if (actual_abi_type != nullptr) { - // operand needs widening and truncating - ptr = LLVMBuildBitCast(g->builder, ptr, - LLVMPointerType(actual_abi_type, 0), ""); - LLVMValueRef load_inst = gen_load(g, ptr, instruction->ptr->value->type, ""); - LLVMSetOrdering(load_inst, ordering); - return LLVMBuildTrunc(g->builder, load_inst, get_llvm_type(g, operand_type), ""); - } - LLVMValueRef load_inst = gen_load(g, ptr, instruction->ptr->value->type, ""); - LLVMSetOrdering(load_inst, ordering); - return load_inst; -} - -static LLVMValueRef ir_render_atomic_store(CodeGen *g, IrExecutableGen *executable, - IrInstGenAtomicStore *instruction) -{ - LLVMAtomicOrdering ordering = to_LLVMAtomicOrdering(instruction->ordering); - LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr); - LLVMValueRef value = ir_llvm_value(g, instruction->value); - - LLVMTypeRef actual_abi_type = get_atomic_abi_type(g, instruction->ptr); - if (actual_abi_type != nullptr) { - // operand needs widening - ptr = LLVMBuildBitCast(g->builder, ptr, - LLVMPointerType(actual_abi_type, 0), ""); - if (instruction->value->value->type->data.integral.is_signed) { - value = LLVMBuildSExt(g->builder, value, actual_abi_type, ""); - } else { - value = LLVMBuildZExt(g->builder, value, actual_abi_type, ""); - } - } - LLVMValueRef store_inst = gen_store(g, value, ptr, instruction->ptr->value->type); - LLVMSetOrdering(store_inst, ordering); - return nullptr; -} - -static LLVMValueRef ir_render_float_op(CodeGen *g, IrExecutableGen *executable, IrInstGenFloatOp *instruction) { - LLVMValueRef operand = ir_llvm_value(g, instruction->operand); - LLVMValueRef fn_val = get_float_fn(g, instruction->base.value->type, ZigLLVMFnIdFloatOp, instruction->fn_id); - return LLVMBuildCall(g->builder, fn_val, &operand, 1, ""); -} - -static LLVMValueRef ir_render_mul_add(CodeGen *g, IrExecutableGen *executable, IrInstGenMulAdd *instruction) { - LLVMValueRef op1 = ir_llvm_value(g, instruction->op1); - LLVMValueRef op2 = ir_llvm_value(g, instruction->op2); - LLVMValueRef op3 = ir_llvm_value(g, instruction->op3); - assert(instruction->base.value->type->id == ZigTypeIdFloat || - instruction->base.value->type->id == ZigTypeIdVector); - LLVMValueRef fn_val = get_float_fn(g, instruction->base.value->type, ZigLLVMFnIdFMA, BuiltinFnIdMulAdd); - LLVMValueRef args[3] = { - op1, - op2, - op3, - }; - return LLVMBuildCall(g->builder, fn_val, args, 3, ""); -} - -static LLVMValueRef ir_render_bswap(CodeGen *g, IrExecutableGen *executable, IrInstGenBswap *instruction) { - LLVMValueRef op = ir_llvm_value(g, instruction->op); - ZigType *expr_type = instruction->base.value->type; - bool is_vector = expr_type->id == ZigTypeIdVector; - ZigType *int_type = is_vector ? expr_type->data.vector.elem_type : expr_type; - assert(int_type->id == ZigTypeIdInt); - if (int_type->data.integral.bit_count % 16 == 0) { - LLVMValueRef fn_val = get_int_builtin_fn(g, expr_type, BuiltinFnIdBswap); - return LLVMBuildCall(g->builder, fn_val, &op, 1, ""); - } - // Not an even number of bytes, so we zext 1 byte, then bswap, shift right 1 byte, truncate - ZigType *extended_type = get_int_type(g, int_type->data.integral.is_signed, - int_type->data.integral.bit_count + 8); - LLVMValueRef shift_amt = LLVMConstInt(get_llvm_type(g, extended_type), 8, false); - if (is_vector) { - extended_type = get_vector_type(g, expr_type->data.vector.len, extended_type); - LLVMValueRef *values = heap::c_allocator.allocate_nonzero(expr_type->data.vector.len); - for (uint32_t i = 0; i < expr_type->data.vector.len; i += 1) { - values[i] = shift_amt; - } - shift_amt = LLVMConstVector(values, expr_type->data.vector.len); - heap::c_allocator.deallocate(values, expr_type->data.vector.len); - } - // aabbcc - LLVMValueRef extended = LLVMBuildZExt(g->builder, op, get_llvm_type(g, extended_type), ""); - // 00aabbcc - LLVMValueRef fn_val = get_int_builtin_fn(g, extended_type, BuiltinFnIdBswap); - LLVMValueRef swapped = LLVMBuildCall(g->builder, fn_val, &extended, 1, ""); - // ccbbaa00 - LLVMValueRef shifted = ZigLLVMBuildLShrExact(g->builder, swapped, shift_amt, ""); - // 00ccbbaa - return LLVMBuildTrunc(g->builder, shifted, get_llvm_type(g, expr_type), ""); -} - -static LLVMValueRef ir_render_bit_reverse(CodeGen *g, IrExecutableGen *executable, IrInstGenBitReverse *instruction) { - LLVMValueRef op = ir_llvm_value(g, instruction->op); - ZigType *int_type = instruction->base.value->type; - assert(int_type->id == ZigTypeIdInt); - LLVMValueRef fn_val = get_int_builtin_fn(g, instruction->base.value->type, BuiltinFnIdBitReverse); - return LLVMBuildCall(g->builder, fn_val, &op, 1, ""); -} - -static LLVMValueRef ir_render_vector_to_array(CodeGen *g, IrExecutableGen *executable, - IrInstGenVectorToArray *instruction) -{ - ZigType *array_type = instruction->base.value->type; - assert(array_type->id == ZigTypeIdArray); - assert(handle_is_ptr(g, array_type)); - LLVMValueRef result_loc = ir_llvm_value(g, instruction->result_loc); - LLVMValueRef vector = ir_llvm_value(g, instruction->vector); - - ZigType *elem_type = array_type->data.array.child_type; - bool bitcast_ok = elem_type->size_in_bits == elem_type->abi_size * 8; - if (bitcast_ok) { - LLVMValueRef casted_ptr = LLVMBuildBitCast(g->builder, result_loc, - LLVMPointerType(get_llvm_type(g, instruction->vector->value->type), 0), ""); - uint32_t alignment = get_ptr_align(g, instruction->result_loc->value->type); - gen_store_untyped(g, vector, casted_ptr, alignment, false); - } else { - // If the ABI size of the element type is not evenly divisible by size_in_bits, a simple bitcast - // will not work, and we fall back to extractelement. - LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; - LLVMTypeRef u32_type_ref = LLVMInt32Type(); - LLVMValueRef zero = LLVMConstInt(usize_type_ref, 0, false); - for (uintptr_t i = 0; i < instruction->vector->value->type->data.vector.len; i++) { - LLVMValueRef index_usize = LLVMConstInt(usize_type_ref, i, false); - LLVMValueRef index_u32 = LLVMConstInt(u32_type_ref, i, false); - LLVMValueRef indexes[] = { zero, index_usize }; - LLVMValueRef elem_ptr = LLVMBuildInBoundsGEP(g->builder, result_loc, indexes, 2, ""); - LLVMValueRef elem = LLVMBuildExtractElement(g->builder, vector, index_u32, ""); - LLVMBuildStore(g->builder, elem, elem_ptr); - } - } - return result_loc; -} - -static LLVMValueRef ir_render_array_to_vector(CodeGen *g, IrExecutableGen *executable, - IrInstGenArrayToVector *instruction) -{ - ZigType *vector_type = instruction->base.value->type; - assert(vector_type->id == ZigTypeIdVector); - assert(!handle_is_ptr(g, vector_type)); - LLVMValueRef array_ptr = ir_llvm_value(g, instruction->array); - LLVMTypeRef vector_type_ref = get_llvm_type(g, vector_type); - - ZigType *elem_type = vector_type->data.vector.elem_type; - bool bitcast_ok = elem_type->size_in_bits == elem_type->abi_size * 8; - if (bitcast_ok) { - LLVMValueRef casted_ptr = LLVMBuildBitCast(g->builder, array_ptr, - LLVMPointerType(vector_type_ref, 0), ""); - ZigType *array_type = instruction->array->value->type; - assert(array_type->id == ZigTypeIdArray); - uint32_t alignment = get_abi_alignment(g, array_type->data.array.child_type); - return gen_load_untyped(g, casted_ptr, alignment, false, ""); - } else { - // If the ABI size of the element type is not evenly divisible by size_in_bits, a simple bitcast - // will not work, and we fall back to insertelement. - LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; - LLVMTypeRef u32_type_ref = LLVMInt32Type(); - LLVMValueRef zero = LLVMConstInt(usize_type_ref, 0, false); - LLVMValueRef vector = LLVMGetUndef(vector_type_ref); - for (uintptr_t i = 0; i < instruction->base.value->type->data.vector.len; i++) { - LLVMValueRef index_usize = LLVMConstInt(usize_type_ref, i, false); - LLVMValueRef index_u32 = LLVMConstInt(u32_type_ref, i, false); - LLVMValueRef indexes[] = { zero, index_usize }; - LLVMValueRef elem_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indexes, 2, ""); - LLVMValueRef elem = LLVMBuildLoad(g->builder, elem_ptr, ""); - vector = LLVMBuildInsertElement(g->builder, vector, elem, index_u32, ""); - } - return vector; - } -} - -static LLVMValueRef ir_render_assert_zero(CodeGen *g, IrExecutableGen *executable, - IrInstGenAssertZero *instruction) -{ - LLVMValueRef target = ir_llvm_value(g, instruction->target); - ZigType *int_type = instruction->target->value->type; - if (ir_want_runtime_safety(g, &instruction->base)) { - return gen_assert_zero(g, target, int_type); - } - return nullptr; -} - -static LLVMValueRef ir_render_assert_non_null(CodeGen *g, IrExecutableGen *executable, - IrInstGenAssertNonNull *instruction) -{ - LLVMValueRef target = ir_llvm_value(g, instruction->target); - ZigType *target_type = instruction->target->value->type; - - if (target_type->id == ZigTypeIdPointer) { - assert(target_type->data.pointer.ptr_len == PtrLenC); - LLVMValueRef non_null_bit = LLVMBuildICmp(g->builder, LLVMIntNE, target, - LLVMConstNull(get_llvm_type(g, target_type)), ""); - - LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "AssertNonNullFail"); - LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "AssertNonNullOk"); - LLVMBuildCondBr(g->builder, non_null_bit, ok_block, fail_block); - - LLVMPositionBuilderAtEnd(g->builder, fail_block); - gen_assertion(g, PanicMsgIdUnwrapOptionalFail, &instruction->base); - - LLVMPositionBuilderAtEnd(g->builder, ok_block); - } else { - zig_unreachable(); - } - return nullptr; -} - -static LLVMValueRef ir_render_suspend_begin(CodeGen *g, IrExecutableGen *executable, - IrInstGenSuspendBegin *instruction) -{ - if (fn_is_async(g->cur_fn)) { - instruction->resume_bb = gen_suspend_begin(g, "SuspendResume"); - } - return nullptr; -} - -static LLVMValueRef ir_render_suspend_finish(CodeGen *g, IrExecutableGen *executable, - IrInstGenSuspendFinish *instruction) -{ - LLVMBuildRetVoid(g->builder); - - LLVMPositionBuilderAtEnd(g->builder, instruction->begin->resume_bb); - if (ir_want_runtime_safety(g, &instruction->base)) { - LLVMBuildStore(g->builder, g->cur_bad_not_suspended_index, g->cur_async_resume_index_ptr); - } - render_async_var_decls(g, instruction->base.base.scope); - return nullptr; -} - -static LLVMValueRef gen_await_early_return(CodeGen *g, IrInstGen *source_instr, - LLVMValueRef target_frame_ptr, ZigType *result_type, ZigType *ptr_result_type, - LLVMValueRef result_loc, bool non_async) -{ - LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; - LLVMValueRef their_result_ptr = nullptr; - if (type_has_bits(g, result_type) && (non_async || result_loc != nullptr)) { - LLVMValueRef their_result_ptr_ptr = LLVMBuildStructGEP(g->builder, target_frame_ptr, frame_ret_start, ""); - their_result_ptr = LLVMBuildLoad(g->builder, their_result_ptr_ptr, ""); - if (result_loc != nullptr) { - LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0); - LLVMValueRef dest_ptr_casted = LLVMBuildBitCast(g->builder, result_loc, ptr_u8, ""); - LLVMValueRef src_ptr_casted = LLVMBuildBitCast(g->builder, their_result_ptr, ptr_u8, ""); - bool is_volatile = false; - uint32_t abi_align = get_abi_alignment(g, result_type); - LLVMValueRef byte_count_val = LLVMConstInt(usize_type_ref, type_size(g, result_type), false); - ZigLLVMBuildMemCpy(g->builder, - dest_ptr_casted, abi_align, - src_ptr_casted, abi_align, byte_count_val, is_volatile); - } - } - if (codegen_fn_has_err_ret_tracing_arg(g, result_type)) { - LLVMValueRef their_trace_ptr_ptr = LLVMBuildStructGEP(g->builder, target_frame_ptr, - frame_index_trace_arg(g, result_type), ""); - LLVMValueRef src_trace_ptr = LLVMBuildLoad(g->builder, their_trace_ptr_ptr, ""); - bool is_llvm_alloca; - LLVMValueRef dest_trace_ptr = get_cur_err_ret_trace_val(g, source_instr->base.scope, &is_llvm_alloca); - LLVMValueRef args[] = { dest_trace_ptr, src_trace_ptr }; - ZigLLVMBuildCall(g->builder, get_merge_err_ret_traces_fn_val(g), args, 2, - get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_CallAttrAuto, ""); - } - if (non_async && type_has_bits(g, result_type)) { - LLVMValueRef result_ptr = (result_loc == nullptr) ? their_result_ptr : result_loc; - return get_handle_value(g, result_ptr, result_type, ptr_result_type); - } else { - return nullptr; - } -} - -static LLVMValueRef ir_render_await(CodeGen *g, IrExecutableGen *executable, IrInstGenAwait *instruction) { - LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; - LLVMValueRef zero = LLVMConstNull(usize_type_ref); - LLVMValueRef target_frame_ptr = ir_llvm_value(g, instruction->frame); - ZigType *result_type = instruction->base.value->type; - ZigType *ptr_result_type = get_pointer_to_type(g, result_type, true); - - LLVMValueRef result_loc = (instruction->result_loc == nullptr) ? - nullptr : ir_llvm_value(g, instruction->result_loc); - - if (instruction->is_nosuspend || - (instruction->target_fn != nullptr && !fn_is_async(instruction->target_fn))) - { - return gen_await_early_return(g, &instruction->base, target_frame_ptr, result_type, - ptr_result_type, result_loc, true); - } - - // Prepare to be suspended - LLVMBasicBlockRef resume_bb = gen_suspend_begin(g, "AwaitResume"); - LLVMBasicBlockRef end_bb = LLVMAppendBasicBlock(g->cur_fn_val, "AwaitEnd"); - - // At this point resuming the function will continue from resume_bb. - // This code is as if it is running inside the suspend block. - - // supply the awaiter return pointer - if (type_has_bits(g, result_type)) { - LLVMValueRef awaiter_ret_ptr_ptr = LLVMBuildStructGEP(g->builder, target_frame_ptr, frame_ret_start + 1, ""); - if (result_loc == nullptr) { - // no copy needed - LLVMBuildStore(g->builder, LLVMConstNull(LLVMGetElementType(LLVMTypeOf(awaiter_ret_ptr_ptr))), - awaiter_ret_ptr_ptr); - } else { - LLVMBuildStore(g->builder, result_loc, awaiter_ret_ptr_ptr); - } - } - - // supply the error return trace pointer - if (codegen_fn_has_err_ret_tracing_arg(g, result_type)) { - bool is_llvm_alloca; - LLVMValueRef my_err_ret_trace_val = get_cur_err_ret_trace_val(g, instruction->base.base.scope, &is_llvm_alloca); - assert(my_err_ret_trace_val != nullptr); - LLVMValueRef err_ret_trace_ptr_ptr = LLVMBuildStructGEP(g->builder, target_frame_ptr, - frame_index_trace_arg(g, result_type) + 1, ""); - LLVMBuildStore(g->builder, my_err_ret_trace_val, err_ret_trace_ptr_ptr); - } - - // caller's own frame pointer - LLVMValueRef awaiter_init_val = LLVMBuildPtrToInt(g->builder, g->cur_frame_ptr, usize_type_ref, ""); - LLVMValueRef awaiter_ptr = LLVMBuildStructGEP(g->builder, target_frame_ptr, frame_awaiter_index, ""); - LLVMValueRef prev_val = gen_maybe_atomic_op(g, LLVMAtomicRMWBinOpXchg, awaiter_ptr, awaiter_init_val, - LLVMAtomicOrderingRelease); - - LLVMBasicBlockRef bad_await_block = LLVMAppendBasicBlock(g->cur_fn_val, "BadAwait"); - LLVMBasicBlockRef complete_suspend_block = LLVMAppendBasicBlock(g->cur_fn_val, "CompleteSuspend"); - LLVMBasicBlockRef early_return_block = LLVMAppendBasicBlock(g->cur_fn_val, "EarlyReturn"); - - LLVMValueRef all_ones = LLVMConstAllOnes(usize_type_ref); - LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, prev_val, bad_await_block, 2); - - LLVMAddCase(switch_instr, zero, complete_suspend_block); - LLVMAddCase(switch_instr, all_ones, early_return_block); - - // We discovered that another awaiter was already here. - LLVMPositionBuilderAtEnd(g->builder, bad_await_block); - gen_assertion(g, PanicMsgIdBadAwait, &instruction->base); - - // Rely on the target to resume us from suspension. - LLVMPositionBuilderAtEnd(g->builder, complete_suspend_block); - LLVMBuildRetVoid(g->builder); - - // Early return: The async function has already completed. We must copy the result and - // the error return trace if applicable. - LLVMPositionBuilderAtEnd(g->builder, early_return_block); - gen_await_early_return(g, &instruction->base, target_frame_ptr, result_type, ptr_result_type, - result_loc, false); - LLVMBuildBr(g->builder, end_bb); - - LLVMPositionBuilderAtEnd(g->builder, resume_bb); - gen_assert_resume_id(g, &instruction->base, ResumeIdReturn, PanicMsgIdResumedAnAwaitingFn, nullptr); - LLVMBuildBr(g->builder, end_bb); - - LLVMPositionBuilderAtEnd(g->builder, end_bb); - // Rely on the spill for the llvm_value to be populated. - // See the implementation of ir_llvm_value. - return nullptr; -} - -static LLVMValueRef ir_render_resume(CodeGen *g, IrExecutableGen *executable, IrInstGenResume *instruction) { - LLVMValueRef frame = ir_llvm_value(g, instruction->frame); - ZigType *frame_type = instruction->frame->value->type; - assert(frame_type->id == ZigTypeIdAnyFrame); - - gen_resume(g, nullptr, frame, ResumeIdManual); - return nullptr; -} - -static LLVMValueRef ir_render_frame_size(CodeGen *g, IrExecutableGen *executable, - IrInstGenFrameSize *instruction) -{ - LLVMValueRef fn_val = ir_llvm_value(g, instruction->fn); - return gen_frame_size(g, fn_val); -} - -static LLVMValueRef ir_render_spill_begin(CodeGen *g, IrExecutableGen *executable, - IrInstGenSpillBegin *instruction) -{ - if (!fn_is_async(g->cur_fn)) - return nullptr; - - switch (instruction->spill_id) { - case SpillIdInvalid: - zig_unreachable(); - case SpillIdRetErrCode: { - LLVMValueRef operand = ir_llvm_value(g, instruction->operand); - LLVMValueRef ptr = ir_llvm_value(g, g->cur_fn->err_code_spill); - LLVMBuildStore(g->builder, operand, ptr); - return nullptr; - } - - } - zig_unreachable(); -} - -static LLVMValueRef ir_render_spill_end(CodeGen *g, IrExecutableGen *executable, IrInstGenSpillEnd *instruction) { - if (!fn_is_async(g->cur_fn)) - return ir_llvm_value(g, instruction->begin->operand); - - switch (instruction->begin->spill_id) { - case SpillIdInvalid: - zig_unreachable(); - case SpillIdRetErrCode: { - LLVMValueRef ptr = ir_llvm_value(g, g->cur_fn->err_code_spill); - return LLVMBuildLoad(g->builder, ptr, ""); - } - - } - zig_unreachable(); -} - -static LLVMValueRef ir_render_vector_extract_elem(CodeGen *g, IrExecutableGen *executable, - IrInstGenVectorExtractElem *instruction) -{ - LLVMValueRef vector = ir_llvm_value(g, instruction->vector); - LLVMValueRef index = ir_llvm_value(g, instruction->index); - return LLVMBuildExtractElement(g->builder, vector, index, ""); -} - -static void set_debug_location(CodeGen *g, IrInstGen *instruction) { - AstNode *source_node = instruction->base.source_node; - Scope *scope = instruction->base.scope; - - assert(source_node); - assert(scope); - - ZigLLVMSetCurrentDebugLocation(g->builder, (int)source_node->line + 1, - (int)source_node->column + 1, get_di_scope(g, scope)); -} - -static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutableGen *executable, IrInstGen *instruction) { - switch (instruction->id) { - case IrInstGenIdInvalid: - case IrInstGenIdConst: - case IrInstGenIdAlloca: - zig_unreachable(); - - case IrInstGenIdDeclVar: - return ir_render_decl_var(g, executable, (IrInstGenDeclVar *)instruction); - case IrInstGenIdReturn: - return ir_render_return(g, executable, (IrInstGenReturn *)instruction); - case IrInstGenIdBinOp: - return ir_render_bin_op(g, executable, (IrInstGenBinOp *)instruction); - case IrInstGenIdCast: - return ir_render_cast(g, executable, (IrInstGenCast *)instruction); - case IrInstGenIdUnreachable: - return ir_render_unreachable(g, executable, (IrInstGenUnreachable *)instruction); - case IrInstGenIdCondBr: - return ir_render_cond_br(g, executable, (IrInstGenCondBr *)instruction); - case IrInstGenIdBr: - return ir_render_br(g, executable, (IrInstGenBr *)instruction); - case IrInstGenIdBinaryNot: - return ir_render_binary_not(g, executable, (IrInstGenBinaryNot *)instruction); - case IrInstGenIdNegation: - return ir_render_negation(g, executable, (IrInstGenNegation *)instruction); - case IrInstGenIdNegationWrapping: - return ir_render_negation_wrapping(g, executable, (IrInstGenNegationWrapping *)instruction); - case IrInstGenIdLoadPtr: - return ir_render_load_ptr(g, executable, (IrInstGenLoadPtr *)instruction); - case IrInstGenIdStorePtr: - return ir_render_store_ptr(g, executable, (IrInstGenStorePtr *)instruction); - case IrInstGenIdVectorStoreElem: - return ir_render_vector_store_elem(g, executable, (IrInstGenVectorStoreElem *)instruction); - case IrInstGenIdVarPtr: - return ir_render_var_ptr(g, executable, (IrInstGenVarPtr *)instruction); - case IrInstGenIdReturnPtr: - return ir_render_return_ptr(g, executable, (IrInstGenReturnPtr *)instruction); - case IrInstGenIdElemPtr: - return ir_render_elem_ptr(g, executable, (IrInstGenElemPtr *)instruction); - case IrInstGenIdCall: - return ir_render_call(g, executable, (IrInstGenCall *)instruction); - case IrInstGenIdStructFieldPtr: - return ir_render_struct_field_ptr(g, executable, (IrInstGenStructFieldPtr *)instruction); - case IrInstGenIdUnionFieldPtr: - return ir_render_union_field_ptr(g, executable, (IrInstGenUnionFieldPtr *)instruction); - case IrInstGenIdAsm: - return ir_render_asm_gen(g, executable, (IrInstGenAsm *)instruction); - case IrInstGenIdTestNonNull: - return ir_render_test_non_null(g, executable, (IrInstGenTestNonNull *)instruction); - case IrInstGenIdOptionalUnwrapPtr: - return ir_render_optional_unwrap_ptr(g, executable, (IrInstGenOptionalUnwrapPtr *)instruction); - case IrInstGenIdClz: - return ir_render_clz(g, executable, (IrInstGenClz *)instruction); - case IrInstGenIdCtz: - return ir_render_ctz(g, executable, (IrInstGenCtz *)instruction); - case IrInstGenIdPopCount: - return ir_render_pop_count(g, executable, (IrInstGenPopCount *)instruction); - case IrInstGenIdSwitchBr: - return ir_render_switch_br(g, executable, (IrInstGenSwitchBr *)instruction); - case IrInstGenIdBswap: - return ir_render_bswap(g, executable, (IrInstGenBswap *)instruction); - case IrInstGenIdBitReverse: - return ir_render_bit_reverse(g, executable, (IrInstGenBitReverse *)instruction); - case IrInstGenIdPhi: - return ir_render_phi(g, executable, (IrInstGenPhi *)instruction); - case IrInstGenIdRef: - return ir_render_ref(g, executable, (IrInstGenRef *)instruction); - case IrInstGenIdErrName: - return ir_render_err_name(g, executable, (IrInstGenErrName *)instruction); - case IrInstGenIdCmpxchg: - return ir_render_cmpxchg(g, executable, (IrInstGenCmpxchg *)instruction); - case IrInstGenIdFence: - return ir_render_fence(g, executable, (IrInstGenFence *)instruction); - case IrInstGenIdTruncate: - return ir_render_truncate(g, executable, (IrInstGenTruncate *)instruction); - case IrInstGenIdBoolNot: - return ir_render_bool_not(g, executable, (IrInstGenBoolNot *)instruction); - case IrInstGenIdMemset: - return ir_render_memset(g, executable, (IrInstGenMemset *)instruction); - case IrInstGenIdMemcpy: - return ir_render_memcpy(g, executable, (IrInstGenMemcpy *)instruction); - case IrInstGenIdSlice: - return ir_render_slice(g, executable, (IrInstGenSlice *)instruction); - case IrInstGenIdBreakpoint: - return ir_render_breakpoint(g, executable, (IrInstGenBreakpoint *)instruction); - case IrInstGenIdReturnAddress: - return ir_render_return_address(g, executable, (IrInstGenReturnAddress *)instruction); - case IrInstGenIdFrameAddress: - return ir_render_frame_address(g, executable, (IrInstGenFrameAddress *)instruction); - case IrInstGenIdFrameHandle: - return ir_render_handle(g, executable, (IrInstGenFrameHandle *)instruction); - case IrInstGenIdOverflowOp: - return ir_render_overflow_op(g, executable, (IrInstGenOverflowOp *)instruction); - case IrInstGenIdTestErr: - return ir_render_test_err(g, executable, (IrInstGenTestErr *)instruction); - case IrInstGenIdUnwrapErrCode: - return ir_render_unwrap_err_code(g, executable, (IrInstGenUnwrapErrCode *)instruction); - case IrInstGenIdUnwrapErrPayload: - return ir_render_unwrap_err_payload(g, executable, (IrInstGenUnwrapErrPayload *)instruction); - case IrInstGenIdOptionalWrap: - return ir_render_optional_wrap(g, executable, (IrInstGenOptionalWrap *)instruction); - case IrInstGenIdErrWrapCode: - return ir_render_err_wrap_code(g, executable, (IrInstGenErrWrapCode *)instruction); - case IrInstGenIdErrWrapPayload: - return ir_render_err_wrap_payload(g, executable, (IrInstGenErrWrapPayload *)instruction); - case IrInstGenIdUnionTag: - return ir_render_union_tag(g, executable, (IrInstGenUnionTag *)instruction); - case IrInstGenIdPtrCast: - return ir_render_ptr_cast(g, executable, (IrInstGenPtrCast *)instruction); - case IrInstGenIdBitCast: - return ir_render_bit_cast(g, executable, (IrInstGenBitCast *)instruction); - case IrInstGenIdWidenOrShorten: - return ir_render_widen_or_shorten(g, executable, (IrInstGenWidenOrShorten *)instruction); - case IrInstGenIdPtrToInt: - return ir_render_ptr_to_int(g, executable, (IrInstGenPtrToInt *)instruction); - case IrInstGenIdIntToPtr: - return ir_render_int_to_ptr(g, executable, (IrInstGenIntToPtr *)instruction); - case IrInstGenIdIntToEnum: - return ir_render_int_to_enum(g, executable, (IrInstGenIntToEnum *)instruction); - case IrInstGenIdIntToErr: - return ir_render_int_to_err(g, executable, (IrInstGenIntToErr *)instruction); - case IrInstGenIdErrToInt: - return ir_render_err_to_int(g, executable, (IrInstGenErrToInt *)instruction); - case IrInstGenIdPanic: - return ir_render_panic(g, executable, (IrInstGenPanic *)instruction); - case IrInstGenIdTagName: - return ir_render_enum_tag_name(g, executable, (IrInstGenTagName *)instruction); - case IrInstGenIdFieldParentPtr: - return ir_render_field_parent_ptr(g, executable, (IrInstGenFieldParentPtr *)instruction); - case IrInstGenIdAlignCast: - return ir_render_align_cast(g, executable, (IrInstGenAlignCast *)instruction); - case IrInstGenIdErrorReturnTrace: - return ir_render_error_return_trace(g, executable, (IrInstGenErrorReturnTrace *)instruction); - case IrInstGenIdAtomicRmw: - return ir_render_atomic_rmw(g, executable, (IrInstGenAtomicRmw *)instruction); - case IrInstGenIdAtomicLoad: - return ir_render_atomic_load(g, executable, (IrInstGenAtomicLoad *)instruction); - case IrInstGenIdAtomicStore: - return ir_render_atomic_store(g, executable, (IrInstGenAtomicStore *)instruction); - case IrInstGenIdSaveErrRetAddr: - return ir_render_save_err_ret_addr(g, executable, (IrInstGenSaveErrRetAddr *)instruction); - case IrInstGenIdFloatOp: - return ir_render_float_op(g, executable, (IrInstGenFloatOp *)instruction); - case IrInstGenIdMulAdd: - return ir_render_mul_add(g, executable, (IrInstGenMulAdd *)instruction); - case IrInstGenIdArrayToVector: - return ir_render_array_to_vector(g, executable, (IrInstGenArrayToVector *)instruction); - case IrInstGenIdVectorToArray: - return ir_render_vector_to_array(g, executable, (IrInstGenVectorToArray *)instruction); - case IrInstGenIdAssertZero: - return ir_render_assert_zero(g, executable, (IrInstGenAssertZero *)instruction); - case IrInstGenIdAssertNonNull: - return ir_render_assert_non_null(g, executable, (IrInstGenAssertNonNull *)instruction); - case IrInstGenIdPtrOfArrayToSlice: - return ir_render_ptr_of_array_to_slice(g, executable, (IrInstGenPtrOfArrayToSlice *)instruction); - case IrInstGenIdSuspendBegin: - return ir_render_suspend_begin(g, executable, (IrInstGenSuspendBegin *)instruction); - case IrInstGenIdSuspendFinish: - return ir_render_suspend_finish(g, executable, (IrInstGenSuspendFinish *)instruction); - case IrInstGenIdResume: - return ir_render_resume(g, executable, (IrInstGenResume *)instruction); - case IrInstGenIdFrameSize: - return ir_render_frame_size(g, executable, (IrInstGenFrameSize *)instruction); - case IrInstGenIdAwait: - return ir_render_await(g, executable, (IrInstGenAwait *)instruction); - case IrInstGenIdSpillBegin: - return ir_render_spill_begin(g, executable, (IrInstGenSpillBegin *)instruction); - case IrInstGenIdSpillEnd: - return ir_render_spill_end(g, executable, (IrInstGenSpillEnd *)instruction); - case IrInstGenIdShuffleVector: - return ir_render_shuffle_vector(g, executable, (IrInstGenShuffleVector *) instruction); - case IrInstGenIdSplat: - return ir_render_splat(g, executable, (IrInstGenSplat *) instruction); - case IrInstGenIdVectorExtractElem: - return ir_render_vector_extract_elem(g, executable, (IrInstGenVectorExtractElem *) instruction); - case IrInstGenIdWasmMemorySize: - return ir_render_wasm_memory_size(g, executable, (IrInstGenWasmMemorySize *) instruction); - case IrInstGenIdWasmMemoryGrow: - return ir_render_wasm_memory_grow(g, executable, (IrInstGenWasmMemoryGrow *) instruction); - } - zig_unreachable(); -} - -static void ir_render(CodeGen *g, ZigFn *fn_entry) { - assert(fn_entry); - - IrExecutableGen *executable = &fn_entry->analyzed_executable; - assert(executable->basic_block_list.length > 0); - - for (size_t block_i = 0; block_i < executable->basic_block_list.length; block_i += 1) { - IrBasicBlockGen *current_block = executable->basic_block_list.at(block_i); - if (get_scope_typeof(current_block->scope) != nullptr) { - LLVMBuildBr(g->builder, current_block->llvm_block); - } - assert(current_block->llvm_block); - LLVMPositionBuilderAtEnd(g->builder, current_block->llvm_block); - for (size_t instr_i = 0; instr_i < current_block->instruction_list.length; instr_i += 1) { - IrInstGen *instruction = current_block->instruction_list.at(instr_i); - if (instruction->base.ref_count == 0 && !ir_inst_gen_has_side_effects(instruction)) - continue; - if (get_scope_typeof(instruction->base.scope) != nullptr) - continue; - - if (!g->strip_debug_symbols) { - set_debug_location(g, instruction); - } - instruction->llvm_value = ir_render_instruction(g, executable, instruction); - if (instruction->spill != nullptr && instruction->llvm_value != nullptr) { - LLVMValueRef spill_ptr = ir_llvm_value(g, instruction->spill); - gen_assign_raw(g, spill_ptr, instruction->spill->value->type, instruction->llvm_value); - instruction->llvm_value = nullptr; - } - } - current_block->llvm_exit_block = LLVMGetInsertBlock(g->builder); - } -} - -static LLVMValueRef gen_const_ptr_struct_recursive(CodeGen *g, ZigValue *struct_const_val, size_t field_index); -static LLVMValueRef gen_const_ptr_array_recursive(CodeGen *g, ZigValue *array_const_val, size_t index); -static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ZigValue *union_const_val); -static LLVMValueRef gen_const_ptr_err_union_code_recursive(CodeGen *g, ZigValue *err_union_const_val); -static LLVMValueRef gen_const_ptr_err_union_payload_recursive(CodeGen *g, ZigValue *err_union_const_val); -static LLVMValueRef gen_const_ptr_optional_payload_recursive(CodeGen *g, ZigValue *optional_const_val); - -static LLVMValueRef gen_parent_ptr(CodeGen *g, ZigValue *val, ConstParent *parent) { - switch (parent->id) { - case ConstParentIdNone: - render_const_val(g, val, ""); - render_const_val_global(g, val, ""); - return val->llvm_global; - case ConstParentIdStruct: - return gen_const_ptr_struct_recursive(g, parent->data.p_struct.struct_val, - parent->data.p_struct.field_index); - case ConstParentIdErrUnionCode: - return gen_const_ptr_err_union_code_recursive(g, parent->data.p_err_union_code.err_union_val); - case ConstParentIdErrUnionPayload: - return gen_const_ptr_err_union_payload_recursive(g, parent->data.p_err_union_payload.err_union_val); - case ConstParentIdOptionalPayload: - return gen_const_ptr_optional_payload_recursive(g, parent->data.p_optional_payload.optional_val); - case ConstParentIdArray: - return gen_const_ptr_array_recursive(g, parent->data.p_array.array_val, - parent->data.p_array.elem_index); - case ConstParentIdUnion: - return gen_const_ptr_union_recursive(g, parent->data.p_union.union_val); - case ConstParentIdScalar: - render_const_val(g, parent->data.p_scalar.scalar_val, ""); - render_const_val_global(g, parent->data.p_scalar.scalar_val, ""); - return parent->data.p_scalar.scalar_val->llvm_global; - } - zig_unreachable(); -} - -static LLVMValueRef gen_const_ptr_array_recursive(CodeGen *g, ZigValue *array_const_val, size_t index) { - expand_undef_array(g, array_const_val); - ConstParent *parent = &array_const_val->parent; - LLVMValueRef base_ptr = gen_parent_ptr(g, array_const_val, parent); - - LLVMTypeKind el_type = LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(base_ptr))); - if (el_type == LLVMArrayTypeKind) { - ZigType *usize = g->builtin_types.entry_usize; - LLVMValueRef indices[] = { - LLVMConstNull(usize->llvm_type), - LLVMConstInt(usize->llvm_type, index, false), - }; - return LLVMConstInBoundsGEP(base_ptr, indices, 2); - } else if (el_type == LLVMStructTypeKind) { - ZigType *u32 = g->builtin_types.entry_u32; - LLVMValueRef indices[] = { - LLVMConstNull(get_llvm_type(g, u32)), - LLVMConstInt(get_llvm_type(g, u32), index, false), - }; - return LLVMConstInBoundsGEP(base_ptr, indices, 2); - } else { - return base_ptr; - } -} - -static LLVMValueRef gen_const_ptr_struct_recursive(CodeGen *g, ZigValue *struct_const_val, size_t field_index) { - ConstParent *parent = &struct_const_val->parent; - LLVMValueRef base_ptr = gen_parent_ptr(g, struct_const_val, parent); - - ZigType *u32 = g->builtin_types.entry_u32; - LLVMValueRef indices[] = { - LLVMConstNull(get_llvm_type(g, u32)), - LLVMConstInt(get_llvm_type(g, u32), field_index, false), - }; - return LLVMConstInBoundsGEP(base_ptr, indices, 2); -} - -static LLVMValueRef gen_const_ptr_err_union_code_recursive(CodeGen *g, ZigValue *err_union_const_val) { - ConstParent *parent = &err_union_const_val->parent; - LLVMValueRef base_ptr = gen_parent_ptr(g, err_union_const_val, parent); - - ZigType *u32 = g->builtin_types.entry_u32; - LLVMValueRef indices[] = { - LLVMConstNull(get_llvm_type(g, u32)), - LLVMConstInt(get_llvm_type(g, u32), err_union_err_index, false), - }; - return LLVMConstInBoundsGEP(base_ptr, indices, 2); -} - -static LLVMValueRef gen_const_ptr_err_union_payload_recursive(CodeGen *g, ZigValue *err_union_const_val) { - ConstParent *parent = &err_union_const_val->parent; - LLVMValueRef base_ptr = gen_parent_ptr(g, err_union_const_val, parent); - - ZigType *u32 = g->builtin_types.entry_u32; - LLVMValueRef indices[] = { - LLVMConstNull(get_llvm_type(g, u32)), - LLVMConstInt(get_llvm_type(g, u32), err_union_payload_index, false), - }; - return LLVMConstInBoundsGEP(base_ptr, indices, 2); -} - -static LLVMValueRef gen_const_ptr_optional_payload_recursive(CodeGen *g, ZigValue *optional_const_val) { - ConstParent *parent = &optional_const_val->parent; - LLVMValueRef base_ptr = gen_parent_ptr(g, optional_const_val, parent); - - ZigType *u32 = g->builtin_types.entry_u32; - LLVMValueRef indices[] = { - LLVMConstNull(get_llvm_type(g, u32)), - LLVMConstInt(get_llvm_type(g, u32), maybe_child_index, false), - }; - return LLVMConstInBoundsGEP(base_ptr, indices, 2); -} - -static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ZigValue *union_const_val) { - ConstParent *parent = &union_const_val->parent; - LLVMValueRef base_ptr = gen_parent_ptr(g, union_const_val, parent); - - // Slot in the structure where the payload is stored, if equal to SIZE_MAX - // the union has no tag and a single field and is collapsed into the field - // itself - size_t union_payload_index = union_const_val->type->data.unionation.gen_union_index; - - ZigType *u32 = g->builtin_types.entry_u32; - LLVMValueRef indices[] = { - LLVMConstNull(get_llvm_type(g, u32)), - LLVMConstInt(get_llvm_type(g, u32), union_payload_index, false), - }; - return LLVMConstInBoundsGEP(base_ptr, indices, (union_payload_index != SIZE_MAX) ? 2 : 1); -} - -static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, ZigValue *const_val) { - switch (const_val->special) { - case ConstValSpecialLazy: - case ConstValSpecialRuntime: - zig_unreachable(); - case ConstValSpecialUndef: - return LLVMConstInt(big_int_type_ref, 0, false); - case ConstValSpecialStatic: - break; - } - - ZigType *type_entry = const_val->type; - assert(type_has_bits(g, type_entry)); - switch (type_entry->id) { - case ZigTypeIdInvalid: - case ZigTypeIdMetaType: - case ZigTypeIdUnreachable: - case ZigTypeIdComptimeFloat: - case ZigTypeIdComptimeInt: - case ZigTypeIdEnumLiteral: - case ZigTypeIdUndefined: - case ZigTypeIdNull: - case ZigTypeIdErrorUnion: - case ZigTypeIdErrorSet: - case ZigTypeIdBoundFn: - case ZigTypeIdVoid: - case ZigTypeIdOpaque: - zig_unreachable(); - case ZigTypeIdBool: - return LLVMConstInt(big_int_type_ref, const_val->data.x_bool ? 1 : 0, false); - case ZigTypeIdEnum: - { - assert(type_entry->data.enumeration.decl_node->data.container_decl.init_arg_expr != nullptr); - LLVMValueRef int_val = gen_const_val(g, const_val, ""); - return LLVMConstZExt(int_val, big_int_type_ref); - } - case ZigTypeIdInt: - { - LLVMValueRef int_val = gen_const_val(g, const_val, ""); - return LLVMConstZExt(int_val, big_int_type_ref); - } - case ZigTypeIdFloat: - { - LLVMValueRef float_val = gen_const_val(g, const_val, ""); - LLVMValueRef int_val = LLVMConstFPToUI(float_val, - LLVMIntType((unsigned)type_entry->data.floating.bit_count)); - return LLVMConstZExt(int_val, big_int_type_ref); - } - case ZigTypeIdPointer: - case ZigTypeIdFn: - case ZigTypeIdOptional: - { - LLVMValueRef ptr_val = gen_const_val(g, const_val, ""); - LLVMValueRef ptr_size_int_val = LLVMConstPtrToInt(ptr_val, g->builtin_types.entry_usize->llvm_type); - return LLVMConstZExt(ptr_size_int_val, big_int_type_ref); - } - case ZigTypeIdArray: { - LLVMValueRef val = LLVMConstInt(big_int_type_ref, 0, false); - if (const_val->data.x_array.special == ConstArraySpecialUndef) { - return val; - } - expand_undef_array(g, const_val); - bool is_big_endian = g->is_big_endian; // TODO get endianness from struct type - uint32_t packed_bits_size = type_size_bits(g, type_entry->data.array.child_type); - size_t used_bits = 0; - for (size_t i = 0; i < type_entry->data.array.len; i += 1) { - ZigValue *elem_val = &const_val->data.x_array.data.s_none.elements[i]; - LLVMValueRef child_val = pack_const_int(g, big_int_type_ref, elem_val); - - if (is_big_endian) { - LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, packed_bits_size, false); - val = LLVMConstShl(val, shift_amt); - val = LLVMConstOr(val, child_val); - } else { - LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, used_bits, false); - LLVMValueRef child_val_shifted = LLVMConstShl(child_val, shift_amt); - val = LLVMConstOr(val, child_val_shifted); - used_bits += packed_bits_size; - } - } - - if (type_entry->data.array.sentinel != nullptr) { - ZigValue *elem_val = type_entry->data.array.sentinel; - LLVMValueRef child_val = pack_const_int(g, big_int_type_ref, elem_val); - - if (is_big_endian) { - LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, packed_bits_size, false); - val = LLVMConstShl(val, shift_amt); - val = LLVMConstOr(val, child_val); - } else { - LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, used_bits, false); - LLVMValueRef child_val_shifted = LLVMConstShl(child_val, shift_amt); - val = LLVMConstOr(val, child_val_shifted); - used_bits += packed_bits_size; - } - } - return val; - } - case ZigTypeIdVector: - zig_panic("TODO bit pack a vector"); - case ZigTypeIdUnion: - zig_panic("TODO bit pack a union"); - case ZigTypeIdStruct: - { - assert(type_entry->data.structure.layout == ContainerLayoutPacked); - bool is_big_endian = g->is_big_endian; // TODO get endianness from struct type - - LLVMValueRef val = LLVMConstInt(big_int_type_ref, 0, false); - size_t used_bits = 0; - 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->gen_index == SIZE_MAX) { - continue; - } - LLVMValueRef child_val = pack_const_int(g, big_int_type_ref, const_val->data.x_struct.fields[i]); - uint32_t packed_bits_size = type_size_bits(g, field->type_entry); - if (is_big_endian) { - LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, packed_bits_size, false); - val = LLVMConstShl(val, shift_amt); - val = LLVMConstOr(val, child_val); - } else { - LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, used_bits, false); - LLVMValueRef child_val_shifted = LLVMConstShl(child_val, shift_amt); - val = LLVMConstOr(val, child_val_shifted); - used_bits += packed_bits_size; - } - } - return val; - } - case ZigTypeIdFnFrame: - zig_panic("TODO bit pack an async function frame"); - case ZigTypeIdAnyFrame: - zig_panic("TODO bit pack an anyframe"); - } - zig_unreachable(); -} - -// We have this because union constants can't be represented by the official union type, -// and this property bubbles up in whatever aggregate type contains a union constant -static bool is_llvm_value_unnamed_type(CodeGen *g, ZigType *type_entry, LLVMValueRef val) { - return LLVMTypeOf(val) != get_llvm_type(g, type_entry); -} - -static LLVMValueRef gen_const_val_ptr(CodeGen *g, ZigValue *const_val, const char *name) { - switch (const_val->data.x_ptr.special) { - case ConstPtrSpecialInvalid: - case ConstPtrSpecialDiscard: - zig_unreachable(); - case ConstPtrSpecialRef: - { - ZigValue *pointee = const_val->data.x_ptr.data.ref.pointee; - render_const_val(g, pointee, ""); - render_const_val_global(g, pointee, ""); - const_val->llvm_value = LLVMConstBitCast(pointee->llvm_global, - get_llvm_type(g, const_val->type)); - return const_val->llvm_value; - } - case ConstPtrSpecialBaseArray: - case ConstPtrSpecialSubArray: - { - ZigValue *array_const_val = const_val->data.x_ptr.data.base_array.array_val; - assert(array_const_val->type->id == ZigTypeIdArray); - if (!type_has_bits(g, array_const_val->type)) { - // make this a null pointer - ZigType *usize = g->builtin_types.entry_usize; - const_val->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->llvm_type), - get_llvm_type(g, const_val->type)); - return const_val->llvm_value; - } - size_t elem_index = const_val->data.x_ptr.data.base_array.elem_index; - LLVMValueRef uncasted_ptr_val = gen_const_ptr_array_recursive(g, array_const_val, elem_index); - LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, get_llvm_type(g, const_val->type)); - const_val->llvm_value = ptr_val; - return ptr_val; - } - case ConstPtrSpecialBaseStruct: - { - ZigValue *struct_const_val = const_val->data.x_ptr.data.base_struct.struct_val; - assert(struct_const_val->type->id == ZigTypeIdStruct); - if (!type_has_bits(g, struct_const_val->type)) { - // make this a null pointer - ZigType *usize = g->builtin_types.entry_usize; - const_val->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->llvm_type), - get_llvm_type(g, const_val->type)); - return const_val->llvm_value; - } - size_t src_field_index = const_val->data.x_ptr.data.base_struct.field_index; - size_t gen_field_index = struct_const_val->type->data.structure.fields[src_field_index]->gen_index; - LLVMValueRef uncasted_ptr_val = gen_const_ptr_struct_recursive(g, struct_const_val, - gen_field_index); - LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, get_llvm_type(g, const_val->type)); - const_val->llvm_value = ptr_val; - return ptr_val; - } - case ConstPtrSpecialBaseErrorUnionCode: - { - ZigValue *err_union_const_val = const_val->data.x_ptr.data.base_err_union_code.err_union_val; - assert(err_union_const_val->type->id == ZigTypeIdErrorUnion); - if (!type_has_bits(g, err_union_const_val->type)) { - // make this a null pointer - ZigType *usize = g->builtin_types.entry_usize; - const_val->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->llvm_type), - get_llvm_type(g, const_val->type)); - return const_val->llvm_value; - } - LLVMValueRef uncasted_ptr_val = gen_const_ptr_err_union_code_recursive(g, err_union_const_val); - LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, get_llvm_type(g, const_val->type)); - const_val->llvm_value = ptr_val; - return ptr_val; - } - case ConstPtrSpecialBaseErrorUnionPayload: - { - ZigValue *err_union_const_val = const_val->data.x_ptr.data.base_err_union_payload.err_union_val; - assert(err_union_const_val->type->id == ZigTypeIdErrorUnion); - if (!type_has_bits(g, err_union_const_val->type)) { - // make this a null pointer - ZigType *usize = g->builtin_types.entry_usize; - const_val->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->llvm_type), - get_llvm_type(g, const_val->type)); - return const_val->llvm_value; - } - LLVMValueRef uncasted_ptr_val = gen_const_ptr_err_union_payload_recursive(g, err_union_const_val); - LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, get_llvm_type(g, const_val->type)); - const_val->llvm_value = ptr_val; - return ptr_val; - } - case ConstPtrSpecialBaseOptionalPayload: - { - ZigValue *optional_const_val = const_val->data.x_ptr.data.base_optional_payload.optional_val; - assert(optional_const_val->type->id == ZigTypeIdOptional); - if (!type_has_bits(g, optional_const_val->type)) { - // make this a null pointer - ZigType *usize = g->builtin_types.entry_usize; - const_val->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->llvm_type), - get_llvm_type(g, const_val->type)); - return const_val->llvm_value; - } - LLVMValueRef uncasted_ptr_val = gen_const_ptr_optional_payload_recursive(g, optional_const_val); - LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, get_llvm_type(g, const_val->type)); - const_val->llvm_value = ptr_val; - return ptr_val; - } - case ConstPtrSpecialHardCodedAddr: - { - uint64_t addr_value = const_val->data.x_ptr.data.hard_coded_addr.addr; - ZigType *usize = g->builtin_types.entry_usize; - const_val->llvm_value = LLVMConstIntToPtr( - LLVMConstInt(usize->llvm_type, addr_value, false), get_llvm_type(g, const_val->type)); - return const_val->llvm_value; - } - case ConstPtrSpecialFunction: - return LLVMConstBitCast(fn_llvm_value(g, const_val->data.x_ptr.data.fn.fn_entry), - get_llvm_type(g, const_val->type)); - case ConstPtrSpecialNull: - return LLVMConstNull(get_llvm_type(g, const_val->type)); - } - zig_unreachable(); -} - -static LLVMValueRef gen_const_val_err_set(CodeGen *g, ZigValue *const_val, const char *name) { - uint64_t value = (const_val->data.x_err_set == nullptr) ? 0 : const_val->data.x_err_set->value; - return LLVMConstInt(get_llvm_type(g, g->builtin_types.entry_global_error_set), value, false); -} - -static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *name) { - Error err; - - ZigType *type_entry = const_val->type; - assert(type_has_bits(g, type_entry)); - - if (const_val->special == ConstValSpecialLazy && - (err = ir_resolve_lazy(g, nullptr, const_val))) - codegen_report_errors_and_exit(g); - - switch (const_val->special) { - case ConstValSpecialLazy: - case ConstValSpecialRuntime: - zig_unreachable(); - case ConstValSpecialUndef: - return LLVMGetUndef(get_llvm_type(g, type_entry)); - case ConstValSpecialStatic: - break; - } - - if ((err = type_resolve(g, type_entry, ResolveStatusLLVMFull))) - zig_unreachable(); - - switch (type_entry->id) { - case ZigTypeIdInt: - return bigint_to_llvm_const(get_llvm_type(g, type_entry), &const_val->data.x_bigint); - case ZigTypeIdErrorSet: - return gen_const_val_err_set(g, const_val, name); - case ZigTypeIdFloat: - switch (type_entry->data.floating.bit_count) { - case 16: - return LLVMConstReal(get_llvm_type(g, type_entry), zig_f16_to_double(const_val->data.x_f16)); - case 32: - return LLVMConstReal(get_llvm_type(g, type_entry), const_val->data.x_f32); - case 64: - return LLVMConstReal(get_llvm_type(g, type_entry), const_val->data.x_f64); - case 128: - { - // TODO make sure this is correct on big endian targets too - uint8_t buf[16]; - memcpy(buf, &const_val->data.x_f128, 16); - LLVMValueRef as_int = LLVMConstIntOfArbitraryPrecision(LLVMInt128Type(), 2, - (uint64_t*)buf); - return LLVMConstBitCast(as_int, get_llvm_type(g, type_entry)); - } - default: - zig_unreachable(); - } - case ZigTypeIdBool: - if (const_val->data.x_bool) { - return LLVMConstAllOnes(LLVMInt1Type()); - } else { - return LLVMConstNull(LLVMInt1Type()); - } - case ZigTypeIdOptional: - { - ZigType *child_type = type_entry->data.maybe.child_type; - - if (get_src_ptr_type(type_entry) != nullptr) { - bool has_bits; - if ((err = type_has_bits2(g, child_type, &has_bits))) - codegen_report_errors_and_exit(g); - - if (has_bits) - return gen_const_val_ptr(g, const_val, name); - - // No bits, treat this value as a boolean - const unsigned bool_val = optional_value_is_null(const_val) ? 0 : 1; - return LLVMConstInt(LLVMInt1Type(), bool_val, false); - } else if (child_type->id == ZigTypeIdErrorSet) { - return gen_const_val_err_set(g, const_val, name); - } else if (!type_has_bits(g, child_type)) { - return LLVMConstInt(LLVMInt1Type(), const_val->data.x_optional ? 1 : 0, false); - } else { - LLVMValueRef child_val; - LLVMValueRef maybe_val; - bool make_unnamed_struct; - if (const_val->data.x_optional) { - child_val = gen_const_val(g, const_val->data.x_optional, ""); - maybe_val = LLVMConstAllOnes(LLVMInt1Type()); - - make_unnamed_struct = is_llvm_value_unnamed_type(g, const_val->type, child_val); - } else { - child_val = LLVMGetUndef(get_llvm_type(g, child_type)); - maybe_val = LLVMConstNull(LLVMInt1Type()); - - make_unnamed_struct = false; - } - - LLVMValueRef fields[] = { - child_val, - maybe_val, - nullptr, - }; - if (make_unnamed_struct) { - LLVMValueRef result = LLVMConstStruct(fields, 2, false); - uint64_t last_field_offset = LLVMOffsetOfElement(g->target_data_ref, LLVMTypeOf(result), 1); - uint64_t end_offset = last_field_offset + - LLVMStoreSizeOfType(g->target_data_ref, LLVMTypeOf(fields[1])); - uint64_t expected_sz = LLVMABISizeOfType(g->target_data_ref, get_llvm_type(g, type_entry)); - unsigned pad_sz = expected_sz - end_offset; - if (pad_sz != 0) { - fields[2] = LLVMGetUndef(LLVMArrayType(LLVMInt8Type(), pad_sz)); - result = LLVMConstStruct(fields, 3, false); - } - uint64_t actual_sz = LLVMStoreSizeOfType(g->target_data_ref, LLVMTypeOf(result)); - assert(actual_sz == expected_sz); - return result; - } else { - return LLVMConstNamedStruct(get_llvm_type(g, type_entry), fields, 2); - } - } - } - case ZigTypeIdStruct: - { - LLVMValueRef *fields = heap::c_allocator.allocate(type_entry->data.structure.gen_field_count); - size_t src_field_count = type_entry->data.structure.src_field_count; - bool make_unnamed_struct = false; - assert(type_entry->data.structure.resolve_status == ResolveStatusLLVMFull); - if (type_entry->data.structure.layout == ContainerLayoutPacked) { - size_t src_field_index = 0; - while (src_field_index < src_field_count) { - TypeStructField *type_struct_field = type_entry->data.structure.fields[src_field_index]; - if (type_struct_field->gen_index == SIZE_MAX) { - src_field_index += 1; - continue; - } - - size_t src_field_index_end = src_field_index + 1; - for (; src_field_index_end < src_field_count; src_field_index_end += 1) { - TypeStructField *it_field = type_entry->data.structure.fields[src_field_index_end]; - if (it_field->gen_index != type_struct_field->gen_index) - break; - } - - if (src_field_index + 1 == src_field_index_end) { - ZigValue *field_val = const_val->data.x_struct.fields[src_field_index]; - LLVMValueRef val = gen_const_val(g, field_val, ""); - fields[type_struct_field->gen_index] = val; - make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(g, field_val->type, val); - } else { - bool is_big_endian = g->is_big_endian; // TODO get endianness from struct type - LLVMTypeRef field_ty = LLVMStructGetTypeAtIndex(get_llvm_type(g, type_entry), - (unsigned)type_struct_field->gen_index); - const size_t size_in_bytes = LLVMStoreSizeOfType(g->target_data_ref, field_ty); - const size_t size_in_bits = size_in_bytes * 8; - LLVMTypeRef big_int_type_ref = LLVMIntType(size_in_bits); - LLVMValueRef val = LLVMConstInt(big_int_type_ref, 0, false); - size_t used_bits = 0; - for (size_t i = src_field_index; i < src_field_index_end; i += 1) { - TypeStructField *it_field = type_entry->data.structure.fields[i]; - if (it_field->gen_index == SIZE_MAX) { - continue; - } - LLVMValueRef child_val = pack_const_int(g, big_int_type_ref, - const_val->data.x_struct.fields[i]); - uint32_t packed_bits_size = type_size_bits(g, it_field->type_entry); - if (is_big_endian) { - LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, - size_in_bits - used_bits - packed_bits_size, false); - LLVMValueRef child_val_shifted = LLVMConstShl(child_val, shift_amt); - val = LLVMConstOr(val, child_val_shifted); - } else { - LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, used_bits, false); - LLVMValueRef child_val_shifted = LLVMConstShl(child_val, shift_amt); - val = LLVMConstOr(val, child_val_shifted); - } - used_bits += packed_bits_size; - } - assert(size_in_bits >= used_bits); - if (LLVMGetTypeKind(field_ty) != LLVMArrayTypeKind) { - assert(LLVMGetTypeKind(field_ty) == LLVMIntegerTypeKind); - fields[type_struct_field->gen_index] = val; - } else { - const LLVMValueRef AMT = LLVMConstInt(LLVMTypeOf(val), 8, false); - - LLVMValueRef *values = heap::c_allocator.allocate(size_in_bytes); - for (size_t i = 0; i < size_in_bytes; i++) { - const size_t idx = is_big_endian ? size_in_bytes - 1 - i : i; - values[idx] = LLVMConstTruncOrBitCast(val, LLVMInt8Type()); - val = LLVMConstLShr(val, AMT); - } - - fields[type_struct_field->gen_index] = LLVMConstArray(LLVMInt8Type(), values, size_in_bytes); - } - } - - src_field_index = src_field_index_end; - } - } else { - for (uint32_t i = 0; i < src_field_count; i += 1) { - TypeStructField *type_struct_field = type_entry->data.structure.fields[i]; - if (type_struct_field->gen_index == SIZE_MAX) { - continue; - } - ZigValue *field_val = const_val->data.x_struct.fields[i]; - if (field_val == nullptr) { - add_node_error(g, type_struct_field->decl_node, - buf_sprintf("compiler bug: generating const value for struct field '%s'", - buf_ptr(type_struct_field->name))); - codegen_report_errors_and_exit(g); - } - ZigType *field_type = field_val->type; - assert(field_type != nullptr); - if ((err = ensure_const_val_repr(nullptr, g, nullptr, field_val, field_type))) { - zig_unreachable(); - } - - LLVMValueRef val = gen_const_val(g, field_val, ""); - make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(g, field_type, val); - - // Find the next runtime field - size_t next_rt_gen_index = type_entry->data.structure.gen_field_count; - size_t next_offset = type_entry->abi_size; - for (size_t j = i + 1; j < src_field_count; j++) { - const size_t index = type_entry->data.structure.fields[j]->gen_index; - const size_t offset = type_entry->data.structure.fields[j]->offset; - - if (index != SIZE_MAX) { - next_rt_gen_index = index; - next_offset = offset; - break; - } - } - - // How much padding is needed to reach the next field - const size_t pad_bytes = next_offset - - (type_struct_field->offset + LLVMABISizeOfType(g->target_data_ref, LLVMTypeOf(val))); - // Catch underflow - assert((ssize_t)pad_bytes >= 0); - - if (type_struct_field->gen_index + 1 != next_rt_gen_index) { - // If there's a hole between this field and the next - // we have an alignment gap to fill - fields[type_struct_field->gen_index] = val; - fields[type_struct_field->gen_index + 1] = LLVMGetUndef(LLVMArrayType(LLVMInt8Type(), pad_bytes)); - } else if (pad_bytes != 0) { - LLVMValueRef padded_val[] = { - val, - LLVMGetUndef(LLVMArrayType(LLVMInt8Type(), pad_bytes)), - }; - fields[type_struct_field->gen_index] = LLVMConstStruct(padded_val, 2, true); - make_unnamed_struct = true; - } else { - fields[type_struct_field->gen_index] = val; - } - } - } - if (make_unnamed_struct) { - return LLVMConstStruct(fields, type_entry->data.structure.gen_field_count, - type_entry->data.structure.layout == ContainerLayoutPacked); - } else { - return LLVMConstNamedStruct(get_llvm_type(g, type_entry), fields, type_entry->data.structure.gen_field_count); - } - } - case ZigTypeIdArray: - { - uint64_t len = type_entry->data.array.len; - switch (const_val->data.x_array.special) { - case ConstArraySpecialUndef: - return LLVMGetUndef(get_llvm_type(g, type_entry)); - case ConstArraySpecialNone: { - uint64_t extra_len_from_sentinel = (type_entry->data.array.sentinel != nullptr) ? 1 : 0; - uint64_t full_len = len + extra_len_from_sentinel; - LLVMValueRef *values = heap::c_allocator.allocate(full_len); - LLVMTypeRef element_type_ref = get_llvm_type(g, type_entry->data.array.child_type); - bool make_unnamed_struct = false; - for (uint64_t i = 0; i < len; i += 1) { - ZigValue *elem_value = &const_val->data.x_array.data.s_none.elements[i]; - LLVMValueRef val = gen_const_val(g, elem_value, ""); - values[i] = val; - make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(g, elem_value->type, val); - } - if (type_entry->data.array.sentinel != nullptr) { - values[len] = gen_const_val(g, type_entry->data.array.sentinel, ""); - } - if (make_unnamed_struct) { - return LLVMConstStruct(values, full_len, true); - } else { - return LLVMConstArray(element_type_ref, values, (unsigned)full_len); - } - } - case ConstArraySpecialBuf: { - Buf *buf = const_val->data.x_array.data.s_buf; - return LLVMConstString(buf_ptr(buf), (unsigned)buf_len(buf), - type_entry->data.array.sentinel == nullptr); - } - } - zig_unreachable(); - } - case ZigTypeIdVector: { - uint32_t len = type_entry->data.vector.len; - switch (const_val->data.x_array.special) { - case ConstArraySpecialUndef: - return LLVMGetUndef(get_llvm_type(g, type_entry)); - case ConstArraySpecialNone: { - LLVMValueRef *values = heap::c_allocator.allocate(len); - for (uint64_t i = 0; i < len; i += 1) { - ZigValue *elem_value = &const_val->data.x_array.data.s_none.elements[i]; - values[i] = gen_const_val(g, elem_value, ""); - } - return LLVMConstVector(values, len); - } - case ConstArraySpecialBuf: { - Buf *buf = const_val->data.x_array.data.s_buf; - assert(buf_len(buf) == len); - LLVMValueRef *values = heap::c_allocator.allocate(len); - for (uint64_t i = 0; i < len; i += 1) { - values[i] = LLVMConstInt(g->builtin_types.entry_u8->llvm_type, buf_ptr(buf)[i], false); - } - return LLVMConstVector(values, len); - } - } - zig_unreachable(); - } - case ZigTypeIdUnion: - { - // Force type_entry->data.unionation.union_llvm_type to get resolved - (void)get_llvm_type(g, type_entry); - - if (type_entry->data.unionation.gen_field_count == 0) { - if (type_entry->data.unionation.tag_type == nullptr) { - return nullptr; - } else { - return bigint_to_llvm_const(get_llvm_type(g, type_entry->data.unionation.tag_type), - &const_val->data.x_union.tag); - } - } - - LLVMTypeRef union_type_ref = type_entry->data.unionation.union_llvm_type; - assert(union_type_ref != nullptr); - - LLVMValueRef union_value_ref; - bool make_unnamed_struct; - ZigValue *payload_value = const_val->data.x_union.payload; - if (payload_value == nullptr || !type_has_bits(g, payload_value->type)) { - if (type_entry->data.unionation.gen_tag_index == SIZE_MAX) - return LLVMGetUndef(get_llvm_type(g, type_entry)); - - union_value_ref = LLVMGetUndef(union_type_ref); - make_unnamed_struct = false; - } else { - uint64_t field_type_bytes = LLVMABISizeOfType(g->target_data_ref, - get_llvm_type(g, payload_value->type)); - uint64_t pad_bytes = type_entry->data.unionation.union_abi_size - field_type_bytes; - LLVMValueRef correctly_typed_value = gen_const_val(g, payload_value, ""); - make_unnamed_struct = is_llvm_value_unnamed_type(g, payload_value->type, correctly_typed_value) || - payload_value->type != type_entry->data.unionation.most_aligned_union_member->type_entry; - - { - if (pad_bytes == 0) { - union_value_ref = correctly_typed_value; - } else { - LLVMValueRef fields[2]; - fields[0] = correctly_typed_value; - fields[1] = LLVMGetUndef(LLVMArrayType(LLVMInt8Type(), (unsigned)pad_bytes)); - if (make_unnamed_struct || type_entry->data.unionation.gen_tag_index != SIZE_MAX) { - union_value_ref = LLVMConstStruct(fields, 2, false); - } else { - union_value_ref = LLVMConstNamedStruct(union_type_ref, fields, 2); - } - } - } - - if (type_entry->data.unionation.gen_tag_index == SIZE_MAX) { - return union_value_ref; - } - } - - LLVMValueRef tag_value = bigint_to_llvm_const( - get_llvm_type(g, type_entry->data.unionation.tag_type), - &const_val->data.x_union.tag); - - LLVMValueRef fields[3]; - fields[type_entry->data.unionation.gen_union_index] = union_value_ref; - fields[type_entry->data.unionation.gen_tag_index] = tag_value; - - if (make_unnamed_struct) { - LLVMValueRef result = LLVMConstStruct(fields, 2, false); - uint64_t last_field_offset = LLVMOffsetOfElement(g->target_data_ref, LLVMTypeOf(result), 1); - uint64_t end_offset = last_field_offset + - LLVMStoreSizeOfType(g->target_data_ref, LLVMTypeOf(fields[1])); - uint64_t expected_sz = LLVMABISizeOfType(g->target_data_ref, get_llvm_type(g, type_entry)); - unsigned pad_sz = expected_sz - end_offset; - if (pad_sz != 0) { - fields[2] = LLVMGetUndef(LLVMArrayType(LLVMInt8Type(), pad_sz)); - result = LLVMConstStruct(fields, 3, false); - } - uint64_t actual_sz = LLVMStoreSizeOfType(g->target_data_ref, LLVMTypeOf(result)); - assert(actual_sz == expected_sz); - return result; - } else { - return LLVMConstNamedStruct(get_llvm_type(g, type_entry), fields, 2); - } - - } - - case ZigTypeIdEnum: - return bigint_to_llvm_const(get_llvm_type(g, type_entry), &const_val->data.x_enum_tag); - case ZigTypeIdFn: - if (const_val->data.x_ptr.special == ConstPtrSpecialFunction && - const_val->data.x_ptr.mut != ConstPtrMutComptimeConst) { - zig_unreachable(); - } - // Treat it the same as we do for pointers - return gen_const_val_ptr(g, const_val, name); - case ZigTypeIdPointer: - return gen_const_val_ptr(g, const_val, name); - case ZigTypeIdErrorUnion: - { - ZigType *payload_type = type_entry->data.error_union.payload_type; - ZigType *err_set_type = type_entry->data.error_union.err_set_type; - if (!type_has_bits(g, payload_type)) { - assert(type_has_bits(g, err_set_type)); - ErrorTableEntry *err_set = const_val->data.x_err_union.error_set->data.x_err_set; - uint64_t value = (err_set == nullptr) ? 0 : err_set->value; - return LLVMConstInt(get_llvm_type(g, g->err_tag_type), value, false); - } else if (!type_has_bits(g, err_set_type)) { - assert(type_has_bits(g, payload_type)); - return gen_const_val(g, const_val->data.x_err_union.payload, ""); - } else { - LLVMValueRef err_tag_value; - LLVMValueRef err_payload_value; - bool make_unnamed_struct; - ErrorTableEntry *err_set = const_val->data.x_err_union.error_set->data.x_err_set; - if (err_set != nullptr) { - err_tag_value = LLVMConstInt(get_llvm_type(g, g->err_tag_type), err_set->value, false); - err_payload_value = LLVMConstNull(get_llvm_type(g, payload_type)); - make_unnamed_struct = false; - } else { - err_tag_value = LLVMConstNull(get_llvm_type(g, g->err_tag_type)); - ZigValue *payload_val = const_val->data.x_err_union.payload; - err_payload_value = gen_const_val(g, payload_val, ""); - make_unnamed_struct = is_llvm_value_unnamed_type(g, payload_val->type, err_payload_value); - } - LLVMValueRef fields[3]; - fields[err_union_err_index] = err_tag_value; - fields[err_union_payload_index] = err_payload_value; - size_t field_count = 2; - if (type_entry->data.error_union.pad_llvm_type != nullptr) { - fields[2] = LLVMGetUndef(type_entry->data.error_union.pad_llvm_type); - field_count = 3; - } - if (make_unnamed_struct) { - return LLVMConstStruct(fields, field_count, false); - } else { - return LLVMConstNamedStruct(get_llvm_type(g, type_entry), fields, field_count); - } - } - } - case ZigTypeIdVoid: - return nullptr; - case ZigTypeIdInvalid: - case ZigTypeIdMetaType: - case ZigTypeIdUnreachable: - case ZigTypeIdComptimeFloat: - case ZigTypeIdComptimeInt: - case ZigTypeIdEnumLiteral: - case ZigTypeIdUndefined: - case ZigTypeIdNull: - case ZigTypeIdBoundFn: - case ZigTypeIdOpaque: - zig_unreachable(); - case ZigTypeIdFnFrame: - zig_panic("TODO"); - case ZigTypeIdAnyFrame: - zig_panic("TODO"); - } - zig_unreachable(); -} - -static void render_const_val(CodeGen *g, ZigValue *const_val, const char *name) { - if (!const_val->llvm_value) - const_val->llvm_value = gen_const_val(g, const_val, name); - - if (const_val->llvm_global) - LLVMSetInitializer(const_val->llvm_global, const_val->llvm_value); -} - -static void render_const_val_global(CodeGen *g, ZigValue *const_val, const char *name) { - if (!const_val->llvm_global) { - LLVMTypeRef type_ref = const_val->llvm_value ? - LLVMTypeOf(const_val->llvm_value) : get_llvm_type(g, const_val->type); - LLVMValueRef global_value = LLVMAddGlobal(g->module, type_ref, name); - LLVMSetLinkage(global_value, (name == nullptr) ? LLVMPrivateLinkage : LLVMInternalLinkage); - LLVMSetGlobalConstant(global_value, true); - LLVMSetUnnamedAddr(global_value, true); - LLVMSetAlignment(global_value, (const_val->llvm_align == 0) ? - get_abi_alignment(g, const_val->type) : const_val->llvm_align); - - const_val->llvm_global = global_value; - } - - if (const_val->llvm_value) - LLVMSetInitializer(const_val->llvm_global, const_val->llvm_value); -} - -static void generate_error_name_table(CodeGen *g) { - if (g->err_name_table != nullptr || !g->generate_error_name_table || g->errors_by_index.length == 1) { - return; - } - - assert(g->errors_by_index.length > 0); - - ZigType *u8_ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false, - PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0, false); - ZigType *str_type = get_slice_type(g, u8_ptr_type); - - LLVMValueRef *values = heap::c_allocator.allocate(g->errors_by_index.length); - values[0] = LLVMGetUndef(get_llvm_type(g, str_type)); - for (size_t i = 1; i < g->errors_by_index.length; i += 1) { - ErrorTableEntry *err_entry = g->errors_by_index.at(i); - Buf *name = &err_entry->name; - - g->largest_err_name_len = max(g->largest_err_name_len, buf_len(name)); - - LLVMValueRef str_init = LLVMConstString(buf_ptr(name), (unsigned)buf_len(name), true); - LLVMValueRef str_global = LLVMAddGlobal(g->module, LLVMTypeOf(str_init), ""); - LLVMSetInitializer(str_global, str_init); - LLVMSetLinkage(str_global, LLVMPrivateLinkage); - LLVMSetGlobalConstant(str_global, true); - LLVMSetUnnamedAddr(str_global, true); - LLVMSetAlignment(str_global, LLVMABIAlignmentOfType(g->target_data_ref, LLVMTypeOf(str_init))); - - LLVMValueRef fields[] = { - LLVMConstBitCast(str_global, get_llvm_type(g, u8_ptr_type)), - LLVMConstInt(g->builtin_types.entry_usize->llvm_type, buf_len(name), false), - }; - values[i] = LLVMConstNamedStruct(get_llvm_type(g, str_type), fields, 2); - } - - LLVMValueRef err_name_table_init = LLVMConstArray(get_llvm_type(g, str_type), values, (unsigned)g->errors_by_index.length); - - g->err_name_table = LLVMAddGlobal(g->module, LLVMTypeOf(err_name_table_init), - get_mangled_name(g, buf_ptr(buf_create_from_str("__zig_err_name_table")))); - LLVMSetInitializer(g->err_name_table, err_name_table_init); - LLVMSetLinkage(g->err_name_table, LLVMPrivateLinkage); - LLVMSetGlobalConstant(g->err_name_table, true); - LLVMSetUnnamedAddr(g->err_name_table, true); - LLVMSetAlignment(g->err_name_table, LLVMABIAlignmentOfType(g->target_data_ref, LLVMTypeOf(err_name_table_init))); -} - -static void build_all_basic_blocks(CodeGen *g, ZigFn *fn) { - IrExecutableGen *executable = &fn->analyzed_executable; - assert(executable->basic_block_list.length > 0); - LLVMValueRef fn_val = fn_llvm_value(g, fn); - LLVMBasicBlockRef first_bb = nullptr; - if (fn_is_async(fn)) { - first_bb = LLVMAppendBasicBlock(fn_val, "AsyncSwitch"); - g->cur_preamble_llvm_block = first_bb; - } - for (size_t block_i = 0; block_i < executable->basic_block_list.length; block_i += 1) { - IrBasicBlockGen *bb = executable->basic_block_list.at(block_i); - bb->llvm_block = LLVMAppendBasicBlock(fn_val, bb->name_hint); - } - if (first_bb == nullptr) { - first_bb = executable->basic_block_list.at(0)->llvm_block; - } - LLVMPositionBuilderAtEnd(g->builder, first_bb); -} - -static void gen_global_var(CodeGen *g, ZigVar *var, LLVMValueRef init_val, - ZigType *type_entry) -{ - if (g->strip_debug_symbols) { - return; - } - - assert(var->gen_is_const); - assert(type_entry); - - ZigType *import = get_scope_import(var->parent_scope); - assert(import); - - bool is_local_to_unit = true; - ZigLLVMCreateGlobalVariable(g->dbuilder, get_di_scope(g, var->parent_scope), var->name, - var->name, import->data.structure.root_struct->di_file, - (unsigned)(var->decl_node->line + 1), - get_llvm_di_type(g, type_entry), is_local_to_unit); - - // TODO ^^ make an actual global variable -} - -static void set_global_tls(CodeGen *g, ZigVar *var, LLVMValueRef global_value) { - bool is_extern = var->decl_node->data.variable_declaration.is_extern; - bool is_export = var->decl_node->data.variable_declaration.is_export; - bool is_internal_linkage = !is_extern && !is_export; - if (var->is_thread_local && (!g->is_single_threaded || !is_internal_linkage)) { - LLVMSetThreadLocalMode(global_value, LLVMGeneralDynamicTLSModel); - } -} - -static void do_code_gen(CodeGen *g) { - Error err; - assert(!g->errors.length); - - generate_error_name_table(g); - - // Generate module level variables - for (size_t i = 0; i < g->global_vars.length; i += 1) { - TldVar *tld_var = g->global_vars.at(i); - ZigVar *var = tld_var->var; - - if (var->var_type->id == ZigTypeIdComptimeFloat) { - // Generate debug info for it but that's it. - ZigValue *const_val = var->const_value; - assert(const_val->special != ConstValSpecialRuntime); - if ((err = ir_resolve_lazy(g, var->decl_node, const_val))) - zig_unreachable(); - if (const_val->type != var->var_type) { - zig_panic("TODO debug info for var with ptr casted value"); - } - ZigType *var_type = g->builtin_types.entry_f128; - ZigValue coerced_value = {}; - coerced_value.special = ConstValSpecialStatic; - coerced_value.type = var_type; - coerced_value.data.x_f128 = bigfloat_to_f128(&const_val->data.x_bigfloat); - LLVMValueRef init_val = gen_const_val(g, &coerced_value, ""); - gen_global_var(g, var, init_val, var_type); - continue; - } - - if (var->var_type->id == ZigTypeIdComptimeInt) { - // Generate debug info for it but that's it. - ZigValue *const_val = var->const_value; - assert(const_val->special != ConstValSpecialRuntime); - if ((err = ir_resolve_lazy(g, var->decl_node, const_val))) - zig_unreachable(); - if (const_val->type != var->var_type) { - zig_panic("TODO debug info for var with ptr casted value"); - } - size_t bits_needed = bigint_bits_needed(&const_val->data.x_bigint); - if (bits_needed < 8) { - bits_needed = 8; - } - ZigType *var_type = get_int_type(g, const_val->data.x_bigint.is_negative, bits_needed); - LLVMValueRef init_val = bigint_to_llvm_const(get_llvm_type(g, var_type), &const_val->data.x_bigint); - gen_global_var(g, var, init_val, var_type); - continue; - } - - if (!type_has_bits(g, var->var_type)) - continue; - - assert(var->decl_node); - - GlobalLinkageId linkage; - const char *unmangled_name = var->name; - const char *symbol_name; - if (var->export_list.length == 0) { - if (var->decl_node->data.variable_declaration.is_extern) { - symbol_name = unmangled_name; - linkage = GlobalLinkageIdStrong; - } else { - symbol_name = get_mangled_name(g, unmangled_name); - linkage = GlobalLinkageIdInternal; - } - } else { - GlobalExport *global_export = &var->export_list.items[0]; - symbol_name = buf_ptr(&global_export->name); - linkage = global_export->linkage; - } - - LLVMValueRef global_value; - bool externally_initialized = var->decl_node->data.variable_declaration.expr == nullptr; - if (externally_initialized) { - LLVMValueRef existing_llvm_var = LLVMGetNamedGlobal(g->module, symbol_name); - if (existing_llvm_var) { - global_value = LLVMConstBitCast(existing_llvm_var, - LLVMPointerType(get_llvm_type(g, var->var_type), 0)); - } else { - global_value = LLVMAddGlobal(g->module, get_llvm_type(g, var->var_type), symbol_name); - // TODO debug info for the extern variable - - LLVMSetLinkage(global_value, to_llvm_linkage(linkage, true)); - maybe_import_dll(g, global_value, GlobalLinkageIdStrong); - LLVMSetAlignment(global_value, var->align_bytes); - LLVMSetGlobalConstant(global_value, var->gen_is_const); - set_global_tls(g, var, global_value); - } - } else { - bool exported = (linkage != GlobalLinkageIdInternal); - render_const_val(g, var->const_value, symbol_name); - render_const_val_global(g, var->const_value, symbol_name); - global_value = var->const_value->llvm_global; - - if (exported) { - LLVMSetLinkage(global_value, to_llvm_linkage(linkage, false)); - maybe_export_dll(g, global_value, GlobalLinkageIdStrong); - } - if (var->section_name) { - LLVMSetSection(global_value, buf_ptr(var->section_name)); - } - LLVMSetAlignment(global_value, var->align_bytes); - - // TODO debug info for function pointers - // Here we use const_value->type because that's the type of the llvm global, - // which we const ptr cast upon use to whatever it needs to be. - if (var->gen_is_const && var->const_value->type->id != ZigTypeIdFn) { - gen_global_var(g, var, var->const_value->llvm_value, var->const_value->type); - } - - LLVMSetGlobalConstant(global_value, var->gen_is_const); - set_global_tls(g, var, global_value); - } - - var->value_ref = global_value; - - for (size_t export_i = 1; export_i < var->export_list.length; export_i += 1) { - GlobalExport *global_export = &var->export_list.items[export_i]; - LLVMAddAlias(g->module, LLVMTypeOf(var->value_ref), var->value_ref, buf_ptr(&global_export->name)); - } - } - - // Generate function definitions. - stage2_progress_update_node(g->sub_progress_node, 0, g->fn_defs.length); - for (size_t fn_i = 0; fn_i < g->fn_defs.length; fn_i += 1) { - ZigFn *fn_table_entry = g->fn_defs.at(fn_i); - Stage2ProgressNode *fn_prog_node = stage2_progress_start(g->sub_progress_node, - buf_ptr(&fn_table_entry->symbol_name), buf_len(&fn_table_entry->symbol_name), 0); - - FnTypeId *fn_type_id = &fn_table_entry->type_entry->data.fn.fn_type_id; - CallingConvention cc = fn_type_id->cc; - bool is_c_abi = !calling_convention_allows_zig_types(cc); - bool want_sret = want_first_arg_sret(g, fn_type_id); - - LLVMValueRef fn = fn_llvm_value(g, fn_table_entry); - g->cur_fn = fn_table_entry; - g->cur_fn_val = fn; - - build_all_basic_blocks(g, fn_table_entry); - clear_debug_source_node(g); - - bool is_async = fn_is_async(fn_table_entry); - - if (is_async) { - g->cur_frame_ptr = LLVMGetParam(fn, 0); - } else { - if (want_sret) { - g->cur_ret_ptr = LLVMGetParam(fn, 0); - } else if (type_has_bits(g, fn_type_id->return_type)) { - g->cur_ret_ptr = build_alloca(g, fn_type_id->return_type, "result", 0); - // TODO add debug info variable for this - } else { - g->cur_ret_ptr = nullptr; - } - } - - uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, fn_table_entry); - bool have_err_ret_trace_arg = err_ret_trace_arg_index != UINT32_MAX; - if (have_err_ret_trace_arg) { - g->cur_err_ret_trace_val_arg = LLVMGetParam(fn, err_ret_trace_arg_index); - } else { - g->cur_err_ret_trace_val_arg = nullptr; - } - - // error return tracing setup - bool have_err_ret_trace_stack = g->have_err_ret_tracing && fn_table_entry->calls_or_awaits_errorable_fn && - !is_async && !have_err_ret_trace_arg; - LLVMValueRef err_ret_array_val = nullptr; - if (have_err_ret_trace_stack) { - ZigType *array_type = get_array_type(g, g->builtin_types.entry_usize, stack_trace_ptr_count, nullptr); - err_ret_array_val = build_alloca(g, array_type, "error_return_trace_addresses", get_abi_alignment(g, array_type)); - - (void)get_llvm_type(g, get_stack_trace_type(g)); - g->cur_err_ret_trace_val_stack = build_alloca(g, get_stack_trace_type(g), "error_return_trace", - get_abi_alignment(g, g->stack_trace_type)); - } else { - g->cur_err_ret_trace_val_stack = nullptr; - } - - if (!is_async) { - // allocate async frames for nosuspend calls & awaits to async functions - ZigType *largest_call_frame_type = nullptr; - IrInstGen *all_calls_alloca = ir_create_alloca(g, &fn_table_entry->fndef_scope->base, - fn_table_entry->body_node, fn_table_entry, g->builtin_types.entry_void, "@async_call_frame"); - for (size_t i = 0; i < fn_table_entry->call_list.length; i += 1) { - IrInstGenCall *call = fn_table_entry->call_list.at(i); - if (call->fn_entry == nullptr) - continue; - if (!fn_is_async(call->fn_entry)) - continue; - if (call->modifier != CallModifierNoSuspend) - continue; - if (call->frame_result_loc != nullptr) - continue; - ZigType *callee_frame_type = get_fn_frame_type(g, call->fn_entry); - 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); - } - // allocate temporary stack data - for (size_t alloca_i = 0; alloca_i < fn_table_entry->alloca_gen_list.length; alloca_i += 1) { - IrInstGenAlloca *instruction = fn_table_entry->alloca_gen_list.at(alloca_i); - ZigType *ptr_type = instruction->base.value->type; - assert(ptr_type->id == ZigTypeIdPointer); - ZigType *child_type = ptr_type->data.pointer.child_type; - if (type_resolve(g, child_type, ResolveStatusSizeKnown)) - zig_unreachable(); - 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; - } - } - if (type_resolve(g, child_type, ResolveStatusLLVMFull)) - zig_unreachable(); - instruction->base.llvm_value = build_alloca(g, child_type, instruction->name_hint, - get_ptr_align(g, ptr_type)); - } - } - - ZigType *import = get_scope_import(&fn_table_entry->fndef_scope->base); - unsigned gen_i_init = want_sret ? 1 : 0; - - // create debug variable declarations for variables and allocate all local variables - FnWalk fn_walk_var = {}; - fn_walk_var.id = FnWalkIdVars; - fn_walk_var.data.vars.import = import; - fn_walk_var.data.vars.fn = fn_table_entry; - fn_walk_var.data.vars.llvm_fn = fn; - fn_walk_var.data.vars.gen_i = gen_i_init; - for (size_t var_i = 0; var_i < fn_table_entry->variable_list.length; var_i += 1) { - ZigVar *var = fn_table_entry->variable_list.at(var_i); - - if (!type_has_bits(g, var->var_type)) { - continue; - } - if (ir_get_var_is_comptime(var)) - continue; - switch (type_requires_comptime(g, var->var_type)) { - case ReqCompTimeInvalid: - zig_unreachable(); - case ReqCompTimeYes: - continue; - case ReqCompTimeNo: - break; - } - - if (var->src_arg_index == SIZE_MAX) { - var->di_loc_var = ZigLLVMCreateAutoVariable(g->dbuilder, get_di_scope(g, var->parent_scope), - var->name, import->data.structure.root_struct->di_file, (unsigned)(var->decl_node->line + 1), - get_llvm_di_type(g, var->var_type), !g->strip_debug_symbols, 0); - - } else if (is_c_abi) { - fn_walk_var.data.vars.var = var; - iter_function_params_c_abi(g, fn_table_entry->type_entry, &fn_walk_var, var->src_arg_index); - } else if (!is_async) { - ZigType *gen_type; - FnGenParamInfo *gen_info = &fn_table_entry->type_entry->data.fn.gen_param_info[var->src_arg_index]; - assert(gen_info->gen_index != SIZE_MAX); - - if (handle_is_ptr(g, var->var_type)) { - if (gen_info->is_byval) { - gen_type = var->var_type; - } else { - gen_type = gen_info->type; - } - var->value_ref = LLVMGetParam(fn, gen_info->gen_index); - } else { - gen_type = var->var_type; - var->value_ref = build_alloca(g, var->var_type, var->name, var->align_bytes); - } - if (var->decl_node) { - var->di_loc_var = ZigLLVMCreateParameterVariable(g->dbuilder, get_di_scope(g, var->parent_scope), - var->name, import->data.structure.root_struct->di_file, - (unsigned)(var->decl_node->line + 1), - get_llvm_di_type(g, gen_type), !g->strip_debug_symbols, 0, (unsigned)(gen_info->gen_index+1)); - } - - } - } - - // finishing error return trace setup. we have to do this after all the allocas. - if (have_err_ret_trace_stack) { - ZigType *usize = g->builtin_types.entry_usize; - size_t index_field_index = g->stack_trace_type->data.structure.fields[0]->gen_index; - LLVMValueRef index_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val_stack, (unsigned)index_field_index, ""); - gen_store_untyped(g, LLVMConstNull(usize->llvm_type), index_field_ptr, 0, false); - - size_t addresses_field_index = g->stack_trace_type->data.structure.fields[1]->gen_index; - LLVMValueRef addresses_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val_stack, (unsigned)addresses_field_index, ""); - - ZigType *slice_type = g->stack_trace_type->data.structure.fields[1]->type_entry; - size_t ptr_field_index = slice_type->data.structure.fields[slice_ptr_index]->gen_index; - LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)ptr_field_index, ""); - LLVMValueRef zero = LLVMConstNull(usize->llvm_type); - LLVMValueRef indices[] = {zero, zero}; - LLVMValueRef err_ret_array_val_elem0_ptr = LLVMBuildInBoundsGEP(g->builder, err_ret_array_val, - indices, 2, ""); - ZigType *ptr_ptr_usize_type = get_pointer_to_type(g, get_pointer_to_type(g, usize, false), false); - gen_store(g, err_ret_array_val_elem0_ptr, ptr_field_ptr, ptr_ptr_usize_type); - - size_t len_field_index = slice_type->data.structure.fields[slice_len_index]->gen_index; - LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)len_field_index, ""); - gen_store(g, LLVMConstInt(usize->llvm_type, stack_trace_ptr_count, false), len_field_ptr, get_pointer_to_type(g, usize, false)); - } - - if (is_async) { - (void)get_llvm_type(g, fn_table_entry->frame_type); - g->cur_resume_block_count = 0; - - LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; - LLVMValueRef size_val = LLVMConstInt(usize_type_ref, fn_table_entry->frame_type->abi_size, false); - if (g->need_frame_size_prefix_data) { - ZigLLVMFunctionSetPrefixData(fn_table_entry->llvm_value, size_val); - } - - if (!g->strip_debug_symbols) { - AstNode *source_node = fn_table_entry->proto_node; - ZigLLVMSetCurrentDebugLocation(g->builder, (int)source_node->line + 1, - (int)source_node->column + 1, get_di_scope(g, fn_table_entry->child_scope)); - } - IrExecutableGen *executable = &fn_table_entry->analyzed_executable; - LLVMBasicBlockRef bad_resume_block = LLVMAppendBasicBlock(g->cur_fn_val, "BadResume"); - LLVMPositionBuilderAtEnd(g->builder, bad_resume_block); - gen_assertion_scope(g, PanicMsgIdBadResume, fn_table_entry->child_scope); - - LLVMPositionBuilderAtEnd(g->builder, g->cur_preamble_llvm_block); - render_async_spills(g); - g->cur_async_awaiter_ptr = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr, frame_awaiter_index, ""); - LLVMValueRef resume_index_ptr = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr, frame_resume_index, ""); - g->cur_async_resume_index_ptr = resume_index_ptr; - - if (type_has_bits(g, fn_type_id->return_type)) { - LLVMValueRef cur_ret_ptr_ptr = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr, frame_ret_start, ""); - g->cur_ret_ptr = LLVMBuildLoad(g->builder, cur_ret_ptr_ptr, ""); - } - uint32_t trace_field_index_stack = UINT32_MAX; - if (codegen_fn_has_err_ret_tracing_stack(g, fn_table_entry, true)) { - trace_field_index_stack = frame_index_trace_stack(g, fn_table_entry); - g->cur_err_ret_trace_val_stack = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr, - trace_field_index_stack, ""); - } - - LLVMValueRef resume_index = LLVMBuildLoad(g->builder, resume_index_ptr, ""); - LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, resume_index, bad_resume_block, 4); - g->cur_async_switch_instr = switch_instr; - - LLVMValueRef zero = LLVMConstNull(usize_type_ref); - IrBasicBlockGen *entry_block = executable->basic_block_list.at(0); - LLVMAddCase(switch_instr, zero, entry_block->llvm_block); - g->cur_resume_block_count += 1; - - { - LLVMBasicBlockRef bad_not_suspended_bb = LLVMAppendBasicBlock(g->cur_fn_val, "NotSuspended"); - size_t new_block_index = g->cur_resume_block_count; - g->cur_resume_block_count += 1; - g->cur_bad_not_suspended_index = LLVMConstInt(usize_type_ref, new_block_index, false); - LLVMAddCase(g->cur_async_switch_instr, g->cur_bad_not_suspended_index, bad_not_suspended_bb); - - LLVMPositionBuilderAtEnd(g->builder, bad_not_suspended_bb); - gen_assertion_scope(g, PanicMsgIdResumeNotSuspendedFn, fn_table_entry->child_scope); - } - - LLVMPositionBuilderAtEnd(g->builder, entry_block->llvm_block); - LLVMBuildStore(g->builder, g->cur_bad_not_suspended_index, g->cur_async_resume_index_ptr); - if (trace_field_index_stack != UINT32_MAX) { - if (codegen_fn_has_err_ret_tracing_arg(g, fn_type_id->return_type)) { - LLVMValueRef trace_ptr_ptr = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr, - frame_index_trace_arg(g, fn_type_id->return_type), ""); - LLVMValueRef zero_ptr = LLVMConstNull(LLVMGetElementType(LLVMTypeOf(trace_ptr_ptr))); - LLVMBuildStore(g->builder, zero_ptr, trace_ptr_ptr); - } - - LLVMValueRef trace_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr, - trace_field_index_stack, ""); - LLVMValueRef addrs_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr, - trace_field_index_stack + 1, ""); - - gen_init_stack_trace(g, trace_field_ptr, addrs_field_ptr); - } - render_async_var_decls(g, entry_block->instruction_list.at(0)->base.scope); - } else { - // create debug variable declarations for parameters - // rely on the first variables in the variable_list being parameters. - FnWalk fn_walk_init = {}; - fn_walk_init.id = FnWalkIdInits; - fn_walk_init.data.inits.fn = fn_table_entry; - fn_walk_init.data.inits.llvm_fn = fn; - fn_walk_init.data.inits.gen_i = gen_i_init; - walk_function_params(g, fn_table_entry->type_entry, &fn_walk_init); - } - - ir_render(g, fn_table_entry); - - stage2_progress_end(fn_prog_node); - } - - assert(!g->errors.length); - - if (buf_len(&g->global_asm) != 0) { - LLVMSetModuleInlineAsm(g->module, buf_ptr(&g->global_asm)); - } - - while (g->type_resolve_stack.length != 0) { - ZigType *ty = g->type_resolve_stack.last(); - if (type_resolve(g, ty, ResolveStatusLLVMFull)) - zig_unreachable(); - } - - ZigLLVMDIBuilderFinalize(g->dbuilder); - - if (g->verbose_llvm_ir) { - fflush(stderr); - LLVMDumpModule(g->module); - } - - char *error = nullptr; - if (LLVMVerifyModule(g->module, LLVMReturnStatusAction, &error)) { - zig_panic("broken LLVM module found: %s\nThis is a bug in the Zig compiler.", error); - } -} - -static void zig_llvm_emit_output(CodeGen *g) { - g->pass1_arena->destruct(&heap::c_allocator); - g->pass1_arena = nullptr; - - bool is_small = g->build_mode == BuildModeSmallRelease; - - char *err_msg = nullptr; - const char *asm_filename = nullptr; - const char *bin_filename = nullptr; - const char *llvm_ir_filename = nullptr; - - if (g->emit_bin) bin_filename = buf_ptr(&g->o_file_output_path); - if (g->emit_asm) asm_filename = buf_ptr(&g->asm_file_output_path); - if (g->emit_llvm_ir) llvm_ir_filename = buf_ptr(&g->llvm_ir_file_output_path); - - // Unfortunately, LLVM shits the bed when we ask for both binary and assembly. So we call the entire - // pipeline multiple times if this is requested. - if (asm_filename != nullptr && bin_filename != nullptr) { - if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, &err_msg, g->build_mode == BuildModeDebug, - is_small, g->enable_time_report, nullptr, bin_filename, llvm_ir_filename)) - { - fprintf(stderr, "LLVM failed to emit file: %s\n", err_msg); - exit(1); - } - bin_filename = nullptr; - llvm_ir_filename = nullptr; - } - - if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, &err_msg, g->build_mode == BuildModeDebug, - is_small, g->enable_time_report, asm_filename, bin_filename, llvm_ir_filename)) - { - fprintf(stderr, "LLVM failed to emit file: %s\n", err_msg); - exit(1); - } - - if (g->emit_bin) { - g->link_objects.append(&g->o_file_output_path); - if (g->bundle_compiler_rt && (g->out_type == OutTypeObj || (g->out_type == OutTypeLib && !g->is_dynamic))) { - zig_link_add_compiler_rt(g, g->sub_progress_node); - } - } - - LLVMDisposeModule(g->module); - g->module = nullptr; - LLVMDisposeTargetData(g->target_data_ref); - g->target_data_ref = nullptr; - LLVMDisposeTargetMachine(g->target_machine); - g->target_machine = nullptr; -} - -struct CIntTypeInfo { - CIntType id; - const char *name; - bool is_signed; -}; - -static const CIntTypeInfo c_int_type_infos[] = { - {CIntTypeShort, "c_short", true}, - {CIntTypeUShort, "c_ushort", false}, - {CIntTypeInt, "c_int", true}, - {CIntTypeUInt, "c_uint", false}, - {CIntTypeLong, "c_long", true}, - {CIntTypeULong, "c_ulong", false}, - {CIntTypeLongLong, "c_longlong", true}, - {CIntTypeULongLong, "c_ulonglong", false}, -}; - -static const bool is_signed_list[] = { false, true, }; - -struct GlobalLinkageValue { - GlobalLinkageId id; - const char *name; -}; - -static void add_fp_entry(CodeGen *g, const char *name, uint32_t bit_count, LLVMTypeRef type_ref, - ZigType **field) -{ - ZigType *entry = new_type_table_entry(ZigTypeIdFloat); - entry->llvm_type = type_ref; - entry->size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->llvm_type); - entry->abi_size = LLVMABISizeOfType(g->target_data_ref, entry->llvm_type); - entry->abi_align = LLVMABIAlignmentOfType(g->target_data_ref, entry->llvm_type); - buf_init_from_str(&entry->name, name); - entry->data.floating.bit_count = bit_count; - - entry->llvm_di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), - entry->size_in_bits, ZigLLVMEncoding_DW_ATE_float()); - *field = entry; - g->primitive_type_table.put(&entry->name, entry); -} - -static void define_builtin_types(CodeGen *g) { - { - // if this type is anywhere in the AST, we should never hit codegen. - ZigType *entry = new_type_table_entry(ZigTypeIdInvalid); - buf_init_from_str(&entry->name, "(invalid)"); - g->builtin_types.entry_invalid = entry; - } - { - ZigType *entry = new_type_table_entry(ZigTypeIdComptimeFloat); - buf_init_from_str(&entry->name, "comptime_float"); - g->builtin_types.entry_num_lit_float = entry; - g->primitive_type_table.put(&entry->name, entry); - } - { - ZigType *entry = new_type_table_entry(ZigTypeIdComptimeInt); - buf_init_from_str(&entry->name, "comptime_int"); - g->builtin_types.entry_num_lit_int = entry; - g->primitive_type_table.put(&entry->name, entry); - } - { - ZigType *entry = new_type_table_entry(ZigTypeIdEnumLiteral); - buf_init_from_str(&entry->name, "(enum literal)"); - g->builtin_types.entry_enum_literal = entry; - } - { - ZigType *entry = new_type_table_entry(ZigTypeIdUndefined); - buf_init_from_str(&entry->name, "(undefined)"); - g->builtin_types.entry_undef = entry; - } - { - ZigType *entry = new_type_table_entry(ZigTypeIdNull); - buf_init_from_str(&entry->name, "(null)"); - g->builtin_types.entry_null = entry; - } - { - ZigType *entry = new_type_table_entry(ZigTypeIdOpaque); - buf_init_from_str(&entry->name, "(anytype)"); - g->builtin_types.entry_anytype = entry; - } - - for (size_t i = 0; i < array_length(c_int_type_infos); i += 1) { - const CIntTypeInfo *info = &c_int_type_infos[i]; - uint32_t size_in_bits = target_c_type_size_in_bits(g->zig_target, info->id); - bool is_signed = info->is_signed; - - ZigType *entry = new_type_table_entry(ZigTypeIdInt); - entry->llvm_type = LLVMIntType(size_in_bits); - entry->size_in_bits = 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); - - buf_init_from_str(&entry->name, info->name); - - entry->llvm_di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), - 8*LLVMStoreSizeOfType(g->target_data_ref, entry->llvm_type), - is_signed ? ZigLLVMEncoding_DW_ATE_signed() : ZigLLVMEncoding_DW_ATE_unsigned()); - entry->data.integral.is_signed = is_signed; - entry->data.integral.bit_count = size_in_bits; - g->primitive_type_table.put(&entry->name, entry); - - get_c_int_type_ptr(g, info->id)[0] = entry; - } - - { - ZigType *entry = new_type_table_entry(ZigTypeIdBool); - entry->llvm_type = LLVMInt1Type(); - entry->size_in_bits = 1; - entry->abi_size = LLVMABISizeOfType(g->target_data_ref, entry->llvm_type); - entry->abi_align = LLVMABIAlignmentOfType(g->target_data_ref, entry->llvm_type); - buf_init_from_str(&entry->name, "bool"); - entry->llvm_di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), - 8*LLVMStoreSizeOfType(g->target_data_ref, entry->llvm_type), - ZigLLVMEncoding_DW_ATE_boolean()); - g->builtin_types.entry_bool = entry; - g->primitive_type_table.put(&entry->name, entry); - } - - for (size_t sign_i = 0; sign_i < array_length(is_signed_list); sign_i += 1) { - bool is_signed = is_signed_list[sign_i]; - - ZigType *entry = new_type_table_entry(ZigTypeIdInt); - entry->llvm_type = LLVMIntType(g->pointer_size_bytes * 8); - entry->size_in_bits = g->pointer_size_bytes * 8; - entry->abi_size = LLVMABISizeOfType(g->target_data_ref, entry->llvm_type); - entry->abi_align = LLVMABIAlignmentOfType(g->target_data_ref, entry->llvm_type); - - const char u_or_i = is_signed ? 'i' : 'u'; - buf_resize(&entry->name, 0); - buf_appendf(&entry->name, "%csize", u_or_i); - - entry->data.integral.is_signed = is_signed; - entry->data.integral.bit_count = g->pointer_size_bytes * 8; - - entry->llvm_di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), - 8*LLVMStoreSizeOfType(g->target_data_ref, entry->llvm_type), - is_signed ? ZigLLVMEncoding_DW_ATE_signed() : ZigLLVMEncoding_DW_ATE_unsigned()); - g->primitive_type_table.put(&entry->name, entry); - - if (is_signed) { - g->builtin_types.entry_isize = entry; - } else { - g->builtin_types.entry_usize = entry; - } - } - - add_fp_entry(g, "f16", 16, LLVMHalfType(), &g->builtin_types.entry_f16); - add_fp_entry(g, "f32", 32, LLVMFloatType(), &g->builtin_types.entry_f32); - add_fp_entry(g, "f64", 64, LLVMDoubleType(), &g->builtin_types.entry_f64); - add_fp_entry(g, "f128", 128, LLVMFP128Type(), &g->builtin_types.entry_f128); - add_fp_entry(g, "c_longdouble", 80, LLVMX86FP80Type(), &g->builtin_types.entry_c_longdouble); - - { - ZigType *entry = new_type_table_entry(ZigTypeIdVoid); - entry->llvm_type = LLVMVoidType(); - buf_init_from_str(&entry->name, "void"); - entry->llvm_di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), - 0, - ZigLLVMEncoding_DW_ATE_signed()); - g->builtin_types.entry_void = entry; - g->primitive_type_table.put(&entry->name, entry); - } - { - ZigType *entry = new_type_table_entry(ZigTypeIdUnreachable); - entry->llvm_type = LLVMVoidType(); - buf_init_from_str(&entry->name, "noreturn"); - entry->llvm_di_type = g->builtin_types.entry_void->llvm_di_type; - g->builtin_types.entry_unreachable = entry; - g->primitive_type_table.put(&entry->name, entry); - } - { - ZigType *entry = new_type_table_entry(ZigTypeIdMetaType); - buf_init_from_str(&entry->name, "type"); - g->builtin_types.entry_type = entry; - g->primitive_type_table.put(&entry->name, entry); - } - - g->builtin_types.entry_u8 = get_int_type(g, false, 8); - g->builtin_types.entry_u16 = get_int_type(g, false, 16); - g->builtin_types.entry_u29 = get_int_type(g, false, 29); - g->builtin_types.entry_u32 = get_int_type(g, false, 32); - g->builtin_types.entry_u64 = get_int_type(g, false, 64); - g->builtin_types.entry_i8 = get_int_type(g, true, 8); - g->builtin_types.entry_i32 = get_int_type(g, true, 32); - g->builtin_types.entry_i64 = get_int_type(g, true, 64); - - { - g->builtin_types.entry_c_void = get_opaque_type(g, nullptr, nullptr, "c_void", - buf_create_from_str("c_void")); - g->primitive_type_table.put(&g->builtin_types.entry_c_void->name, g->builtin_types.entry_c_void); - } - - { - ZigType *entry = new_type_table_entry(ZigTypeIdErrorSet); - buf_init_from_str(&entry->name, "anyerror"); - entry->data.error_set.err_count = UINT32_MAX; - - // TODO https://github.com/ziglang/zig/issues/786 - g->err_tag_type = g->builtin_types.entry_u16; - - entry->size_in_bits = g->err_tag_type->size_in_bits; - entry->abi_align = g->err_tag_type->abi_align; - entry->abi_size = g->err_tag_type->abi_size; - - g->builtin_types.entry_global_error_set = entry; - - g->errors_by_index.append(nullptr); - - g->primitive_type_table.put(&entry->name, entry); - } -} - -static void define_intern_values(CodeGen *g) { - { - auto& value = g->intern.x_undefined; - value.type = g->builtin_types.entry_undef; - value.special = ConstValSpecialStatic; - } - { - auto& value = g->intern.x_void; - value.type = g->builtin_types.entry_void; - value.special = ConstValSpecialStatic; - } - { - auto& value = g->intern.x_null; - value.type = g->builtin_types.entry_null; - value.special = ConstValSpecialStatic; - } - { - auto& value = g->intern.x_unreachable; - value.type = g->builtin_types.entry_unreachable; - value.special = ConstValSpecialStatic; - } - { - auto& value = g->intern.zero_byte; - value.type = g->builtin_types.entry_u8; - value.special = ConstValSpecialStatic; - bigint_init_unsigned(&value.data.x_bigint, 0); - } -} - -static BuiltinFnEntry *create_builtin_fn(CodeGen *g, BuiltinFnId id, const char *name, size_t count) { - BuiltinFnEntry *builtin_fn = heap::c_allocator.create(); - buf_init_from_str(&builtin_fn->name, name); - builtin_fn->id = id; - builtin_fn->param_count = count; - g->builtin_fn_table.put(&builtin_fn->name, builtin_fn); - return builtin_fn; -} - -static void define_builtin_fns(CodeGen *g) { - create_builtin_fn(g, BuiltinFnIdBreakpoint, "breakpoint", 0); - create_builtin_fn(g, BuiltinFnIdReturnAddress, "returnAddress", 0); - create_builtin_fn(g, BuiltinFnIdMemcpy, "memcpy", 3); - create_builtin_fn(g, BuiltinFnIdMemset, "memset", 3); - create_builtin_fn(g, BuiltinFnIdSizeof, "sizeOf", 1); - create_builtin_fn(g, BuiltinFnIdAlignOf, "alignOf", 1); - create_builtin_fn(g, BuiltinFnIdField, "field", 2); - create_builtin_fn(g, BuiltinFnIdTypeInfo, "typeInfo", 1); - create_builtin_fn(g, BuiltinFnIdType, "Type", 1); - create_builtin_fn(g, BuiltinFnIdHasField, "hasField", 2); - create_builtin_fn(g, BuiltinFnIdTypeof, "TypeOf", SIZE_MAX); - create_builtin_fn(g, BuiltinFnIdAddWithOverflow, "addWithOverflow", 4); - create_builtin_fn(g, BuiltinFnIdSubWithOverflow, "subWithOverflow", 4); - create_builtin_fn(g, BuiltinFnIdMulWithOverflow, "mulWithOverflow", 4); - create_builtin_fn(g, BuiltinFnIdShlWithOverflow, "shlWithOverflow", 4); - create_builtin_fn(g, BuiltinFnIdCInclude, "cInclude", 1); - create_builtin_fn(g, BuiltinFnIdCDefine, "cDefine", 2); - create_builtin_fn(g, BuiltinFnIdCUndef, "cUndef", 1); - create_builtin_fn(g, BuiltinFnIdCtz, "ctz", 2); - create_builtin_fn(g, BuiltinFnIdClz, "clz", 2); - create_builtin_fn(g, BuiltinFnIdPopCount, "popCount", 2); - create_builtin_fn(g, BuiltinFnIdBswap, "byteSwap", 2); - create_builtin_fn(g, BuiltinFnIdBitReverse, "bitReverse", 2); - create_builtin_fn(g, BuiltinFnIdImport, "import", 1); - create_builtin_fn(g, BuiltinFnIdCImport, "cImport", 1); - create_builtin_fn(g, BuiltinFnIdErrName, "errorName", 1); - create_builtin_fn(g, BuiltinFnIdTypeName, "typeName", 1); - create_builtin_fn(g, BuiltinFnIdEmbedFile, "embedFile", 1); - create_builtin_fn(g, BuiltinFnIdCmpxchgWeak, "cmpxchgWeak", 6); - create_builtin_fn(g, BuiltinFnIdCmpxchgStrong, "cmpxchgStrong", 6); - create_builtin_fn(g, BuiltinFnIdFence, "fence", 1); - create_builtin_fn(g, BuiltinFnIdTruncate, "truncate", 2); - create_builtin_fn(g, BuiltinFnIdIntCast, "intCast", 2); - create_builtin_fn(g, BuiltinFnIdFloatCast, "floatCast", 2); - create_builtin_fn(g, BuiltinFnIdIntToFloat, "intToFloat", 2); - create_builtin_fn(g, BuiltinFnIdFloatToInt, "floatToInt", 2); - create_builtin_fn(g, BuiltinFnIdBoolToInt, "boolToInt", 1); - create_builtin_fn(g, BuiltinFnIdErrToInt, "errorToInt", 1); - create_builtin_fn(g, BuiltinFnIdIntToErr, "intToError", 1); - create_builtin_fn(g, BuiltinFnIdEnumToInt, "enumToInt", 1); - create_builtin_fn(g, BuiltinFnIdIntToEnum, "intToEnum", 2); - create_builtin_fn(g, BuiltinFnIdCompileErr, "compileError", 1); - create_builtin_fn(g, BuiltinFnIdCompileLog, "compileLog", SIZE_MAX); - create_builtin_fn(g, BuiltinFnIdVectorType, "Vector", 2); - create_builtin_fn(g, BuiltinFnIdShuffle, "shuffle", 4); - create_builtin_fn(g, BuiltinFnIdSplat, "splat", 2); - create_builtin_fn(g, BuiltinFnIdSetCold, "setCold", 1); - create_builtin_fn(g, BuiltinFnIdSetRuntimeSafety, "setRuntimeSafety", 1); - create_builtin_fn(g, BuiltinFnIdSetFloatMode, "setFloatMode", 1); - create_builtin_fn(g, BuiltinFnIdPanic, "panic", 1); - create_builtin_fn(g, BuiltinFnIdPtrCast, "ptrCast", 2); - create_builtin_fn(g, BuiltinFnIdBitCast, "bitCast", 2); - create_builtin_fn(g, BuiltinFnIdIntToPtr, "intToPtr", 2); - create_builtin_fn(g, BuiltinFnIdPtrToInt, "ptrToInt", 1); - create_builtin_fn(g, BuiltinFnIdTagName, "tagName", 1); - create_builtin_fn(g, BuiltinFnIdTagType, "TagType", 1); - create_builtin_fn(g, BuiltinFnIdFieldParentPtr, "fieldParentPtr", 3); - create_builtin_fn(g, BuiltinFnIdByteOffsetOf, "byteOffsetOf", 2); - create_builtin_fn(g, BuiltinFnIdBitOffsetOf, "bitOffsetOf", 2); - create_builtin_fn(g, BuiltinFnIdDivExact, "divExact", 2); - create_builtin_fn(g, BuiltinFnIdDivTrunc, "divTrunc", 2); - create_builtin_fn(g, BuiltinFnIdDivFloor, "divFloor", 2); - create_builtin_fn(g, BuiltinFnIdRem, "rem", 2); - create_builtin_fn(g, BuiltinFnIdMod, "mod", 2); - create_builtin_fn(g, BuiltinFnIdSqrt, "sqrt", 1); - create_builtin_fn(g, BuiltinFnIdSin, "sin", 1); - create_builtin_fn(g, BuiltinFnIdCos, "cos", 1); - create_builtin_fn(g, BuiltinFnIdExp, "exp", 1); - create_builtin_fn(g, BuiltinFnIdExp2, "exp2", 1); - create_builtin_fn(g, BuiltinFnIdLog, "log", 1); - create_builtin_fn(g, BuiltinFnIdLog2, "log2", 1); - create_builtin_fn(g, BuiltinFnIdLog10, "log10", 1); - create_builtin_fn(g, BuiltinFnIdFabs, "fabs", 1); - create_builtin_fn(g, BuiltinFnIdFloor, "floor", 1); - create_builtin_fn(g, BuiltinFnIdCeil, "ceil", 1); - create_builtin_fn(g, BuiltinFnIdTrunc, "trunc", 1); - create_builtin_fn(g, BuiltinFnIdNearbyInt, "nearbyInt", 1); - create_builtin_fn(g, BuiltinFnIdRound, "round", 1); - create_builtin_fn(g, BuiltinFnIdMulAdd, "mulAdd", 4); - create_builtin_fn(g, BuiltinFnIdAsyncCall, "asyncCall", SIZE_MAX); - create_builtin_fn(g, BuiltinFnIdShlExact, "shlExact", 2); - create_builtin_fn(g, BuiltinFnIdShrExact, "shrExact", 2); - create_builtin_fn(g, BuiltinFnIdSetEvalBranchQuota, "setEvalBranchQuota", 1); - create_builtin_fn(g, BuiltinFnIdAlignCast, "alignCast", 2); - create_builtin_fn(g, BuiltinFnIdSetAlignStack, "setAlignStack", 1); - create_builtin_fn(g, BuiltinFnIdExport, "export", 2); - create_builtin_fn(g, BuiltinFnIdErrorReturnTrace, "errorReturnTrace", 0); - create_builtin_fn(g, BuiltinFnIdAtomicRmw, "atomicRmw", 5); - create_builtin_fn(g, BuiltinFnIdAtomicLoad, "atomicLoad", 3); - create_builtin_fn(g, BuiltinFnIdAtomicStore, "atomicStore", 4); - create_builtin_fn(g, BuiltinFnIdErrSetCast, "errSetCast", 2); - create_builtin_fn(g, BuiltinFnIdThis, "This", 0); - create_builtin_fn(g, BuiltinFnIdHasDecl, "hasDecl", 2); - create_builtin_fn(g, BuiltinFnIdUnionInit, "unionInit", 3); - create_builtin_fn(g, BuiltinFnIdFrameHandle, "frame", 0); - create_builtin_fn(g, BuiltinFnIdFrameType, "Frame", 1); - create_builtin_fn(g, BuiltinFnIdFrameAddress, "frameAddress", 0); - create_builtin_fn(g, BuiltinFnIdFrameSize, "frameSize", 1); - create_builtin_fn(g, BuiltinFnIdAs, "as", 2); - create_builtin_fn(g, BuiltinFnIdCall, "call", 3); - create_builtin_fn(g, BuiltinFnIdBitSizeof, "bitSizeOf", 1); - create_builtin_fn(g, BuiltinFnIdWasmMemorySize, "wasmMemorySize", 1); - create_builtin_fn(g, BuiltinFnIdWasmMemoryGrow, "wasmMemoryGrow", 2); - create_builtin_fn(g, BuiltinFnIdSrc, "src", 0); -} - -static const char *bool_to_str(bool b) { - return b ? "true" : "false"; -} - -static const char *build_mode_to_str(BuildMode build_mode) { - switch (build_mode) { - case BuildModeDebug: return "Mode.Debug"; - case BuildModeSafeRelease: return "Mode.ReleaseSafe"; - case BuildModeFastRelease: return "Mode.ReleaseFast"; - case BuildModeSmallRelease: return "Mode.ReleaseSmall"; - } - zig_unreachable(); -} - -static const char *subsystem_to_str(TargetSubsystem subsystem) { - switch (subsystem) { - case TargetSubsystemConsole: return "Console"; - case TargetSubsystemWindows: return "Windows"; - case TargetSubsystemPosix: return "Posix"; - case TargetSubsystemNative: return "Native"; - case TargetSubsystemEfiApplication: return "EfiApplication"; - case TargetSubsystemEfiBootServiceDriver: return "EfiBootServiceDriver"; - case TargetSubsystemEfiRom: return "EfiRom"; - case TargetSubsystemEfiRuntimeDriver: return "EfiRuntimeDriver"; - case TargetSubsystemAuto: zig_unreachable(); - } - zig_unreachable(); -} - -static bool detect_dynamic_link(CodeGen *g) { - if (g->is_dynamic) - return true; - if (g->zig_target->os == OsFreestanding) - return false; - if (target_os_requires_libc(g->zig_target->os)) - return true; - if (g->libc_link_lib != nullptr && target_is_glibc(g->zig_target)) - return true; - // If there are no dynamic libraries then we can disable dynamic linking. - for (size_t i = 0; i < g->link_libs_list.length; i += 1) { - LinkLib *link_lib = g->link_libs_list.at(i); - if (target_is_libc_lib_name(g->zig_target, buf_ptr(link_lib->name))) - continue; - if (target_is_libcpp_lib_name(g->zig_target, buf_ptr(link_lib->name))) - continue; - return true; - } - return false; -} - -static bool detect_pic(CodeGen *g) { - if (target_requires_pic(g->zig_target, g->libc_link_lib != nullptr)) - return true; - switch (g->want_pic) { - case WantPICDisabled: - return false; - case WantPICEnabled: - return true; - case WantPICAuto: - return g->have_dynamic_link; - } - zig_unreachable(); -} - -static bool detect_stack_probing(CodeGen *g) { - if (!target_supports_stack_probing(g->zig_target)) - return false; - switch (g->want_stack_check) { - case WantStackCheckDisabled: - return false; - case WantStackCheckEnabled: - return true; - case WantStackCheckAuto: - return g->build_mode == BuildModeSafeRelease || g->build_mode == BuildModeDebug; - } - zig_unreachable(); -} - -static bool detect_sanitize_c(CodeGen *g) { - if (!target_supports_sanitize_c(g->zig_target)) - return false; - switch (g->want_sanitize_c) { - case WantCSanitizeDisabled: - return false; - case WantCSanitizeEnabled: - return true; - case WantCSanitizeAuto: - return g->build_mode == BuildModeSafeRelease || g->build_mode == BuildModeDebug; - } - zig_unreachable(); -} - -// Returns TargetSubsystemAuto to mean "no subsystem" -TargetSubsystem detect_subsystem(CodeGen *g) { - if (g->subsystem != TargetSubsystemAuto) - return g->subsystem; - if (g->zig_target->os == OsWindows) { - if (g->have_dllmain_crt_startup || (g->out_type == OutTypeLib && g->is_dynamic)) - return TargetSubsystemAuto; - if (g->have_c_main || g->is_test_build || g->have_winmain_crt_startup || g->have_wwinmain_crt_startup) - return TargetSubsystemConsole; - if (g->have_winmain || g->have_wwinmain) - return TargetSubsystemWindows; - } else if (g->zig_target->os == OsUefi) { - return TargetSubsystemEfiApplication; - } - return TargetSubsystemAuto; -} - -static bool detect_single_threaded(CodeGen *g) { - if (g->want_single_threaded) - return true; - if (target_is_single_threaded(g->zig_target)) { - return true; - } - return false; -} - -static bool detect_err_ret_tracing(CodeGen *g) { - return !g->strip_debug_symbols && - g->build_mode != BuildModeFastRelease && - g->build_mode != BuildModeSmallRelease; -} - -static LLVMCodeModel to_llvm_code_model(CodeGen *g) { - switch (g->code_model) { - case CodeModelDefault: - return LLVMCodeModelDefault; - case CodeModelTiny: - return LLVMCodeModelTiny; - case CodeModelSmall: - return LLVMCodeModelSmall; - case CodeModelKernel: - return LLVMCodeModelKernel; - case CodeModelMedium: - return LLVMCodeModelMedium; - case CodeModelLarge: - return LLVMCodeModelLarge; - } - - zig_unreachable(); -} - -Buf *codegen_generate_builtin_source(CodeGen *g) { - g->have_dynamic_link = detect_dynamic_link(g); - g->have_pic = detect_pic(g); - g->have_stack_probing = detect_stack_probing(g); - g->have_sanitize_c = detect_sanitize_c(g); - g->is_single_threaded = detect_single_threaded(g); - g->have_err_ret_tracing = detect_err_ret_tracing(g); - - Buf *contents = buf_alloc(); - buf_appendf(contents, "usingnamespace @import(\"std\").builtin;\n\n"); - - const char *cur_os = nullptr; - { - uint32_t field_count = (uint32_t)target_os_count(); - for (uint32_t i = 0; i < field_count; i += 1) { - Os os_type = target_os_enum(i); - const char *name = target_os_name(os_type); - - if (os_type == g->zig_target->os) { - g->target_os_index = i; - cur_os = name; - } - } - } - assert(cur_os != nullptr); - - const char *cur_arch = nullptr; - { - uint32_t field_count = (uint32_t)target_arch_count(); - for (uint32_t arch_i = 0; arch_i < field_count; arch_i += 1) { - ZigLLVM_ArchType arch = target_arch_enum(arch_i); - const char *arch_name = target_arch_name(arch); - if (arch == g->zig_target->arch) { - g->target_arch_index = arch_i; - cur_arch = arch_name; - } - } - } - assert(cur_arch != nullptr); - - const char *cur_abi = nullptr; - { - uint32_t field_count = (uint32_t)target_abi_count(); - for (uint32_t i = 0; i < field_count; i += 1) { - ZigLLVM_EnvironmentType abi = target_abi_enum(i); - const char *name = target_abi_name(abi); - - if (abi == g->zig_target->abi) { - g->target_abi_index = i; - cur_abi = name; - } - } - } - assert(cur_abi != nullptr); - - const char *cur_obj_fmt = nullptr; - { - uint32_t field_count = (uint32_t)target_oformat_count(); - for (uint32_t i = 0; i < field_count; i += 1) { - ZigLLVM_ObjectFormatType oformat = target_oformat_enum(i); - const char *name = target_oformat_name(oformat); - - ZigLLVM_ObjectFormatType target_oformat = target_object_format(g->zig_target); - if (oformat == target_oformat) { - g->target_oformat_index = i; - cur_obj_fmt = name; - } - } - - } - assert(cur_obj_fmt != nullptr); - - // If any of these asserts trip then you need to either fix the internal compiler enum - // or the corresponding one in std.Target or std.builtin. - static_assert(ContainerLayoutAuto == 0, ""); - static_assert(ContainerLayoutExtern == 1, ""); - static_assert(ContainerLayoutPacked == 2, ""); - - static_assert(CallingConventionUnspecified == 0, ""); - static_assert(CallingConventionC == 1, ""); - static_assert(CallingConventionCold == 2, ""); - static_assert(CallingConventionNaked == 3, ""); - static_assert(CallingConventionAsync == 4, ""); - static_assert(CallingConventionInterrupt == 5, ""); - static_assert(CallingConventionSignal == 6, ""); - static_assert(CallingConventionStdcall == 7, ""); - static_assert(CallingConventionFastcall == 8, ""); - static_assert(CallingConventionVectorcall == 9, ""); - static_assert(CallingConventionThiscall == 10, ""); - static_assert(CallingConventionAPCS == 11, ""); - static_assert(CallingConventionAAPCS == 12, ""); - static_assert(CallingConventionAAPCSVFP == 13, ""); - - static_assert(FnInlineAuto == 0, ""); - static_assert(FnInlineAlways == 1, ""); - static_assert(FnInlineNever == 2, ""); - - static_assert(BuiltinPtrSizeOne == 0, ""); - static_assert(BuiltinPtrSizeMany == 1, ""); - static_assert(BuiltinPtrSizeSlice == 2, ""); - static_assert(BuiltinPtrSizeC == 3, ""); - - static_assert(TargetSubsystemConsole == 0, ""); - static_assert(TargetSubsystemWindows == 1, ""); - static_assert(TargetSubsystemPosix == 2, ""); - static_assert(TargetSubsystemNative == 3, ""); - static_assert(TargetSubsystemEfiApplication == 4, ""); - static_assert(TargetSubsystemEfiBootServiceDriver == 5, ""); - static_assert(TargetSubsystemEfiRom == 6, ""); - static_assert(TargetSubsystemEfiRuntimeDriver == 7, ""); - { - const char *endian_str = g->is_big_endian ? "Endian.Big" : "Endian.Little"; - buf_appendf(contents, "pub const endian = %s;\n", endian_str); - } - const char *out_type = nullptr; - switch (g->out_type) { - case OutTypeExe: - out_type = "Exe"; - break; - case OutTypeLib: - out_type = "Lib"; - break; - case OutTypeObj: - case OutTypeUnknown: // This happens when running the `zig builtin` command. - out_type = "Obj"; - break; - } - buf_appendf(contents, "pub const output_mode = OutputMode.%s;\n", out_type); - const char *link_type = g->have_dynamic_link ? "Dynamic" : "Static"; - buf_appendf(contents, "pub const link_mode = LinkMode.%s;\n", link_type); - buf_appendf(contents, "pub const is_test = %s;\n", bool_to_str(g->is_test_build)); - buf_appendf(contents, "pub const single_threaded = %s;\n", bool_to_str(g->is_single_threaded)); - buf_append_str(contents, "/// Deprecated: use `std.Target.cpu.arch`\n"); - buf_appendf(contents, "pub const arch = Arch.%s;\n", cur_arch); - buf_appendf(contents, "pub const abi = Abi.%s;\n", cur_abi); - { - buf_append_str(contents, "pub const cpu: Cpu = "); - if (g->zig_target->cpu_builtin_str != nullptr) { - buf_append_str(contents, g->zig_target->cpu_builtin_str); - } else { - buf_appendf(contents, "Target.Cpu.baseline(.%s);\n", cur_arch); - } - } - { - buf_append_str(contents, "pub const os = "); - if (g->zig_target->os_builtin_str != nullptr) { - buf_append_str(contents, g->zig_target->os_builtin_str); - } else { - buf_appendf(contents, "Target.Os.defaultVersionRange(.%s);\n", cur_os); - } - } - buf_appendf(contents, "pub const object_format = ObjectFormat.%s;\n", cur_obj_fmt); - buf_appendf(contents, "pub const mode = %s;\n", build_mode_to_str(g->build_mode)); - buf_appendf(contents, "pub const link_libc = %s;\n", bool_to_str(g->libc_link_lib != nullptr)); - buf_appendf(contents, "pub const link_libcpp = %s;\n", bool_to_str(g->libcpp_link_lib != nullptr)); - buf_appendf(contents, "pub const have_error_return_tracing = %s;\n", bool_to_str(g->have_err_ret_tracing)); - buf_appendf(contents, "pub const valgrind_support = %s;\n", bool_to_str(want_valgrind_support(g))); - buf_appendf(contents, "pub const position_independent_code = %s;\n", bool_to_str(g->have_pic)); - buf_appendf(contents, "pub const strip_debug_info = %s;\n", bool_to_str(g->strip_debug_symbols)); - - { - const char *code_model; - switch (g->code_model) { - case CodeModelDefault: - code_model = "default"; - break; - case CodeModelTiny: - code_model = "tiny"; - break; - case CodeModelSmall: - code_model = "small"; - break; - case CodeModelKernel: - code_model = "kernel"; - break; - case CodeModelMedium: - code_model = "medium"; - break; - case CodeModelLarge: - code_model = "large"; - break; - default: - zig_unreachable(); - } - - buf_appendf(contents, "pub const code_model = CodeModel.%s;\n", code_model); - } - - { - TargetSubsystem detected_subsystem = detect_subsystem(g); - if (detected_subsystem != TargetSubsystemAuto) { - buf_appendf(contents, "pub const explicit_subsystem = SubSystem.%s;\n", subsystem_to_str(detected_subsystem)); - } - } - - if (g->is_test_build) { - buf_appendf(contents, - "pub var test_functions: []TestFn = undefined; // overwritten later\n" - ); - - buf_appendf(contents, "pub const test_io_mode = %s;\n", - g->test_is_evented ? ".evented" : ".blocking"); - } - - return contents; -} - -static ZigPackage *create_test_runner_pkg(CodeGen *g) { - return codegen_create_package(g, buf_ptr(g->zig_std_special_dir), "test_runner.zig", "std.special"); -} - -static Error define_builtin_compile_vars(CodeGen *g) { - if (g->std_package == nullptr) - return ErrorNone; - - Error err; - - Buf *manifest_dir = buf_alloc(); - os_path_join(get_global_cache_dir(), buf_create_from_str("builtin"), manifest_dir); - - CacheHash cache_hash; - cache_init(&cache_hash, manifest_dir); - - Buf *compiler_id; - if ((err = get_compiler_id(&compiler_id))) - return err; - - // Only a few things affect builtin.zig - cache_buf(&cache_hash, compiler_id); - cache_int(&cache_hash, g->build_mode); - cache_bool(&cache_hash, g->strip_debug_symbols); - cache_int(&cache_hash, g->out_type); - cache_bool(&cache_hash, detect_dynamic_link(g)); - cache_bool(&cache_hash, g->is_test_build); - cache_bool(&cache_hash, g->is_single_threaded); - cache_bool(&cache_hash, g->test_is_evented); - cache_int(&cache_hash, g->code_model); - cache_int(&cache_hash, g->zig_target->is_native_os); - cache_int(&cache_hash, g->zig_target->is_native_cpu); - cache_int(&cache_hash, g->zig_target->arch); - cache_int(&cache_hash, g->zig_target->vendor); - cache_int(&cache_hash, g->zig_target->os); - cache_int(&cache_hash, g->zig_target->abi); - if (g->zig_target->cache_hash != nullptr) { - cache_mem(&cache_hash, g->zig_target->cache_hash, g->zig_target->cache_hash_len); - } - if (g->zig_target->glibc_or_darwin_version != nullptr) { - cache_int(&cache_hash, g->zig_target->glibc_or_darwin_version->major); - cache_int(&cache_hash, g->zig_target->glibc_or_darwin_version->minor); - cache_int(&cache_hash, g->zig_target->glibc_or_darwin_version->patch); - } - cache_bool(&cache_hash, g->have_err_ret_tracing); - cache_bool(&cache_hash, g->libc_link_lib != nullptr); - cache_bool(&cache_hash, g->libcpp_link_lib != nullptr); - cache_bool(&cache_hash, g->valgrind_support); - cache_bool(&cache_hash, g->link_eh_frame_hdr); - cache_int(&cache_hash, detect_subsystem(g)); - - Buf digest = BUF_INIT; - buf_resize(&digest, 0); - if ((err = cache_hit(&cache_hash, &digest))) { - // Treat an invalid format error as a cache miss. - if (err != ErrorInvalidFormat) - return err; - } - - // We should always get a cache hit because there are no - // files in the input hash. - assert(buf_len(&digest) != 0); - - Buf *this_dir = buf_alloc(); - os_path_join(manifest_dir, &digest, this_dir); - - if ((err = os_make_path(this_dir))) - return err; - - const char *builtin_zig_basename = "builtin.zig"; - Buf *builtin_zig_path = buf_alloc(); - os_path_join(this_dir, buf_create_from_str(builtin_zig_basename), builtin_zig_path); - - bool hit; - if ((err = os_file_exists(builtin_zig_path, &hit))) - return err; - Buf *contents; - if (hit) { - contents = buf_alloc(); - if ((err = os_fetch_file_path(builtin_zig_path, contents))) { - fprintf(stderr, "Unable to open '%s': %s\n", buf_ptr(builtin_zig_path), err_str(err)); - exit(1); - } - } else { - contents = codegen_generate_builtin_source(g); - if ((err = os_write_file(builtin_zig_path, contents))) { - fprintf(stderr, "Unable to write file '%s': %s\n", buf_ptr(builtin_zig_path), err_str(err)); - exit(1); - } - } - - assert(g->main_pkg); - assert(g->std_package); - g->compile_var_package = new_package(buf_ptr(this_dir), builtin_zig_basename, "builtin"); - if (g->is_test_build) { - if (g->test_runner_package == nullptr) { - g->test_runner_package = create_test_runner_pkg(g); - } - g->root_pkg = g->test_runner_package; - } else { - g->root_pkg = g->main_pkg; - } - g->compile_var_package->package_table.put(buf_create_from_str("std"), g->std_package); - g->main_pkg->package_table.put(buf_create_from_str("builtin"), g->compile_var_package); - g->main_pkg->package_table.put(buf_create_from_str("root"), g->root_pkg); - g->std_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package); - g->std_package->package_table.put(buf_create_from_str("std"), g->std_package); - g->std_package->package_table.put(buf_create_from_str("root"), g->root_pkg); - g->compile_var_import = add_source_file(g, g->compile_var_package, builtin_zig_path, contents, - SourceKindPkgMain); - - return ErrorNone; -} - -static void init(CodeGen *g) { - if (g->module) - return; - - g->have_dynamic_link = detect_dynamic_link(g); - g->have_pic = detect_pic(g); - g->have_stack_probing = detect_stack_probing(g); - g->have_sanitize_c = detect_sanitize_c(g); - g->is_single_threaded = detect_single_threaded(g); - g->have_err_ret_tracing = detect_err_ret_tracing(g); - - if (target_is_single_threaded(g->zig_target)) { - g->is_single_threaded = true; - } - - assert(g->root_out_name); - g->module = LLVMModuleCreateWithName(buf_ptr(g->root_out_name)); - - LLVMSetTarget(g->module, buf_ptr(&g->llvm_triple_str)); - - if (target_object_format(g->zig_target) == ZigLLVM_COFF) { - ZigLLVMAddModuleCodeViewFlag(g->module); - } else { - ZigLLVMAddModuleDebugInfoFlag(g->module); - } - - LLVMTargetRef target_ref; - char *err_msg = nullptr; - if (LLVMGetTargetFromTriple(buf_ptr(&g->llvm_triple_str), &target_ref, &err_msg)) { - fprintf(stderr, - "Zig is expecting LLVM to understand this target: '%s'\n" - "However LLVM responded with: \"%s\"\n" - "Zig is unable to continue. This is a bug in Zig:\n" - "https://github.com/ziglang/zig/issues/438\n" - , buf_ptr(&g->llvm_triple_str), err_msg); - exit(1); - } - - bool is_optimized = g->build_mode != BuildModeDebug; - LLVMCodeGenOptLevel opt_level = is_optimized ? LLVMCodeGenLevelAggressive : LLVMCodeGenLevelNone; - - LLVMRelocMode reloc_mode; - if (g->have_pic) { - reloc_mode = LLVMRelocPIC; - } else if (g->have_dynamic_link) { - reloc_mode = LLVMRelocDynamicNoPic; - } else { - reloc_mode = LLVMRelocStatic; - } - - const char *target_specific_cpu_args = ""; - const char *target_specific_features = ""; - - if (g->zig_target->is_native_cpu) { - target_specific_cpu_args = ZigLLVMGetHostCPUName(); - target_specific_features = ZigLLVMGetNativeFeatures(); - } - - // Override CPU and features if defined by user. - if (g->zig_target->llvm_cpu_name != nullptr) { - target_specific_cpu_args = g->zig_target->llvm_cpu_name; - } - if (g->zig_target->llvm_cpu_features != nullptr) { - target_specific_features = g->zig_target->llvm_cpu_features; - } - if (g->verbose_llvm_cpu_features) { - fprintf(stderr, "name=%s triple=%s\n", buf_ptr(g->root_out_name), buf_ptr(&g->llvm_triple_str)); - fprintf(stderr, "name=%s target_specific_cpu_args=%s\n", buf_ptr(g->root_out_name), target_specific_cpu_args); - fprintf(stderr, "name=%s target_specific_features=%s\n", buf_ptr(g->root_out_name), target_specific_features); - } - - // TODO handle float ABI better- it should depend on the ABI portion of std.Target - ZigLLVMABIType float_abi = ZigLLVMABITypeDefault; - - // TODO a way to override this as part of std.Target ABI? - const char *abi_name = nullptr; - if (target_is_riscv(g->zig_target)) { - // RISC-V Linux defaults to ilp32d/lp64d - if (g->zig_target->os == OsLinux) { - abi_name = (g->zig_target->arch == ZigLLVM_riscv32) ? "ilp32d" : "lp64d"; - } else { - abi_name = (g->zig_target->arch == ZigLLVM_riscv32) ? "ilp32" : "lp64"; - } - } - - g->target_machine = ZigLLVMCreateTargetMachine(target_ref, buf_ptr(&g->llvm_triple_str), - target_specific_cpu_args, target_specific_features, opt_level, reloc_mode, - to_llvm_code_model(g), g->function_sections, float_abi, abi_name); - - g->target_data_ref = LLVMCreateTargetDataLayout(g->target_machine); - - char *layout_str = LLVMCopyStringRepOfTargetData(g->target_data_ref); - LLVMSetDataLayout(g->module, layout_str); - - - assert(g->pointer_size_bytes == LLVMPointerSize(g->target_data_ref)); - g->is_big_endian = (LLVMByteOrder(g->target_data_ref) == LLVMBigEndian); - - g->builder = LLVMCreateBuilder(); - g->dbuilder = ZigLLVMCreateDIBuilder(g->module, true); - - // Don't use ZIG_VERSION_STRING here, llvm misparses it when it includes - // the git revision. - Buf *producer = buf_sprintf("zig %d.%d.%d", ZIG_VERSION_MAJOR, ZIG_VERSION_MINOR, ZIG_VERSION_PATCH); - const char *flags = ""; - unsigned runtime_version = 0; - - // For macOS stack traces, we want to avoid having to parse the compilation unit debug - // info. As long as each debug info file has a path independent of the compilation unit - // directory (DW_AT_comp_dir), then we never have to look at the compilation unit debug - // info. If we provide an absolute path to LLVM here for the compilation unit debug info, - // LLVM will emit DWARF info that depends on DW_AT_comp_dir. To avoid this, we pass "." - // for the compilation unit directory. This forces each debug file to have a directory - // rather than be relative to DW_AT_comp_dir. According to DWARF 5, debug files will - // no longer reference DW_AT_comp_dir, for the purpose of being able to support the - // common practice of stripping all but the line number sections from an executable. - const char *compile_unit_dir = target_os_is_darwin(g->zig_target->os) ? "." : - buf_ptr(&g->main_pkg->root_src_dir); - - ZigLLVMDIFile *compile_unit_file = ZigLLVMCreateFile(g->dbuilder, buf_ptr(g->root_out_name), - compile_unit_dir); - g->compile_unit = ZigLLVMCreateCompileUnit(g->dbuilder, ZigLLVMLang_DW_LANG_C99(), - compile_unit_file, buf_ptr(producer), is_optimized, flags, runtime_version, - "", 0, !g->strip_debug_symbols); - - // This is for debug stuff that doesn't have a real file. - g->dummy_di_file = nullptr; - - define_builtin_types(g); - define_intern_values(g); - - IrInstGen *sentinel_instructions = heap::c_allocator.allocate(2); - g->invalid_inst_gen = &sentinel_instructions[0]; - g->invalid_inst_gen->value = g->pass1_arena->create(); - g->invalid_inst_gen->value->type = g->builtin_types.entry_invalid; - - g->unreach_instruction = &sentinel_instructions[1]; - g->unreach_instruction->value = g->pass1_arena->create(); - g->unreach_instruction->value->type = g->builtin_types.entry_unreachable; - - g->invalid_inst_src = heap::c_allocator.create(); - - define_builtin_fns(g); - Error err; - if ((err = define_builtin_compile_vars(g))) { - fprintf(stderr, "Unable to create builtin.zig: %s\n", err_str(err)); - exit(1); - } -} - -static void detect_libc(CodeGen *g) { - Error err; - - if (g->libc != nullptr || g->libc_link_lib == nullptr) - return; - - if (target_can_build_libc(g->zig_target)) { - const char *generic_name = target_libc_generic_name(g->zig_target); - const char *arch_name = target_arch_name(g->zig_target->arch); - const char *abi_name = target_abi_name(g->zig_target->abi); - if (target_is_musl(g->zig_target)) { - // musl has some overrides. its headers are ABI-agnostic and so they all have the "musl" ABI name. - abi_name = "musl"; - // some architectures are handled by the same set of headers - arch_name = target_arch_musl_name(g->zig_target->arch); - } - Buf *arch_include_dir = buf_sprintf("%s" OS_SEP "libc" OS_SEP "include" OS_SEP "%s-%s-%s", - buf_ptr(g->zig_lib_dir), arch_name, target_os_name(g->zig_target->os), abi_name); - Buf *generic_include_dir = buf_sprintf("%s" OS_SEP "libc" OS_SEP "include" OS_SEP "generic-%s", - buf_ptr(g->zig_lib_dir), generic_name); - Buf *arch_os_include_dir = buf_sprintf("%s" OS_SEP "libc" OS_SEP "include" OS_SEP "%s-%s-any", - buf_ptr(g->zig_lib_dir), target_arch_name(g->zig_target->arch), target_os_name(g->zig_target->os)); - Buf *generic_os_include_dir = buf_sprintf("%s" OS_SEP "libc" OS_SEP "include" OS_SEP "any-%s-any", - buf_ptr(g->zig_lib_dir), target_os_name(g->zig_target->os)); - - g->libc_include_dir_len = 4; - g->libc_include_dir_list = heap::c_allocator.allocate(g->libc_include_dir_len); - g->libc_include_dir_list[0] = buf_ptr(arch_include_dir); - g->libc_include_dir_list[1] = buf_ptr(generic_include_dir); - g->libc_include_dir_list[2] = buf_ptr(arch_os_include_dir); - g->libc_include_dir_list[3] = buf_ptr(generic_os_include_dir); - return; - } - - if (g->zig_target->is_native_os) { - g->libc = heap::c_allocator.create(); - - if ((err = stage2_libc_find_native(g->libc))) { - fprintf(stderr, - "Unable to link against libc: Unable to find libc installation: %s\n" - "See `zig libc --help` for more details.\n", err_str(err)); - exit(1); - } - - bool want_sys_dir = !mem_eql_mem(g->libc->include_dir, g->libc->include_dir_len, - g->libc->sys_include_dir, g->libc->sys_include_dir_len); - size_t want_um_and_shared_dirs = (g->zig_target->os == OsWindows) ? 2 : 0; - size_t dir_count = 1 + want_sys_dir + want_um_and_shared_dirs; - g->libc_include_dir_len = 0; - g->libc_include_dir_list = heap::c_allocator.allocate(dir_count); - - g->libc_include_dir_list[g->libc_include_dir_len] = buf_ptr(buf_create_from_mem( - g->libc->include_dir, g->libc->include_dir_len)); - g->libc_include_dir_len += 1; - - if (want_sys_dir) { - g->libc_include_dir_list[g->libc_include_dir_len] = buf_ptr(buf_create_from_mem( - g->libc->sys_include_dir, g->libc->sys_include_dir_len)); - g->libc_include_dir_len += 1; - } - - if (want_um_and_shared_dirs != 0) { - Buf *include_dir_parent = buf_alloc(); - os_path_join(buf_create_from_mem(g->libc->include_dir, g->libc->include_dir_len), - buf_create_from_str(".."), include_dir_parent); - - Buf *buff1 = buf_alloc(); - os_path_join(include_dir_parent, buf_create_from_str("um"), buff1); - g->libc_include_dir_list[g->libc_include_dir_len] = buf_ptr(buff1); - g->libc_include_dir_len += 1; - - Buf *buff2 = buf_alloc(); - os_path_join(include_dir_parent, buf_create_from_str("shared"), buff2); - g->libc_include_dir_list[g->libc_include_dir_len] = buf_ptr(buff2); - g->libc_include_dir_len += 1; - } - assert(g->libc_include_dir_len == dir_count); - } else if ((g->out_type == OutTypeExe || (g->out_type == OutTypeLib && g->is_dynamic)) && - !target_os_is_darwin(g->zig_target->os)) - { - Buf triple_buf = BUF_INIT; - target_triple_zig(&triple_buf, g->zig_target); - fprintf(stderr, - "Zig is unable to provide a libc for the chosen target '%s'.\n" - "The target is non-native, so Zig also cannot use the native libc installation.\n" - "Choose a target which has a libc available (see `zig targets`), or\n" - "provide a libc installation text file (see `zig libc --help`).\n", buf_ptr(&triple_buf)); - exit(1); - } -} - -// does not add the "cc" arg -void add_cc_args(CodeGen *g, ZigList &args, const char *out_dep_path, - bool translate_c, FileExt source_kind) -{ - if (translate_c) { - args.append("-x"); - args.append("c"); - } - - args.append("-nostdinc"); - if (source_kind == FileExtCpp) { - args.append("-nostdinc++"); - } - args.append("-fno-spell-checking"); - - if (g->function_sections) { - args.append("-ffunction-sections"); - } - - if (!translate_c) { - switch (g->err_color) { - case ErrColorAuto: - break; - case ErrColorOff: - args.append("-fno-color-diagnostics"); - args.append("-fno-caret-diagnostics"); - break; - case ErrColorOn: - args.append("-fcolor-diagnostics"); - args.append("-fcaret-diagnostics"); - break; - } - } - - for (size_t i = 0; i < g->framework_dirs.length; i += 1) { - args.append("-iframework"); - args.append(g->framework_dirs.at(i)); - } - - if (g->libcpp_link_lib != nullptr) { - const char *libcxx_include_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxx" OS_SEP "include", - buf_ptr(g->zig_lib_dir))); - - const char *libcxxabi_include_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxxabi" OS_SEP "include", - buf_ptr(g->zig_lib_dir))); - - args.append("-isystem"); - args.append(libcxx_include_path); - - args.append("-isystem"); - args.append(libcxxabi_include_path); - - if (target_abi_is_musl(g->zig_target->abi)) { - args.append("-D_LIBCPP_HAS_MUSL_LIBC"); - } - args.append("-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS"); - args.append("-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS"); - } - - args.append("-target"); - args.append(buf_ptr(&g->llvm_triple_str)); - - switch (source_kind) { - case FileExtC: - case FileExtCpp: - case FileExtHeader: - // According to Rich Felker libc headers are supposed to go before C language headers. - // However as noted by @dimenus, appending libc headers before c_headers breaks intrinsics - // and other compiler specific items. - args.append("-isystem"); - args.append(buf_ptr(g->zig_c_headers_dir)); - - for (size_t i = 0; i < g->libc_include_dir_len; i += 1) { - const char *include_dir = g->libc_include_dir_list[i]; - args.append("-isystem"); - args.append(include_dir); - } - - if (g->zig_target->llvm_cpu_name != nullptr) { - args.append("-Xclang"); - args.append("-target-cpu"); - args.append("-Xclang"); - args.append(g->zig_target->llvm_cpu_name); - } - if (g->zig_target->llvm_cpu_features != nullptr) { - // https://github.com/ziglang/zig/issues/5017 - SplitIterator it = memSplit(str(g->zig_target->llvm_cpu_features), str(",")); - Optional> flag = SplitIterator_next(&it); - while (flag.is_some) { - args.append("-Xclang"); - args.append("-target-feature"); - args.append("-Xclang"); - args.append(buf_ptr(buf_create_from_slice(flag.value))); - flag = SplitIterator_next(&it); - } - } - if (translate_c) { - // this gives us access to preprocessing entities, presumably at - // the cost of performance - args.append("-Xclang"); - args.append("-detailed-preprocessing-record"); - } - if (out_dep_path != nullptr) { - args.append("-MD"); - args.append("-MV"); - args.append("-MF"); - args.append(out_dep_path); - } - break; - case FileExtAsm: - case FileExtLLVMIr: - case FileExtLLVMBitCode: - case FileExtUnknown: - break; - } - for (size_t i = 0; i < g->zig_target->llvm_cpu_features_asm_len; i += 1) { - args.append(g->zig_target->llvm_cpu_features_asm_ptr[i]); - } - - if (g->zig_target->os == OsFreestanding) { - args.append("-ffreestanding"); - } - - // windows.h has files such as pshpack1.h which do #pragma packing, triggering a clang warning. - // So for this target, we disable this warning. - if (g->zig_target->os == OsWindows && target_abi_is_gnu(g->zig_target->abi)) { - args.append("-Wno-pragma-pack"); - } - - if (!g->strip_debug_symbols) { - args.append("-g"); - } - - if (codegen_have_frame_pointer(g)) { - args.append("-fno-omit-frame-pointer"); - } else { - args.append("-fomit-frame-pointer"); - } - - if (g->have_sanitize_c) { - args.append("-fsanitize=undefined"); - args.append("-fsanitize-trap=undefined"); - } - - switch (g->build_mode) { - case BuildModeDebug: - // windows c runtime requires -D_DEBUG if using debug libraries - args.append("-D_DEBUG"); - args.append("-Og"); - - if (g->libc_link_lib != nullptr) { - args.append("-fstack-protector-strong"); - args.append("--param"); - args.append("ssp-buffer-size=4"); - } else { - args.append("-fno-stack-protector"); - } - break; - case BuildModeSafeRelease: - // See the comment in the BuildModeFastRelease case for why we pass -O2 rather - // than -O3 here. - args.append("-O2"); - if (g->libc_link_lib != nullptr) { - args.append("-D_FORTIFY_SOURCE=2"); - args.append("-fstack-protector-strong"); - args.append("--param"); - args.append("ssp-buffer-size=4"); - } else { - args.append("-fno-stack-protector"); - } - break; - case BuildModeFastRelease: - args.append("-DNDEBUG"); - // Here we pass -O2 rather than -O3 because, although we do the equivalent of - // -O3 in Zig code, the justification for the difference here is that Zig - // has better detection and prevention of undefined behavior, so -O3 is safer for - // Zig code than it is for C code. Also, C programmers are used to their code - // running in -O2 and thus the -O3 path has been tested less. - args.append("-O2"); - args.append("-fno-stack-protector"); - break; - case BuildModeSmallRelease: - args.append("-DNDEBUG"); - args.append("-Os"); - args.append("-fno-stack-protector"); - break; - } - - if (target_supports_fpic(g->zig_target) && g->have_pic) { - args.append("-fPIC"); - } - - for (size_t arg_i = 0; arg_i < g->clang_argv_len; arg_i += 1) { - args.append(g->clang_argv[arg_i]); - } - -} - -void codegen_translate_c(CodeGen *g, Buf *full_path) { - Error err; - - Buf *src_basename = buf_alloc(); - Buf *src_dirname = buf_alloc(); - os_path_split(full_path, src_dirname, src_basename); - - Buf noextname = BUF_INIT; - os_path_extname(src_basename, &noextname, nullptr); - - Buf *zig_basename = buf_sprintf("%s.zig", buf_ptr(&noextname)); - - detect_libc(g); - - Buf cache_digest = BUF_INIT; - buf_resize(&cache_digest, 0); - - CacheHash *cache_hash = nullptr; - if (g->enable_cache) { - if ((err = create_c_object_cache(g, &cache_hash, true))) { - // Already printed error; verbose = true - exit(1); - } - cache_file(cache_hash, full_path); - // to distinguish from generating a C object - cache_buf(cache_hash, buf_create_from_str("translate-c")); - - if ((err = cache_hit(cache_hash, &cache_digest))) { - if (err != ErrorInvalidFormat) { - fprintf(stderr, "unable to check cache: %s\n", err_str(err)); - exit(1); - } - } - if (cache_hash->manifest_file_path != nullptr) { - g->caches_to_release.append(cache_hash); - } - } - - if (g->enable_cache && buf_len(&cache_digest) != 0) { - // cache hit - Buf *cached_path = buf_sprintf("%s" OS_SEP CACHE_OUT_SUBDIR OS_SEP "%s" OS_SEP "%s", - buf_ptr(g->cache_dir), buf_ptr(&cache_digest), buf_ptr(zig_basename)); - fprintf(stdout, "%s\n", buf_ptr(cached_path)); - return; - } - - // cache miss or cache disabled - init(g); - - Buf *out_dep_path = nullptr; - const char *out_dep_path_cstr = nullptr; - - if (g->enable_cache) { - buf_alloc();// we can't know the digest until we do the C compiler invocation, so we - // need a tmp filename. - out_dep_path = buf_alloc(); - if ((err = get_tmp_filename(g, out_dep_path, buf_sprintf("%s.d", buf_ptr(zig_basename))))) { - fprintf(stderr, "unable to create tmp dir: %s\n", err_str(err)); - exit(1); - } - out_dep_path_cstr = buf_ptr(out_dep_path); - } - - ZigList clang_argv = {0}; - add_cc_args(g, clang_argv, out_dep_path_cstr, true, FileExtC); - - clang_argv.append(buf_ptr(full_path)); - - if (g->verbose_cc) { - fprintf(stderr, "clang"); - for (size_t i = 0; i < clang_argv.length; i += 1) { - fprintf(stderr, " %s", clang_argv.at(i)); - } - fprintf(stderr, "\n"); - } - - clang_argv.append(nullptr); // to make the [start...end] argument work - - const char *resources_path = buf_ptr(g->zig_c_headers_dir); - Stage2ErrorMsg *errors_ptr; - size_t errors_len; - Stage2Ast *ast; - - err = stage2_translate_c(&ast, &errors_ptr, &errors_len, - &clang_argv.at(0), &clang_argv.last(), resources_path); - - if (err == ErrorCCompileErrors && errors_len > 0) { - for (size_t i = 0; i < errors_len; i += 1) { - Stage2ErrorMsg *clang_err = &errors_ptr[i]; - - ErrorMsg *err_msg = err_msg_create_with_offset( - clang_err->filename_ptr ? - buf_create_from_mem(clang_err->filename_ptr, clang_err->filename_len) : nullptr, - clang_err->line, clang_err->column, clang_err->offset, clang_err->source, - buf_create_from_mem(clang_err->msg_ptr, clang_err->msg_len)); - print_err_msg(err_msg, g->err_color); - } - exit(1); - } - - if (err) { - fprintf(stderr, "unable to parse C file: %s\n", err_str(err)); - exit(1); - } - - if (!g->enable_cache) { - stage2_render_ast(ast, stdout); - return; - } - - // add the files depended on to the cache system - if ((err = cache_add_dep_file(cache_hash, out_dep_path, true))) { - // Don't treat the absence of the .d file as a fatal error, the - // compiler may not produce one eg. when compiling .s files - if (err != ErrorFileNotFound) { - fprintf(stderr, "Failed to add C source dependencies to cache: %s\n", err_str(err)); - exit(1); - } - } - if (err != ErrorFileNotFound) { - os_delete_file(out_dep_path); - } - - if ((err = cache_final(cache_hash, &cache_digest))) { - fprintf(stderr, "Unable to finalize cache hash: %s\n", err_str(err)); - exit(1); - } - - Buf *artifact_dir = buf_sprintf("%s" OS_SEP CACHE_OUT_SUBDIR OS_SEP "%s", - buf_ptr(g->cache_dir), buf_ptr(&cache_digest)); - - if ((err = os_make_path(artifact_dir))) { - fprintf(stderr, "Unable to make dir: %s\n", err_str(err)); - exit(1); - } - - Buf *cached_path = buf_sprintf("%s" OS_SEP "%s", buf_ptr(artifact_dir), buf_ptr(zig_basename)); - - FILE *out_file = fopen(buf_ptr(cached_path), "wb"); - if (out_file == nullptr) { - fprintf(stderr, "Unable to open output file: %s\n", strerror(errno)); - exit(1); - } - stage2_render_ast(ast, out_file); - if (fclose(out_file) != 0) { - fprintf(stderr, "Unable to write to output file: %s\n", strerror(errno)); - exit(1); - } - fprintf(stdout, "%s\n", buf_ptr(cached_path)); -} - -static void update_test_functions_builtin_decl(CodeGen *g) { - Error err; - - assert(g->is_test_build); - - if (g->test_fns.length == 0) { - fprintf(stderr, "No tests to run.\n"); - exit(0); - } - - ZigType *fn_type = get_test_fn_type(g); - - ZigValue *test_fn_type_val = get_builtin_value(g, "TestFn"); - assert(test_fn_type_val->type->id == ZigTypeIdMetaType); - ZigType *struct_type = test_fn_type_val->data.x_type; - if ((err = type_resolve(g, struct_type, ResolveStatusSizeKnown))) - zig_unreachable(); - - ZigValue *test_fn_array = g->pass1_arena->create(); - test_fn_array->type = get_array_type(g, struct_type, g->test_fns.length, nullptr); - test_fn_array->special = ConstValSpecialStatic; - test_fn_array->data.x_array.data.s_none.elements = g->pass1_arena->allocate(g->test_fns.length); - - for (size_t i = 0; i < g->test_fns.length; i += 1) { - ZigFn *test_fn_entry = g->test_fns.at(i); - - ZigValue *this_val = &test_fn_array->data.x_array.data.s_none.elements[i]; - this_val->special = ConstValSpecialStatic; - this_val->type = struct_type; - this_val->parent.id = ConstParentIdArray; - this_val->parent.data.p_array.array_val = test_fn_array; - this_val->parent.data.p_array.elem_index = i; - this_val->data.x_struct.fields = alloc_const_vals_ptrs(g, 3); - - ZigValue *name_field = this_val->data.x_struct.fields[0]; - ZigValue *name_array_val = create_const_str_lit(g, &test_fn_entry->symbol_name)->data.x_ptr.data.ref.pointee; - init_const_slice(g, name_field, name_array_val, 0, buf_len(&test_fn_entry->symbol_name), true); - - ZigValue *fn_field = this_val->data.x_struct.fields[1]; - fn_field->type = fn_type; - fn_field->special = ConstValSpecialStatic; - fn_field->data.x_ptr.special = ConstPtrSpecialFunction; - fn_field->data.x_ptr.mut = ConstPtrMutComptimeConst; - fn_field->data.x_ptr.data.fn.fn_entry = test_fn_entry; - - ZigValue *frame_size_field = this_val->data.x_struct.fields[2]; - frame_size_field->type = get_optional_type(g, g->builtin_types.entry_usize); - frame_size_field->special = ConstValSpecialStatic; - frame_size_field->data.x_optional = nullptr; - - if (fn_is_async(test_fn_entry)) { - frame_size_field->data.x_optional = g->pass1_arena->create(); - frame_size_field->data.x_optional->special = ConstValSpecialStatic; - frame_size_field->data.x_optional->type = g->builtin_types.entry_usize; - bigint_init_unsigned(&frame_size_field->data.x_optional->data.x_bigint, - test_fn_entry->frame_type->abi_size); - } - } - report_errors_and_maybe_exit(g); - - ZigValue *test_fn_slice = create_const_slice(g, test_fn_array, 0, g->test_fns.length, true); - - update_compile_var(g, buf_create_from_str("test_functions"), test_fn_slice); - assert(g->test_runner_package != nullptr); -} - -static Buf *get_resolved_root_src_path(CodeGen *g) { - // TODO memoize - if (buf_len(&g->main_pkg->root_src_path) == 0) - return nullptr; - - Buf rel_full_path = BUF_INIT; - os_path_join(&g->main_pkg->root_src_dir, &g->main_pkg->root_src_path, &rel_full_path); - - Buf *resolved_path = buf_alloc(); - Buf *resolve_paths[] = {&rel_full_path}; - *resolved_path = os_path_resolve(resolve_paths, 1); - - return resolved_path; -} - -static void gen_root_source(CodeGen *g) { - Buf *resolved_path = get_resolved_root_src_path(g); - if (resolved_path == nullptr) - return; - - Buf *source_code = buf_alloc(); - Error err; - // No need for using the caching system for this file fetch because it is handled - // separately. - if ((err = os_fetch_file_path(resolved_path, source_code))) { - fprintf(stderr, "unable to open '%s': %s\n", buf_ptr(resolved_path), err_str(err)); - exit(1); - } - - ZigType *root_import_alias = add_source_file(g, g->main_pkg, resolved_path, source_code, SourceKindRoot); - assert(root_import_alias == g->root_import); - - assert(g->root_out_name); - assert(g->out_type != OutTypeUnknown); - - if (!g->is_dummy_so) { - // Zig has lazy top level definitions. Here we semantically analyze the panic function. - Buf *import_target_path; - Buf full_path = BUF_INIT; - ZigType *std_import; - if ((err = analyze_import(g, g->root_import, buf_create_from_str("std"), &std_import, - &import_target_path, &full_path))) - { - if (err == ErrorFileNotFound) { - fprintf(stderr, "unable to find '%s'", buf_ptr(import_target_path)); - } else { - fprintf(stderr, "unable to open '%s': %s\n", buf_ptr(&full_path), err_str(err)); - } - exit(1); - } - - Tld *builtin_tld = find_decl(g, &get_container_scope(std_import)->base, - buf_create_from_str("builtin")); - assert(builtin_tld != nullptr); - resolve_top_level_decl(g, builtin_tld, nullptr, false); - report_errors_and_maybe_exit(g); - assert(builtin_tld->id == TldIdVar); - TldVar *builtin_tld_var = (TldVar*)builtin_tld; - ZigValue *builtin_val = builtin_tld_var->var->const_value; - assert(builtin_val->type->id == ZigTypeIdMetaType); - ZigType *builtin_type = builtin_val->data.x_type; - - Tld *panic_tld = find_decl(g, &get_container_scope(builtin_type)->base, - buf_create_from_str("panic")); - assert(panic_tld != nullptr); - resolve_top_level_decl(g, panic_tld, nullptr, false); - report_errors_and_maybe_exit(g); - assert(panic_tld->id == TldIdVar); - TldVar *panic_tld_var = (TldVar*)panic_tld; - ZigValue *panic_fn_val = panic_tld_var->var->const_value; - assert(panic_fn_val->type->id == ZigTypeIdFn); - assert(panic_fn_val->data.x_ptr.special == ConstPtrSpecialFunction); - g->panic_fn = panic_fn_val->data.x_ptr.data.fn.fn_entry; - assert(g->panic_fn != nullptr); - } - - if (!g->error_during_imports) { - semantic_analyze(g); - } - report_errors_and_maybe_exit(g); - - if (g->is_test_build) { - update_test_functions_builtin_decl(g); - if (!g->error_during_imports) { - semantic_analyze(g); - } - } - - report_errors_and_maybe_exit(g); - -} - -static void print_zig_cc_cmd(ZigList *args) { - for (size_t arg_i = 0; arg_i < args->length; arg_i += 1) { - const char *space_str = (arg_i == 0) ? "" : " "; - fprintf(stderr, "%s%s", space_str, args->at(arg_i)); - } - fprintf(stderr, "\n"); -} - -// Caller should delete the file when done or rename it into a better location. -static Error get_tmp_filename(CodeGen *g, Buf *out, Buf *suffix) { - Error err; - buf_resize(out, 0); - os_path_join(g->cache_dir, buf_create_from_str("tmp" OS_SEP), out); - if ((err = os_make_path(out))) { - return err; - } - const char base64[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"; - assert(array_length(base64) == 64 + 1); - for (size_t i = 0; i < 12; i += 1) { - buf_append_char(out, base64[rand() % 64]); - } - buf_append_char(out, '-'); - buf_append_buf(out, suffix); - return ErrorNone; -} - -Error create_c_object_cache(CodeGen *g, CacheHash **out_cache_hash, bool verbose) { - Error err; - CacheHash *cache_hash = heap::c_allocator.create(); - Buf *manifest_dir = buf_sprintf("%s" OS_SEP CACHE_HASH_SUBDIR, buf_ptr(g->cache_dir)); - cache_init(cache_hash, manifest_dir); - - Buf *compiler_id; - if ((err = get_compiler_id(&compiler_id))) { - if (verbose) { - fprintf(stderr, "unable to get compiler id: %s\n", err_str(err)); - } - return err; - } - cache_buf(cache_hash, compiler_id); - cache_int(cache_hash, g->err_color); - cache_list_of_str(cache_hash, g->framework_dirs.items, g->framework_dirs.length); - cache_bool(cache_hash, g->libcpp_link_lib != nullptr); - cache_buf(cache_hash, g->zig_lib_dir); - cache_buf(cache_hash, g->zig_c_headers_dir); - cache_list_of_str(cache_hash, g->libc_include_dir_list, g->libc_include_dir_len); - cache_int(cache_hash, g->zig_target->is_native_os); - cache_int(cache_hash, g->zig_target->is_native_cpu); - cache_int(cache_hash, g->zig_target->arch); - cache_int(cache_hash, g->zig_target->vendor); - cache_int(cache_hash, g->zig_target->os); - cache_int(cache_hash, g->zig_target->abi); - cache_bool(cache_hash, g->strip_debug_symbols); - cache_int(cache_hash, g->build_mode); - cache_bool(cache_hash, g->have_pic); - cache_bool(cache_hash, g->have_sanitize_c); - cache_bool(cache_hash, want_valgrind_support(g)); - cache_bool(cache_hash, g->function_sections); - cache_int(cache_hash, g->code_model); - cache_bool(cache_hash, codegen_have_frame_pointer(g)); - cache_bool(cache_hash, g->libc_link_lib); - if (g->zig_target->cache_hash != nullptr) { - cache_mem(cache_hash, g->zig_target->cache_hash, g->zig_target->cache_hash_len); - } - - for (size_t arg_i = 0; arg_i < g->clang_argv_len; arg_i += 1) { - cache_str(cache_hash, g->clang_argv[arg_i]); - } - - *out_cache_hash = cache_hash; - return ErrorNone; -} - -static bool need_llvm_module(CodeGen *g) { - return buf_len(&g->main_pkg->root_src_path) != 0; -} - -// before gen_c_objects -static bool main_output_dir_is_just_one_c_object_pre(CodeGen *g) { - return g->enable_cache && g->c_source_files.length == 1 && !need_llvm_module(g) && - g->out_type == OutTypeObj && g->link_objects.length == 0; -} - -// after gen_c_objects -static bool main_output_dir_is_just_one_c_object_post(CodeGen *g) { - return g->enable_cache && g->link_objects.length == 1 && !need_llvm_module(g) && g->out_type == OutTypeObj; -} - -// returns true if it was a cache miss -static void gen_c_object(CodeGen *g, Buf *self_exe_path, CFile *c_file) { - Error err; - - Buf *artifact_dir; - Buf *o_final_path; - - Buf *o_dir = buf_sprintf("%s" OS_SEP CACHE_OUT_SUBDIR, buf_ptr(g->cache_dir)); - - Buf *c_source_file = buf_create_from_str(c_file->source_path); - Buf *c_source_basename = buf_alloc(); - os_path_split(c_source_file, nullptr, c_source_basename); - - Stage2ProgressNode *child_prog_node = stage2_progress_start(g->sub_progress_node, buf_ptr(c_source_basename), - buf_len(c_source_basename), 0); - - Buf *final_o_basename = buf_alloc(); - if (c_file->preprocessor_only_basename == nullptr) { - // We special case when doing build-obj for just one C file - if (main_output_dir_is_just_one_c_object_pre(g)) { - buf_init_from_buf(final_o_basename, g->root_out_name); - } else { - os_path_extname(c_source_basename, final_o_basename, nullptr); - } - buf_append_str(final_o_basename, target_o_file_ext(g->zig_target)); - } else { - buf_init_from_str(final_o_basename, c_file->preprocessor_only_basename); - } - - CacheHash *cache_hash; - if ((err = create_c_object_cache(g, &cache_hash, true))) { - // Already printed error; verbose = true - exit(1); - } - cache_file(cache_hash, c_source_file); - - // Note: not directory args, just args that always have a file next - static const char *file_args[] = { - "-include", - }; - for (size_t arg_i = 0; arg_i < c_file->args.length; arg_i += 1) { - const char *arg = c_file->args.at(arg_i); - cache_str(cache_hash, arg); - for (size_t file_arg_i = 0; file_arg_i < array_length(file_args); file_arg_i += 1) { - if (strcmp(arg, file_args[file_arg_i]) == 0 && arg_i + 1 < c_file->args.length) { - arg_i += 1; - cache_file(cache_hash, buf_create_from_str(c_file->args.at(arg_i))); - } - } - } - - Buf digest = BUF_INIT; - buf_resize(&digest, 0); - if ((err = cache_hit(cache_hash, &digest))) { - if (err != ErrorInvalidFormat) { - if (err == ErrorCacheUnavailable) { - // already printed error - } else { - fprintf(stderr, "unable to check cache when compiling C object: %s\n", err_str(err)); - } - exit(1); - } - } - bool is_cache_miss = g->disable_c_depfile || (buf_len(&digest) == 0); - if (is_cache_miss) { - // we can't know the digest until we do the C compiler invocation, so we - // need a tmp filename. - Buf *out_obj_path = buf_alloc(); - if ((err = get_tmp_filename(g, out_obj_path, final_o_basename))) { - fprintf(stderr, "unable to create tmp dir: %s\n", err_str(err)); - exit(1); - } - - Termination term; - ZigList args = {}; - args.append(buf_ptr(self_exe_path)); - args.append("clang"); - - if (c_file->preprocessor_only_basename == nullptr) { - args.append("-c"); - } - - Buf *out_dep_path = g->disable_c_depfile ? nullptr : buf_sprintf("%s.d", buf_ptr(out_obj_path)); - const char *out_dep_path_cstr = (out_dep_path == nullptr) ? nullptr : buf_ptr(out_dep_path); - FileExt ext = classify_file_ext(buf_ptr(c_source_basename), buf_len(c_source_basename)); - add_cc_args(g, args, out_dep_path_cstr, false, ext); - - args.append("-o"); - args.append(buf_ptr(out_obj_path)); - - args.append(buf_ptr(c_source_file)); - - for (size_t arg_i = 0; arg_i < c_file->args.length; arg_i += 1) { - args.append(c_file->args.at(arg_i)); - } - - if (g->verbose_cc) { - print_zig_cc_cmd(&args); - } - os_spawn_process(args, &term); - if (term.how != TerminationIdClean || term.code != 0) { - fprintf(stderr, "\nThe following command failed:\n"); - print_zig_cc_cmd(&args); - exit(1); - } - - if (out_dep_path != nullptr) { - // add the files depended on to the cache system - if ((err = cache_add_dep_file(cache_hash, out_dep_path, true))) { - // Don't treat the absence of the .d file as a fatal error, the - // compiler may not produce one eg. when compiling .s files - if (err != ErrorFileNotFound) { - fprintf(stderr, "Failed to add C source dependencies to cache: %s\n", err_str(err)); - exit(1); - } - } - if (err != ErrorFileNotFound) { - os_delete_file(out_dep_path); - } - - if ((err = cache_final(cache_hash, &digest))) { - fprintf(stderr, "Unable to finalize cache hash: %s\n", err_str(err)); - exit(1); - } - } - artifact_dir = buf_alloc(); - os_path_join(o_dir, &digest, artifact_dir); - if ((err = os_make_path(artifact_dir))) { - fprintf(stderr, "Unable to create output directory '%s': %s", - buf_ptr(artifact_dir), err_str(err)); - exit(1); - } - o_final_path = buf_alloc(); - os_path_join(artifact_dir, final_o_basename, o_final_path); - if ((err = os_rename(out_obj_path, o_final_path))) { - fprintf(stderr, "Unable to rename object: %s\n", err_str(err)); - exit(1); - } - } else { - // cache hit - artifact_dir = buf_alloc(); - os_path_join(o_dir, &digest, artifact_dir); - o_final_path = buf_alloc(); - os_path_join(artifact_dir, final_o_basename, o_final_path); - } - - g->c_artifact_dir = artifact_dir; - g->link_objects.append(o_final_path); - g->caches_to_release.append(cache_hash); - - stage2_progress_end(child_prog_node); -} - -// returns true if we had any cache misses -static void gen_c_objects(CodeGen *g) { - Error err; - - if (g->c_source_files.length == 0) - return; - - Buf *self_exe_path = buf_alloc(); - if ((err = os_self_exe_path(self_exe_path))) { - fprintf(stderr, "Unable to get self exe path: %s\n", err_str(err)); - exit(1); - } - - codegen_add_time_event(g, "Compile C Objects"); - const char *c_prog_name = "Compile C Objects"; - codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node, c_prog_name, strlen(c_prog_name), - g->c_source_files.length)); - - for (size_t c_file_i = 0; c_file_i < g->c_source_files.length; c_file_i += 1) { - CFile *c_file = g->c_source_files.at(c_file_i); - gen_c_object(g, self_exe_path, c_file); - } -} - -void codegen_add_object(CodeGen *g, Buf *object_path) { - g->link_objects.append(object_path); -} - -// Must be coordinated with with CIntType enum -static const char *c_int_type_names[] = { - "short", - "unsigned short", - "int", - "unsigned int", - "long", - "unsigned long", - "long long", - "unsigned long long", -}; - -struct GenH { - ZigList types_to_declare; -}; - -static void prepend_c_type_to_decl_list(CodeGen *g, GenH *gen_h, ZigType *type_entry) { - if (type_entry->gen_h_loop_flag) - return; - type_entry->gen_h_loop_flag = true; - - switch (type_entry->id) { - case ZigTypeIdInvalid: - case ZigTypeIdMetaType: - case ZigTypeIdComptimeFloat: - case ZigTypeIdComptimeInt: - case ZigTypeIdEnumLiteral: - case ZigTypeIdUndefined: - case ZigTypeIdNull: - case ZigTypeIdBoundFn: - case ZigTypeIdErrorUnion: - case ZigTypeIdErrorSet: - case ZigTypeIdFnFrame: - case ZigTypeIdAnyFrame: - zig_unreachable(); - case ZigTypeIdVoid: - case ZigTypeIdUnreachable: - return; - case ZigTypeIdBool: - g->c_want_stdbool = true; - return; - case ZigTypeIdInt: - g->c_want_stdint = true; - return; - case ZigTypeIdFloat: - return; - case ZigTypeIdOpaque: - gen_h->types_to_declare.append(type_entry); - return; - case ZigTypeIdStruct: - if(type_entry->data.structure.layout == ContainerLayoutExtern) { - for (uint32_t i = 0; i < type_entry->data.structure.src_field_count; i += 1) { - TypeStructField *field = type_entry->data.structure.fields[i]; - prepend_c_type_to_decl_list(g, gen_h, field->type_entry); - } - } - gen_h->types_to_declare.append(type_entry); - return; - case ZigTypeIdUnion: - for (uint32_t i = 0; i < type_entry->data.unionation.src_field_count; i += 1) { - TypeUnionField *field = &type_entry->data.unionation.fields[i]; - prepend_c_type_to_decl_list(g, gen_h, field->type_entry); - } - gen_h->types_to_declare.append(type_entry); - return; - case ZigTypeIdEnum: - prepend_c_type_to_decl_list(g, gen_h, type_entry->data.enumeration.tag_int_type); - gen_h->types_to_declare.append(type_entry); - return; - case ZigTypeIdPointer: - prepend_c_type_to_decl_list(g, gen_h, type_entry->data.pointer.child_type); - return; - case ZigTypeIdArray: - prepend_c_type_to_decl_list(g, gen_h, type_entry->data.array.child_type); - return; - case ZigTypeIdVector: - prepend_c_type_to_decl_list(g, gen_h, type_entry->data.vector.elem_type); - return; - case ZigTypeIdOptional: - prepend_c_type_to_decl_list(g, gen_h, type_entry->data.maybe.child_type); - return; - case ZigTypeIdFn: - for (size_t i = 0; i < type_entry->data.fn.fn_type_id.param_count; i += 1) { - prepend_c_type_to_decl_list(g, gen_h, type_entry->data.fn.fn_type_id.param_info[i].type); - } - prepend_c_type_to_decl_list(g, gen_h, type_entry->data.fn.fn_type_id.return_type); - return; - } -} - -static void get_c_type(CodeGen *g, GenH *gen_h, ZigType *type_entry, Buf *out_buf) { - assert(type_entry); - - for (size_t i = 0; i < array_length(c_int_type_names); i += 1) { - if (type_entry == g->builtin_types.entry_c_int[i]) { - buf_init_from_str(out_buf, c_int_type_names[i]); - return; - } - } - if (type_entry == g->builtin_types.entry_c_longdouble) { - buf_init_from_str(out_buf, "long double"); - return; - } - if (type_entry == g->builtin_types.entry_c_void) { - buf_init_from_str(out_buf, "void"); - return; - } - if (type_entry == g->builtin_types.entry_isize) { - g->c_want_stdint = true; - buf_init_from_str(out_buf, "intptr_t"); - return; - } - if (type_entry == g->builtin_types.entry_usize) { - g->c_want_stdint = true; - buf_init_from_str(out_buf, "uintptr_t"); - return; - } - - prepend_c_type_to_decl_list(g, gen_h, type_entry); - - switch (type_entry->id) { - case ZigTypeIdVoid: - buf_init_from_str(out_buf, "void"); - break; - case ZigTypeIdBool: - buf_init_from_str(out_buf, "bool"); - break; - case ZigTypeIdUnreachable: - buf_init_from_str(out_buf, "__attribute__((__noreturn__)) void"); - break; - case ZigTypeIdFloat: - switch (type_entry->data.floating.bit_count) { - case 32: - buf_init_from_str(out_buf, "float"); - break; - case 64: - buf_init_from_str(out_buf, "double"); - break; - case 80: - buf_init_from_str(out_buf, "__float80"); - break; - case 128: - buf_init_from_str(out_buf, "__float128"); - break; - default: - zig_unreachable(); - } - break; - case ZigTypeIdInt: - buf_resize(out_buf, 0); - buf_appendf(out_buf, "%sint%" PRIu32 "_t", - type_entry->data.integral.is_signed ? "" : "u", - type_entry->data.integral.bit_count); - break; - case ZigTypeIdPointer: - { - Buf child_buf = BUF_INIT; - ZigType *child_type = type_entry->data.pointer.child_type; - get_c_type(g, gen_h, child_type, &child_buf); - - const char *const_str = type_entry->data.pointer.is_const ? "const " : ""; - buf_resize(out_buf, 0); - buf_appendf(out_buf, "%s%s *", const_str, buf_ptr(&child_buf)); - break; - } - case ZigTypeIdOptional: - { - ZigType *child_type = type_entry->data.maybe.child_type; - if (!type_has_bits(g, child_type)) { - buf_init_from_str(out_buf, "bool"); - return; - } else if (type_is_nonnull_ptr(g, child_type)) { - return get_c_type(g, gen_h, child_type, out_buf); - } else { - zig_unreachable(); - } - } - case ZigTypeIdStruct: - case ZigTypeIdOpaque: - { - buf_init_from_str(out_buf, "struct "); - buf_append_buf(out_buf, type_h_name(type_entry)); - return; - } - case ZigTypeIdUnion: - { - buf_init_from_str(out_buf, "union "); - buf_append_buf(out_buf, type_h_name(type_entry)); - return; - } - case ZigTypeIdEnum: - { - buf_init_from_str(out_buf, "enum "); - buf_append_buf(out_buf, type_h_name(type_entry)); - return; - } - case ZigTypeIdArray: - { - ZigTypeArray *array_data = &type_entry->data.array; - - Buf *child_buf = buf_alloc(); - get_c_type(g, gen_h, array_data->child_type, child_buf); - - buf_resize(out_buf, 0); - buf_appendf(out_buf, "%s", buf_ptr(child_buf)); - return; - } - case ZigTypeIdVector: - zig_panic("TODO implement get_c_type for vector types"); - case ZigTypeIdErrorUnion: - case ZigTypeIdErrorSet: - case ZigTypeIdFn: - zig_panic("TODO implement get_c_type for more types"); - case ZigTypeIdInvalid: - case ZigTypeIdMetaType: - case ZigTypeIdBoundFn: - case ZigTypeIdComptimeFloat: - case ZigTypeIdComptimeInt: - case ZigTypeIdEnumLiteral: - case ZigTypeIdUndefined: - case ZigTypeIdNull: - case ZigTypeIdFnFrame: - case ZigTypeIdAnyFrame: - zig_unreachable(); - } -} - -static const char *preprocessor_alphabet1 = "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; -static const char *preprocessor_alphabet2 = "_abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; - -static bool need_to_preprocessor_mangle(Buf *src) { - for (size_t i = 0; i < buf_len(src); i += 1) { - const char *alphabet = (i == 0) ? preprocessor_alphabet1 : preprocessor_alphabet2; - uint8_t byte = buf_ptr(src)[i]; - if (strchr(alphabet, byte) == nullptr) { - return true; - } - } - return false; -} - -static Buf *preprocessor_mangle(Buf *src) { - if (!need_to_preprocessor_mangle(src)) { - return buf_create_from_buf(src); - } - Buf *result = buf_alloc(); - for (size_t i = 0; i < buf_len(src); i += 1) { - const char *alphabet = (i == 0) ? preprocessor_alphabet1 : preprocessor_alphabet2; - uint8_t byte = buf_ptr(src)[i]; - if (strchr(alphabet, byte) == nullptr) { - // perform escape - buf_appendf(result, "_%02x_", byte); - } else { - buf_append_char(result, byte); - } - } - return result; -} - -static void gen_h_file_types(CodeGen* g, GenH* gen_h, Buf* out_buf) { - for (size_t type_i = 0; type_i < gen_h->types_to_declare.length; type_i += 1) { - ZigType *type_entry = gen_h->types_to_declare.at(type_i); - switch (type_entry->id) { - case ZigTypeIdInvalid: - case ZigTypeIdMetaType: - case ZigTypeIdVoid: - case ZigTypeIdBool: - case ZigTypeIdUnreachable: - case ZigTypeIdInt: - case ZigTypeIdFloat: - case ZigTypeIdPointer: - case ZigTypeIdComptimeFloat: - case ZigTypeIdComptimeInt: - case ZigTypeIdEnumLiteral: - case ZigTypeIdArray: - case ZigTypeIdUndefined: - case ZigTypeIdNull: - case ZigTypeIdErrorUnion: - case ZigTypeIdErrorSet: - case ZigTypeIdBoundFn: - case ZigTypeIdOptional: - case ZigTypeIdFn: - case ZigTypeIdVector: - case ZigTypeIdFnFrame: - case ZigTypeIdAnyFrame: - zig_unreachable(); - - case ZigTypeIdEnum: - if (type_entry->data.enumeration.layout == ContainerLayoutExtern) { - buf_appendf(out_buf, "enum %s {\n", buf_ptr(type_h_name(type_entry))); - for (uint32_t field_i = 0; field_i < type_entry->data.enumeration.src_field_count; field_i += 1) { - TypeEnumField *enum_field = &type_entry->data.enumeration.fields[field_i]; - Buf *value_buf = buf_alloc(); - bigint_append_buf(value_buf, &enum_field->value, 10); - buf_appendf(out_buf, " %s = %s", buf_ptr(enum_field->name), buf_ptr(value_buf)); - if (field_i != type_entry->data.enumeration.src_field_count - 1) { - buf_appendf(out_buf, ","); - } - buf_appendf(out_buf, "\n"); - } - buf_appendf(out_buf, "};\n\n"); - } else { - buf_appendf(out_buf, "enum %s;\n\n", buf_ptr(type_h_name(type_entry))); - } - break; - case ZigTypeIdStruct: - if (type_entry->data.structure.layout == ContainerLayoutExtern) { - buf_appendf(out_buf, "struct %s {\n", buf_ptr(type_h_name(type_entry))); - for (uint32_t field_i = 0; field_i < type_entry->data.structure.src_field_count; field_i += 1) { - TypeStructField *struct_field = type_entry->data.structure.fields[field_i]; - - Buf *type_name_buf = buf_alloc(); - get_c_type(g, gen_h, struct_field->type_entry, type_name_buf); - - if (struct_field->type_entry->id == ZigTypeIdArray) { - buf_appendf(out_buf, " %s %s[%" ZIG_PRI_u64 "];\n", buf_ptr(type_name_buf), - buf_ptr(struct_field->name), - struct_field->type_entry->data.array.len); - } else { - buf_appendf(out_buf, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(struct_field->name)); - } - - } - buf_appendf(out_buf, "};\n\n"); - } else { - buf_appendf(out_buf, "struct %s;\n\n", buf_ptr(type_h_name(type_entry))); - } - break; - case ZigTypeIdUnion: - if (type_entry->data.unionation.layout == ContainerLayoutExtern) { - buf_appendf(out_buf, "union %s {\n", buf_ptr(type_h_name(type_entry))); - for (uint32_t field_i = 0; field_i < type_entry->data.unionation.src_field_count; field_i += 1) { - TypeUnionField *union_field = &type_entry->data.unionation.fields[field_i]; - - Buf *type_name_buf = buf_alloc(); - get_c_type(g, gen_h, union_field->type_entry, type_name_buf); - buf_appendf(out_buf, " %s %s;\n", buf_ptr(type_name_buf), buf_ptr(union_field->name)); - } - buf_appendf(out_buf, "};\n\n"); - } else { - buf_appendf(out_buf, "union %s;\n\n", buf_ptr(type_h_name(type_entry))); - } - break; - case ZigTypeIdOpaque: - buf_appendf(out_buf, "struct %s;\n\n", buf_ptr(type_h_name(type_entry))); - break; - } - } -} - -static void gen_h_file_functions(CodeGen* g, GenH* gen_h, Buf* out_buf, Buf* export_macro) { - for (size_t fn_def_i = 0; fn_def_i < g->fn_defs.length; fn_def_i += 1) { - ZigFn *fn_table_entry = g->fn_defs.at(fn_def_i); - - if (fn_table_entry->export_list.length == 0) - continue; - - FnTypeId *fn_type_id = &fn_table_entry->type_entry->data.fn.fn_type_id; - - Buf return_type_c = BUF_INIT; - get_c_type(g, gen_h, fn_type_id->return_type, &return_type_c); - - Buf *symbol_name; - if (fn_table_entry->export_list.length == 0) { - symbol_name = &fn_table_entry->symbol_name; - } else { - GlobalExport *fn_export = &fn_table_entry->export_list.items[0]; - symbol_name = &fn_export->name; - } - - if (export_macro != nullptr) { - buf_appendf(out_buf, "%s %s %s(", - buf_ptr(export_macro), - buf_ptr(&return_type_c), - buf_ptr(symbol_name)); - } else { - buf_appendf(out_buf, "%s %s(", - buf_ptr(&return_type_c), - buf_ptr(symbol_name)); - } - - Buf param_type_c = BUF_INIT; - if (fn_type_id->param_count > 0) { - for (size_t param_i = 0; param_i < fn_type_id->param_count; param_i += 1) { - FnTypeParamInfo *param_info = &fn_type_id->param_info[param_i]; - AstNode *param_decl_node = get_param_decl_node(fn_table_entry, param_i); - Buf *param_name = param_decl_node->data.param_decl.name; - - const char *comma_str = (param_i == 0) ? "" : ", "; - const char *restrict_str = param_info->is_noalias ? "restrict" : ""; - get_c_type(g, gen_h, param_info->type, ¶m_type_c); - - if (param_info->type->id == ZigTypeIdArray) { - // Arrays decay to pointers - buf_appendf(out_buf, "%s%s%s %s[]", comma_str, buf_ptr(¶m_type_c), - restrict_str, buf_ptr(param_name)); - } else { - buf_appendf(out_buf, "%s%s%s %s", comma_str, buf_ptr(¶m_type_c), - restrict_str, buf_ptr(param_name)); - } - } - buf_appendf(out_buf, ")"); - } else { - buf_appendf(out_buf, "void)"); - } - - buf_appendf(out_buf, ";\n"); - } -} - -static void gen_h_file_variables(CodeGen* g, GenH* gen_h, Buf* h_buf, Buf* export_macro) { - for (size_t exp_var_i = 0; exp_var_i < g->global_vars.length; exp_var_i += 1) { - ZigVar* var = g->global_vars.at(exp_var_i)->var; - if (var->export_list.length == 0) - continue; - - Buf var_type_c = BUF_INIT; - get_c_type(g, gen_h, var->var_type, &var_type_c); - - if (export_macro != nullptr) { - buf_appendf(h_buf, "extern %s %s %s;\n", - buf_ptr(export_macro), - buf_ptr(&var_type_c), - var->name); - } else { - buf_appendf(h_buf, "extern %s %s;\n", - buf_ptr(&var_type_c), - var->name); - } - } -} - -static void gen_h_file(CodeGen *g) { - GenH gen_h_data = {0}; - GenH *gen_h = &gen_h_data; - - assert(!g->is_test_build); - assert(!g->disable_gen_h); - - Buf *out_h_path = buf_sprintf("%s" OS_SEP "%s.h", buf_ptr(g->output_dir), buf_ptr(g->root_out_name)); - - FILE *out_h = fopen(buf_ptr(out_h_path), "wb"); - if (!out_h) - zig_panic("unable to open %s: %s\n", buf_ptr(out_h_path), strerror(errno)); - - Buf *export_macro = nullptr; - if (g->is_dynamic) { - export_macro = preprocessor_mangle(buf_sprintf("%s_EXPORT", buf_ptr(g->root_out_name))); - buf_upcase(export_macro); - } - - Buf fns_buf = BUF_INIT; - buf_resize(&fns_buf, 0); - gen_h_file_functions(g, gen_h, &fns_buf, export_macro); - - Buf vars_buf = BUF_INIT; - buf_resize(&vars_buf, 0); - gen_h_file_variables(g, gen_h, &vars_buf, export_macro); - - // Types will be populated by exported functions and variables so it has to run last. - Buf types_buf = BUF_INIT; - buf_resize(&types_buf, 0); - gen_h_file_types(g, gen_h, &types_buf); - - Buf *ifdef_dance_name = preprocessor_mangle(buf_sprintf("%s_H", buf_ptr(g->root_out_name))); - buf_upcase(ifdef_dance_name); - - fprintf(out_h, "#ifndef %s\n", buf_ptr(ifdef_dance_name)); - fprintf(out_h, "#define %s\n\n", buf_ptr(ifdef_dance_name)); - - if (g->c_want_stdbool) - fprintf(out_h, "#include \n"); - if (g->c_want_stdint) - fprintf(out_h, "#include \n"); - - fprintf(out_h, "\n"); - - if (g->is_dynamic) { - fprintf(out_h, "#if defined(_WIN32)\n"); - fprintf(out_h, "#define %s __declspec(dllimport)\n", buf_ptr(export_macro)); - fprintf(out_h, "#else\n"); - fprintf(out_h, "#define %s __attribute__((visibility (\"default\")))\n", - buf_ptr(export_macro)); - fprintf(out_h, "#endif\n"); - fprintf(out_h, "\n"); - } - - fprintf(out_h, "#ifdef __cplusplus\n"); - fprintf(out_h, "extern \"C\" {\n"); - fprintf(out_h, "#endif\n"); - fprintf(out_h, "\n"); - - fprintf(out_h, "%s", buf_ptr(&types_buf)); - fprintf(out_h, "%s\n", buf_ptr(&fns_buf)); - fprintf(out_h, "%s\n", buf_ptr(&vars_buf)); - - fprintf(out_h, "#ifdef __cplusplus\n"); - fprintf(out_h, "} // extern \"C\"\n"); - fprintf(out_h, "#endif\n\n"); - - fprintf(out_h, "#endif // %s\n", buf_ptr(ifdef_dance_name)); - - if (fclose(out_h)) - zig_panic("unable to close h file: %s", strerror(errno)); -} - -void codegen_print_timing_report(CodeGen *g, FILE *f) { - double start_time = g->timing_events.at(0).time; - double end_time = g->timing_events.last().time; - double total = end_time - start_time; - fprintf(f, "%20s%12s%12s%12s%12s\n", "Name", "Start", "End", "Duration", "Percent"); - for (size_t i = 0; i < g->timing_events.length - 1; i += 1) { - TimeEvent *te = &g->timing_events.at(i); - TimeEvent *next_te = &g->timing_events.at(i + 1); - fprintf(f, "%20s%12.4f%12.4f%12.4f%12.4f\n", te->name, - te->time - start_time, - next_te->time - start_time, - next_te->time - te->time, - (next_te->time - te->time) / total); - } - fprintf(f, "%20s%12.4f%12.4f%12.4f%12.4f\n", "Total", 0.0, total, total, 1.0); -} - -void codegen_add_time_event(CodeGen *g, const char *name) { - OsTimeStamp timestamp = os_timestamp_monotonic(); - double seconds = (double)timestamp.sec; - seconds += ((double)timestamp.nsec) / 1000000000.0; - g->timing_events.append({seconds, name}); -} - -static void add_cache_pkg(CodeGen *g, CacheHash *ch, ZigPackage *pkg) { - if (buf_len(&pkg->root_src_path) == 0) - return; - pkg->added_to_cache = true; - - Buf *rel_full_path = buf_alloc(); - os_path_join(&pkg->root_src_dir, &pkg->root_src_path, rel_full_path); - cache_file(ch, rel_full_path); - - auto it = pkg->package_table.entry_iterator(); - for (;;) { - auto *entry = it.next(); - if (!entry) - break; - - if (!pkg->added_to_cache) { - cache_buf(ch, entry->key); - add_cache_pkg(g, ch, entry->value); - } - } -} - -// Called before init() -// is_cache_hit takes into account gen_c_objects -static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) { - Error err; - - Buf *compiler_id; - if ((err = get_compiler_id(&compiler_id))) - return err; - - CacheHash *ch = &g->cache_hash; - cache_init(ch, manifest_dir); - - add_cache_pkg(g, ch, g->main_pkg); - if (g->linker_script != nullptr) { - cache_file(ch, buf_create_from_str(g->linker_script)); - } - cache_buf(ch, compiler_id); - cache_buf(ch, g->root_out_name); - cache_buf(ch, g->zig_lib_dir); - cache_buf(ch, g->zig_std_dir); - cache_list_of_link_lib(ch, g->link_libs_list.items, g->link_libs_list.length); - cache_list_of_buf(ch, g->darwin_frameworks.items, g->darwin_frameworks.length); - cache_list_of_buf(ch, g->rpath_list.items, g->rpath_list.length); - cache_list_of_buf(ch, g->forbidden_libs.items, g->forbidden_libs.length); - cache_int(ch, g->build_mode); - cache_int(ch, g->out_type); - cache_bool(ch, g->zig_target->is_native_os); - cache_bool(ch, g->zig_target->is_native_cpu); - cache_int(ch, g->zig_target->arch); - cache_int(ch, g->zig_target->vendor); - cache_int(ch, g->zig_target->os); - cache_int(ch, g->zig_target->abi); - if (g->zig_target->cache_hash != nullptr) { - cache_mem(ch, g->zig_target->cache_hash, g->zig_target->cache_hash_len); - } - if (g->zig_target->glibc_or_darwin_version != nullptr) { - cache_int(ch, g->zig_target->glibc_or_darwin_version->major); - cache_int(ch, g->zig_target->glibc_or_darwin_version->minor); - cache_int(ch, g->zig_target->glibc_or_darwin_version->patch); - } - if (g->zig_target->dynamic_linker != nullptr) { - cache_str(ch, g->zig_target->dynamic_linker); - } - cache_int(ch, detect_subsystem(g)); - cache_bool(ch, g->strip_debug_symbols); - cache_bool(ch, g->is_test_build); - if (g->is_test_build) { - cache_buf_opt(ch, g->test_filter); - cache_buf_opt(ch, g->test_name_prefix); - cache_bool(ch, g->test_is_evented); - } - cache_bool(ch, g->link_eh_frame_hdr); - cache_bool(ch, g->is_single_threaded); - cache_bool(ch, g->linker_rdynamic); - cache_bool(ch, g->each_lib_rpath); - cache_bool(ch, g->disable_gen_h); - cache_bool(ch, g->bundle_compiler_rt); - cache_bool(ch, want_valgrind_support(g)); - cache_bool(ch, g->have_pic); - cache_bool(ch, g->have_dynamic_link); - cache_bool(ch, g->have_stack_probing); - cache_bool(ch, g->have_sanitize_c); - cache_bool(ch, g->is_dummy_so); - cache_bool(ch, g->function_sections); - cache_bool(ch, g->enable_dump_analysis); - cache_bool(ch, g->enable_doc_generation); - cache_bool(ch, g->emit_bin); - cache_bool(ch, g->emit_llvm_ir); - cache_bool(ch, g->emit_asm); - cache_bool(ch, g->is_versioned); - cache_usize(ch, g->version_major); - cache_usize(ch, g->version_minor); - cache_usize(ch, g->version_patch); - cache_list_of_str(ch, g->llvm_argv, g->llvm_argv_len); - cache_list_of_str(ch, g->clang_argv, g->clang_argv_len); - cache_list_of_str(ch, g->lib_dirs.items, g->lib_dirs.length); - cache_list_of_str(ch, g->framework_dirs.items, g->framework_dirs.length); - if (g->libc) { - cache_slice(ch, Slice{g->libc->include_dir, g->libc->include_dir_len}); - cache_slice(ch, Slice{g->libc->sys_include_dir, g->libc->sys_include_dir_len}); - cache_slice(ch, Slice{g->libc->crt_dir, g->libc->crt_dir_len}); - cache_slice(ch, Slice{g->libc->msvc_lib_dir, g->libc->msvc_lib_dir_len}); - cache_slice(ch, Slice{g->libc->kernel32_lib_dir, g->libc->kernel32_lib_dir_len}); - } - cache_buf_opt(ch, g->version_script_path); - cache_buf_opt(ch, g->override_soname); - cache_buf_opt(ch, g->linker_optimization); - cache_int(ch, g->linker_gc_sections); - cache_int(ch, g->linker_allow_shlib_undefined); - cache_int(ch, g->linker_bind_global_refs_locally); - cache_bool(ch, g->linker_z_nodelete); - cache_bool(ch, g->linker_z_defs); - cache_usize(ch, g->stack_size_override); - - // gen_c_objects appends objects to g->link_objects which we want to include in the hash - gen_c_objects(g); - cache_list_of_file(ch, g->link_objects.items, g->link_objects.length); - - buf_resize(digest, 0); - if ((err = cache_hit(ch, digest))) { - if (err != ErrorInvalidFormat) - return err; - } - - if (ch->manifest_file_path != nullptr) { - g->caches_to_release.append(ch); - } - - return ErrorNone; -} - -static void resolve_out_paths(CodeGen *g) { - assert(g->output_dir != nullptr); - assert(g->root_out_name != nullptr); - - if (g->emit_bin) { - Buf *out_basename = buf_create_from_buf(g->root_out_name); - Buf *o_basename = buf_create_from_buf(g->root_out_name); - switch (g->out_type) { - case OutTypeUnknown: - zig_unreachable(); - case OutTypeObj: - if (need_llvm_module(g) && g->link_objects.length != 0 && !g->enable_cache && - buf_eql_buf(o_basename, out_basename)) - { - // make it not collide with main output object - buf_append_str(o_basename, ".root"); - } - buf_append_str(o_basename, target_o_file_ext(g->zig_target)); - buf_append_str(out_basename, target_o_file_ext(g->zig_target)); - break; - case OutTypeExe: - buf_append_str(o_basename, target_o_file_ext(g->zig_target)); - buf_append_str(out_basename, target_exe_file_ext(g->zig_target)); - break; - case OutTypeLib: - buf_append_str(o_basename, target_o_file_ext(g->zig_target)); - buf_resize(out_basename, 0); - buf_append_str(out_basename, target_lib_file_prefix(g->zig_target)); - buf_append_buf(out_basename, g->root_out_name); - buf_append_str(out_basename, target_lib_file_ext(g->zig_target, !g->is_dynamic, g->is_versioned, - g->version_major, g->version_minor, g->version_patch)); - break; - } - os_path_join(g->output_dir, o_basename, &g->o_file_output_path); - os_path_join(g->output_dir, out_basename, &g->bin_file_output_path); - } - if (g->emit_asm) { - Buf *asm_basename = buf_create_from_buf(g->root_out_name); - const char *asm_ext = target_asm_file_ext(g->zig_target); - buf_append_str(asm_basename, asm_ext); - os_path_join(g->output_dir, asm_basename, &g->asm_file_output_path); - } - if (g->emit_llvm_ir) { - Buf *llvm_ir_basename = buf_create_from_buf(g->root_out_name); - const char *llvm_ir_ext = target_llvm_ir_file_ext(g->zig_target); - buf_append_str(llvm_ir_basename, llvm_ir_ext); - os_path_join(g->output_dir, llvm_ir_basename, &g->llvm_ir_file_output_path); - } -} - -static void output_type_information(CodeGen *g) { - if (g->enable_dump_analysis) { - const char *analysis_json_filename = buf_ptr(buf_sprintf("%s" OS_SEP "%s-analysis.json", - buf_ptr(g->output_dir), buf_ptr(g->root_out_name))); - FILE *f = fopen(analysis_json_filename, "wb"); - if (f == nullptr) { - fprintf(stderr, "Unable to open '%s': %s\n", analysis_json_filename, strerror(errno)); - exit(1); - } - zig_print_analysis_dump(g, f, " ", "\n"); - if (fclose(f) != 0) { - fprintf(stderr, "Unable to write '%s': %s\n", analysis_json_filename, strerror(errno)); - exit(1); - } - } - if (g->enable_doc_generation) { - Error err; - Buf *doc_dir_path = buf_sprintf("%s" OS_SEP "docs", buf_ptr(g->output_dir)); - if ((err = os_make_path(doc_dir_path))) { - fprintf(stderr, "Unable to create directory %s: %s\n", buf_ptr(doc_dir_path), err_str(err)); - exit(1); - } - Buf *index_html_src_path = buf_sprintf("%s" OS_SEP "special" OS_SEP "docs" OS_SEP "index.html", - buf_ptr(g->zig_std_dir)); - Buf *index_html_dest_path = buf_sprintf("%s" OS_SEP "index.html", buf_ptr(doc_dir_path)); - Buf *main_js_src_path = buf_sprintf("%s" OS_SEP "special" OS_SEP "docs" OS_SEP "main.js", - buf_ptr(g->zig_std_dir)); - Buf *main_js_dest_path = buf_sprintf("%s" OS_SEP "main.js", buf_ptr(doc_dir_path)); - - if ((err = os_copy_file(index_html_src_path, index_html_dest_path))) { - fprintf(stderr, "Unable to copy %s to %s: %s\n", buf_ptr(index_html_src_path), - buf_ptr(index_html_dest_path), err_str(err)); - exit(1); - } - if ((err = os_copy_file(main_js_src_path, main_js_dest_path))) { - fprintf(stderr, "Unable to copy %s to %s: %s\n", buf_ptr(main_js_src_path), - buf_ptr(main_js_dest_path), err_str(err)); - exit(1); - } - const char *data_js_filename = buf_ptr(buf_sprintf("%s" OS_SEP "data.js", buf_ptr(doc_dir_path))); - FILE *f = fopen(data_js_filename, "wb"); - if (f == nullptr) { - fprintf(stderr, "Unable to open '%s': %s\n", data_js_filename, strerror(errno)); - exit(1); - } - fprintf(f, "zigAnalysis="); - zig_print_analysis_dump(g, f, "", ""); - fprintf(f, ";"); - if (fclose(f) != 0) { - fprintf(stderr, "Unable to write '%s': %s\n", data_js_filename, strerror(errno)); - exit(1); - } - } -} - -static void init_output_dir(CodeGen *g, Buf *digest) { - if (main_output_dir_is_just_one_c_object_post(g)) { - g->output_dir = buf_alloc(); - os_path_dirname(g->link_objects.at(0), g->output_dir); - } else { - g->output_dir = buf_sprintf("%s" OS_SEP CACHE_OUT_SUBDIR OS_SEP "%s", - buf_ptr(g->cache_dir), buf_ptr(digest)); - } -} - -void codegen_build_and_link(CodeGen *g) { - Error err; - assert(g->out_type != OutTypeUnknown); - - if (!g->enable_cache) { - if (g->output_dir == nullptr) { - g->output_dir = buf_create_from_str("."); - } else if ((err = os_make_path(g->output_dir))) { - fprintf(stderr, "Unable to create output directory: %s\n", err_str(err)); - exit(1); - } - } - - g->have_dynamic_link = detect_dynamic_link(g); - g->have_pic = detect_pic(g); - g->is_single_threaded = detect_single_threaded(g); - g->have_err_ret_tracing = detect_err_ret_tracing(g); - g->have_sanitize_c = detect_sanitize_c(g); - detect_libc(g); - - Buf digest = BUF_INIT; - if (g->enable_cache) { - Buf *manifest_dir = buf_alloc(); - os_path_join(g->cache_dir, buf_create_from_str(CACHE_HASH_SUBDIR), manifest_dir); - - if ((err = check_cache(g, manifest_dir, &digest))) { - if (err == ErrorCacheUnavailable) { - // message already printed - } else if (err == ErrorNotDir) { - fprintf(stderr, "Unable to check cache: %s is not a directory\n", - buf_ptr(manifest_dir)); - } else { - fprintf(stderr, "Unable to check cache: %s: %s\n", buf_ptr(manifest_dir), err_str(err)); - } - exit(1); - } - } else { - // There is a call to this in check_cache - gen_c_objects(g); - } - - if (g->enable_cache && buf_len(&digest) != 0) { - init_output_dir(g, &digest); - resolve_out_paths(g); - } else { - if (need_llvm_module(g)) { - init(g); - - codegen_add_time_event(g, "Semantic Analysis"); - const char *progress_name = "Semantic Analysis"; - codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node, - progress_name, strlen(progress_name), 0)); - - gen_root_source(g); - - } - if (g->enable_cache) { - if (buf_len(&digest) == 0) { - if ((err = cache_final(&g->cache_hash, &digest))) { - fprintf(stderr, "Unable to finalize cache hash: %s\n", err_str(err)); - exit(1); - } - } - init_output_dir(g, &digest); - - if ((err = os_make_path(g->output_dir))) { - fprintf(stderr, "Unable to create output directory: %s\n", err_str(err)); - exit(1); - } - } - resolve_out_paths(g); - - if (g->enable_dump_analysis || g->enable_doc_generation) { - output_type_information(g); - } - - if (need_llvm_module(g)) { - codegen_add_time_event(g, "Code Generation"); - { - const char *progress_name = "Code Generation"; - codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node, - progress_name, strlen(progress_name), 0)); - } - - do_code_gen(g); - codegen_add_time_event(g, "LLVM Emit Output"); - { - const char *progress_name = "LLVM Emit Output"; - codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node, - progress_name, strlen(progress_name), 0)); - } - zig_llvm_emit_output(g); - - if (!g->disable_gen_h && (g->out_type == OutTypeObj || g->out_type == OutTypeLib)) { - codegen_add_time_event(g, "Generate .h"); - { - const char *progress_name = "Generate .h"; - codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node, - progress_name, strlen(progress_name), 0)); - } - gen_h_file(g); - } - } - - // If we're outputting assembly or llvm IR we skip linking. - // If we're making a library or executable we must link. - // If there is more than one object, we have to link them (with -r). - // Finally, if we didn't make an object from zig source, and we don't have caching enabled, - // then we have an object from C source that we must copy to the output dir which we do with a -r link. - if (g->emit_bin && - (g->out_type != OutTypeObj || g->link_objects.length > 1 || - (!need_llvm_module(g) && !g->enable_cache))) - { - codegen_link(g); - } - } - - codegen_release_caches(g); - codegen_add_time_event(g, "Done"); - codegen_switch_sub_prog_node(g, nullptr); -} - -void codegen_release_caches(CodeGen *g) { - while (g->caches_to_release.length != 0) { - cache_release(g->caches_to_release.pop()); - } -} - -ZigPackage *codegen_create_package(CodeGen *g, const char *root_src_dir, const char *root_src_path, - const char *pkg_path) -{ - init(g); - ZigPackage *pkg = new_package(root_src_dir, root_src_path, pkg_path); - if (g->std_package != nullptr) { - assert(g->compile_var_package != nullptr); - pkg->package_table.put(buf_create_from_str("std"), g->std_package); - - pkg->package_table.put(buf_create_from_str("root"), g->root_pkg); - - pkg->package_table.put(buf_create_from_str("builtin"), g->compile_var_package); - } - return pkg; -} - -CodeGen *create_child_codegen(CodeGen *parent_gen, Buf *root_src_path, OutType out_type, - Stage2LibCInstallation *libc, const char *name, Stage2ProgressNode *parent_progress_node) -{ - Stage2ProgressNode *child_progress_node = stage2_progress_start( - parent_progress_node ? parent_progress_node : parent_gen->sub_progress_node, - name, strlen(name), 0); - - CodeGen *child_gen = codegen_create(nullptr, root_src_path, parent_gen->zig_target, out_type, - parent_gen->build_mode, parent_gen->zig_lib_dir, libc, get_global_cache_dir(), false, child_progress_node); - child_gen->root_out_name = buf_create_from_str(name); - child_gen->disable_gen_h = true; - child_gen->want_stack_check = WantStackCheckDisabled; - child_gen->want_sanitize_c = WantCSanitizeDisabled; - child_gen->verbose_tokenize = parent_gen->verbose_tokenize; - child_gen->verbose_ast = parent_gen->verbose_ast; - child_gen->verbose_link = parent_gen->verbose_link; - child_gen->verbose_ir = parent_gen->verbose_ir; - child_gen->verbose_llvm_ir = parent_gen->verbose_llvm_ir; - child_gen->verbose_cimport = parent_gen->verbose_cimport; - child_gen->verbose_cc = parent_gen->verbose_cc; - child_gen->verbose_llvm_cpu_features = parent_gen->verbose_llvm_cpu_features; - child_gen->llvm_argv = parent_gen->llvm_argv; - - codegen_set_strip(child_gen, parent_gen->strip_debug_symbols); - child_gen->want_pic = parent_gen->have_pic ? WantPICEnabled : WantPICDisabled; - child_gen->valgrind_support = ValgrindSupportDisabled; - - codegen_set_errmsg_color(child_gen, parent_gen->err_color); - - child_gen->enable_cache = true; - - return child_gen; -} - -CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget *target, - OutType out_type, BuildMode build_mode, Buf *override_lib_dir, - Stage2LibCInstallation *libc, Buf *cache_dir, bool is_test_build, Stage2ProgressNode *progress_node) -{ - CodeGen *g = heap::c_allocator.create(); - g->emit_bin = true; - g->pass1_arena = heap::ArenaAllocator::construct(&heap::c_allocator, &heap::c_allocator, "pass1"); - g->main_progress_node = progress_node; - - codegen_add_time_event(g, "Initialize"); - { - const char *progress_name = "Initialize"; - codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node, - progress_name, strlen(progress_name), 0)); - } - - g->subsystem = TargetSubsystemAuto; - g->libc = libc; - g->zig_target = target; - g->cache_dir = cache_dir; - - if (override_lib_dir == nullptr) { - g->zig_lib_dir = get_zig_lib_dir(); - } else { - g->zig_lib_dir = override_lib_dir; - } - - g->zig_std_dir = buf_alloc(); - os_path_join(g->zig_lib_dir, buf_create_from_str("std"), g->zig_std_dir); - - g->zig_c_headers_dir = buf_alloc(); - os_path_join(g->zig_lib_dir, buf_create_from_str("include"), g->zig_c_headers_dir); - - g->build_mode = build_mode; - g->out_type = out_type; - g->import_table.init(32); - g->builtin_fn_table.init(32); - g->primitive_type_table.init(32); - g->type_table.init(32); - g->fn_type_table.init(32); - g->error_table.init(16); - g->generic_table.init(16); - g->llvm_fn_table.init(16); - g->memoized_fn_eval_table.init(16); - g->exported_symbol_names.init(8); - g->external_symbol_names.init(8); - g->string_literals_table.init(16); - g->type_info_cache.init(32); - g->one_possible_values.init(32); - g->is_test_build = is_test_build; - g->is_single_threaded = false; - g->code_model = CodeModelDefault; - buf_resize(&g->global_asm, 0); - - for (size_t i = 0; i < array_length(symbols_that_llvm_depends_on); i += 1) { - g->external_symbol_names.put(buf_create_from_str(symbols_that_llvm_depends_on[i]), nullptr); - } - - if (root_src_path) { - Buf *root_pkg_path; - Buf *rel_root_src_path; - if (main_pkg_path == nullptr) { - Buf *src_basename = buf_alloc(); - Buf *src_dir = buf_alloc(); - os_path_split(root_src_path, src_dir, src_basename); - - if (buf_len(src_basename) == 0) { - fprintf(stderr, "Invalid root source path: %s\n", buf_ptr(root_src_path)); - exit(1); - } - root_pkg_path = src_dir; - rel_root_src_path = src_basename; - } else { - Buf resolved_root_src_path = os_path_resolve(&root_src_path, 1); - Buf resolved_main_pkg_path = os_path_resolve(&main_pkg_path, 1); - - if (!buf_starts_with_buf(&resolved_root_src_path, &resolved_main_pkg_path)) { - fprintf(stderr, "Root source path '%s' outside main package path '%s'", - buf_ptr(root_src_path), buf_ptr(main_pkg_path)); - exit(1); - } - root_pkg_path = main_pkg_path; - rel_root_src_path = buf_create_from_mem( - buf_ptr(&resolved_root_src_path) + buf_len(&resolved_main_pkg_path) + 1, - buf_len(&resolved_root_src_path) - buf_len(&resolved_main_pkg_path) - 1); - } - - g->main_pkg = new_package(buf_ptr(root_pkg_path), buf_ptr(rel_root_src_path), ""); - g->std_package = new_package(buf_ptr(g->zig_std_dir), "std.zig", "std"); - g->main_pkg->package_table.put(buf_create_from_str("std"), g->std_package); - } else { - g->main_pkg = new_package(".", "", ""); - } - - g->zig_std_special_dir = buf_alloc(); - os_path_join(g->zig_std_dir, buf_sprintf("special"), g->zig_std_special_dir); - - assert(target != nullptr); - if (!target->is_native_os) { - g->each_lib_rpath = false; - } else { - g->each_lib_rpath = true; - } - - if (target_os_requires_libc(g->zig_target->os)) { - g->libc_link_lib = create_link_lib(buf_create_from_str("c")); - g->link_libs_list.append(g->libc_link_lib); - } - - target_triple_llvm(&g->llvm_triple_str, g->zig_target); - g->pointer_size_bytes = target_arch_pointer_bit_width(g->zig_target->arch) / 8; - - if (!target_has_debug_info(g->zig_target)) { - g->strip_debug_symbols = true; - } - - return g; -} - -bool codegen_fn_has_err_ret_tracing_arg(CodeGen *g, ZigType *return_type) { - return g->have_err_ret_tracing && - (return_type->id == ZigTypeIdErrorUnion || - return_type->id == ZigTypeIdErrorSet); -} - -bool codegen_fn_has_err_ret_tracing_stack(CodeGen *g, ZigFn *fn, bool is_async) { - if (is_async) { - return g->have_err_ret_tracing && (fn->calls_or_awaits_errorable_fn || - codegen_fn_has_err_ret_tracing_arg(g, fn->type_entry->data.fn.fn_type_id.return_type)); - } else { - return g->have_err_ret_tracing && fn->calls_or_awaits_errorable_fn && - !codegen_fn_has_err_ret_tracing_arg(g, fn->type_entry->data.fn.fn_type_id.return_type); - } -} - -void codegen_switch_sub_prog_node(CodeGen *g, Stage2ProgressNode *node) { - if (g->sub_progress_node != nullptr) { - stage2_progress_end(g->sub_progress_node); - } - g->sub_progress_node = node; -} - -ZigValue *CodeGen::Intern::for_undefined() { -#ifdef ZIG_ENABLE_MEM_PROFILE - mem::intern_counters.x_undefined += 1; -#endif - return &this->x_undefined; -} - -ZigValue *CodeGen::Intern::for_void() { -#ifdef ZIG_ENABLE_MEM_PROFILE - mem::intern_counters.x_void += 1; -#endif - return &this->x_void; -} - -ZigValue *CodeGen::Intern::for_null() { -#ifdef ZIG_ENABLE_MEM_PROFILE - mem::intern_counters.x_null += 1; -#endif - return &this->x_null; -} - -ZigValue *CodeGen::Intern::for_unreachable() { -#ifdef ZIG_ENABLE_MEM_PROFILE - mem::intern_counters.x_unreachable += 1; -#endif - return &this->x_unreachable; -} - -ZigValue *CodeGen::Intern::for_zero_byte() { -#ifdef ZIG_ENABLE_MEM_PROFILE - mem::intern_counters.zero_byte += 1; -#endif - return &this->zero_byte; -} diff --git a/src/codegen.hpp b/src/codegen.hpp deleted file mode 100644 index 3139071d5267fcfa1d27a6425b0e184e633829f2..0000000000000000000000000000000000000000 --- a/src/codegen.hpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (c) 2015 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#ifndef ZIG_CODEGEN_HPP -#define ZIG_CODEGEN_HPP - -#include "parser.hpp" -#include "errmsg.hpp" -#include "target.hpp" -#include "stage2.h" - -#include - -CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget *target, - OutType out_type, BuildMode build_mode, Buf *zig_lib_dir, - Stage2LibCInstallation *libc, Buf *cache_dir, bool is_test_build, Stage2ProgressNode *progress_node); - -CodeGen *create_child_codegen(CodeGen *parent_gen, Buf *root_src_path, OutType out_type, - Stage2LibCInstallation *libc, const char *name, Stage2ProgressNode *progress_node); - -void codegen_set_clang_argv(CodeGen *codegen, const char **args, size_t len); -void codegen_set_llvm_argv(CodeGen *codegen, const char **args, size_t len); -void codegen_set_each_lib_rpath(CodeGen *codegen, bool each_lib_rpath); - -void codegen_set_strip(CodeGen *codegen, bool strip); -void codegen_set_errmsg_color(CodeGen *codegen, ErrColor err_color); -void codegen_set_out_name(CodeGen *codegen, Buf *out_name); -void codegen_add_lib_dir(CodeGen *codegen, const char *dir); -void codegen_add_forbidden_lib(CodeGen *codegen, Buf *lib); -LinkLib *codegen_add_link_lib(CodeGen *codegen, Buf *lib); -void codegen_add_framework(CodeGen *codegen, const char *name); -void codegen_add_rpath(CodeGen *codegen, const char *name); -void codegen_set_rdynamic(CodeGen *g, bool rdynamic); -void codegen_set_linker_script(CodeGen *g, const char *linker_script); -void codegen_set_test_filter(CodeGen *g, Buf *filter); -void codegen_set_test_name_prefix(CodeGen *g, Buf *prefix); -void codegen_set_lib_version(CodeGen *g, bool is_versioned, size_t major, size_t minor, size_t patch); -void codegen_add_time_event(CodeGen *g, const char *name); -void codegen_print_timing_report(CodeGen *g, FILE *f); -void codegen_link(CodeGen *g); -void zig_link_add_compiler_rt(CodeGen *g, Stage2ProgressNode *progress_node); -void codegen_build_and_link(CodeGen *g); - -ZigPackage *codegen_create_package(CodeGen *g, const char *root_src_dir, const char *root_src_path, - const char *pkg_path); -void codegen_add_assembly(CodeGen *g, Buf *path); -void codegen_add_object(CodeGen *g, Buf *object_path); - -void codegen_translate_c(CodeGen *g, Buf *full_path); - -Buf *codegen_generate_builtin_source(CodeGen *g); - -TargetSubsystem detect_subsystem(CodeGen *g); - -void codegen_release_caches(CodeGen *codegen); -bool codegen_fn_has_err_ret_tracing_arg(CodeGen *g, ZigType *return_type); -bool codegen_fn_has_err_ret_tracing_stack(CodeGen *g, ZigFn *fn, bool is_async); - -ATTRIBUTE_NORETURN -void codegen_report_errors_and_exit(CodeGen *g); - -void codegen_switch_sub_prog_node(CodeGen *g, Stage2ProgressNode *node); - -#endif diff --git a/src/codegen.zig b/src/codegen.zig new file mode 100644 index 0000000000000000000000000000000000000000..a1d3cc2fc4b64ed84055d202bba260500988461e --- /dev/null +++ b/src/codegen.zig @@ -0,0 +1,2796 @@ +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 Compilation = @import("Compilation.zig"); +const ErrorMsg = Compilation.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: *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/codegen/arm.zig b/src/codegen/arm.zig new file mode 100644 index 0000000000000000000000000000000000000000..05178ea7d37afb551ea9baed8f1ba09cdad739fa --- /dev/null +++ b/src/codegen/arm.zig @@ -0,0 +1,607 @@ +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/codegen/c.zig b/src/codegen/c.zig new file mode 100644 index 0000000000000000000000000000000000000000..34ddcfbb3b33bf9925cd90234324821fc9d1fdc2 --- /dev/null +++ b/src/codegen/c.zig @@ -0,0 +1,299 @@ +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/codegen/llvm.zig b/src/codegen/llvm.zig new file mode 100644 index 0000000000000000000000000000000000000000..01fa0baf0293f829fefb6774cf9fc4e0dafefe5c --- /dev/null +++ b/src/codegen/llvm.zig @@ -0,0 +1,125 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; + +pub fn targetTriple(allocator: *Allocator, target: std.Target) ![]u8 { + const llvm_arch = switch (target.cpu.arch) { + .arm => "arm", + .armeb => "armeb", + .aarch64 => "aarch64", + .aarch64_be => "aarch64_be", + .aarch64_32 => "aarch64_32", + .arc => "arc", + .avr => "avr", + .bpfel => "bpfel", + .bpfeb => "bpfeb", + .hexagon => "hexagon", + .mips => "mips", + .mipsel => "mipsel", + .mips64 => "mips64", + .mips64el => "mips64el", + .msp430 => "msp430", + .powerpc => "powerpc", + .powerpc64 => "powerpc64", + .powerpc64le => "powerpc64le", + .r600 => "r600", + .amdgcn => "amdgcn", + .riscv32 => "riscv32", + .riscv64 => "riscv64", + .sparc => "sparc", + .sparcv9 => "sparcv9", + .sparcel => "sparcel", + .s390x => "s390x", + .tce => "tce", + .tcele => "tcele", + .thumb => "thumb", + .thumbeb => "thumbeb", + .i386 => "i386", + .x86_64 => "x86_64", + .xcore => "xcore", + .nvptx => "nvptx", + .nvptx64 => "nvptx64", + .le32 => "le32", + .le64 => "le64", + .amdil => "amdil", + .amdil64 => "amdil64", + .hsail => "hsail", + .hsail64 => "hsail64", + .spir => "spir", + .spir64 => "spir64", + .kalimba => "kalimba", + .shave => "shave", + .lanai => "lanai", + .wasm32 => "wasm32", + .wasm64 => "wasm64", + .renderscript32 => "renderscript32", + .renderscript64 => "renderscript64", + .ve => "ve", + .spu_2 => return error.LLVMBackendDoesNotSupportSPUMarkII, + }; + // TODO Add a sub-arch for some architectures depending on CPU features. + + const llvm_os = switch (target.os.tag) { + .freestanding => "unknown", + .ananas => "ananas", + .cloudabi => "cloudabi", + .dragonfly => "dragonfly", + .freebsd => "freebsd", + .fuchsia => "fuchsia", + .ios => "ios", + .kfreebsd => "kfreebsd", + .linux => "linux", + .lv2 => "lv2", + .macosx => "macosx", + .netbsd => "netbsd", + .openbsd => "openbsd", + .solaris => "solaris", + .windows => "windows", + .haiku => "haiku", + .minix => "minix", + .rtems => "rtems", + .nacl => "nacl", + .cnk => "cnk", + .aix => "aix", + .cuda => "cuda", + .nvcl => "nvcl", + .amdhsa => "amdhsa", + .ps4 => "ps4", + .elfiamcu => "elfiamcu", + .tvos => "tvos", + .watchos => "watchos", + .mesa3d => "mesa3d", + .contiki => "contiki", + .amdpal => "amdpal", + .hermit => "hermit", + .hurd => "hurd", + .wasi => "wasi", + .emscripten => "emscripten", + .uefi => "windows", + .other => "unknown", + }; + + const llvm_abi = switch (target.abi) { + .none => "unknown", + .gnu => "gnu", + .gnuabin32 => "gnuabin32", + .gnuabi64 => "gnuabi64", + .gnueabi => "gnueabi", + .gnueabihf => "gnueabihf", + .gnux32 => "gnux32", + .code16 => "code16", + .eabi => "eabi", + .eabihf => "eabihf", + .android => "android", + .musl => "musl", + .musleabi => "musleabi", + .musleabihf => "musleabihf", + .msvc => "msvc", + .itanium => "itanium", + .cygnus => "cygnus", + .coreclr => "coreclr", + .simulator => "simulator", + .macabi => "macabi", + }; + + return std.fmt.allocPrint(allocator, "{}-unknown-{}-{}", .{ llvm_arch, llvm_os, llvm_abi }); +} diff --git a/src/codegen/riscv64.zig b/src/codegen/riscv64.zig new file mode 100644 index 0000000000000000000000000000000000000000..96b9c58f9c3b041263e44cd215ae5a71df63aa37 --- /dev/null +++ b/src/codegen/riscv64.zig @@ -0,0 +1,433 @@ +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/codegen/spu-mk2.zig b/src/codegen/spu-mk2.zig new file mode 100644 index 0000000000000000000000000000000000000000..542862cacaa444033616d002c8fcfb2133fea1a0 --- /dev/null +++ b/src/codegen/spu-mk2.zig @@ -0,0 +1,170 @@ +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/codegen/spu-mk2/interpreter.zig b/src/codegen/spu-mk2/interpreter.zig new file mode 100644 index 0000000000000000000000000000000000000000..1ec99546c6cbe1ee30d1473e455824d2f6dc9421 --- /dev/null +++ b/src/codegen/spu-mk2/interpreter.zig @@ -0,0 +1,166 @@ +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/codegen/wasm.zig b/src/codegen/wasm.zig new file mode 100644 index 0000000000000000000000000000000000000000..4ea883840941ad47109538eba6395172df83d238 --- /dev/null +++ b/src/codegen/wasm.zig @@ -0,0 +1,142 @@ +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 Module = @import("../Module.zig"); +const Decl = Module.Decl; +const Inst = @import("../ir.zig").Inst; +const Type = @import("../type.zig").Type; +const Value = @import("../value.zig").Value; + +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/codegen/x86.zig b/src/codegen/x86.zig new file mode 100644 index 0000000000000000000000000000000000000000..fdad4e56db6139258e72b615a4d11dd8246b0d19 --- /dev/null +++ b/src/codegen/x86.zig @@ -0,0 +1,123 @@ +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/codegen/x86_64.zig b/src/codegen/x86_64.zig new file mode 100644 index 0000000000000000000000000000000000000000..dea39f82cdbdd2c4d6d623c6d3b697c00d9bfda1 --- /dev/null +++ b/src/codegen/x86_64.zig @@ -0,0 +1,220 @@ +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/compiler.cpp b/src/compiler.cpp deleted file mode 100644 index 6c477a1506fa2939cc94bc6edecb41bec5b3a75b..0000000000000000000000000000000000000000 --- a/src/compiler.cpp +++ /dev/null @@ -1,196 +0,0 @@ -#include "cache_hash.hpp" -#include "os.hpp" -#include "compiler.hpp" - -#include - -Error get_compiler_id(Buf **result) { - static Buf saved_compiler_id = BUF_INIT; - - if (saved_compiler_id.list.length != 0) { - *result = &saved_compiler_id; - return ErrorNone; - } - - Error err; - Buf *manifest_dir = buf_alloc(); - os_path_join(get_global_cache_dir(), buf_create_from_str("exe"), manifest_dir); - - CacheHash cache_hash; - CacheHash *ch = &cache_hash; - cache_init(ch, manifest_dir); - Buf self_exe_path = BUF_INIT; - if ((err = os_self_exe_path(&self_exe_path))) - return err; - - cache_file(ch, &self_exe_path); - - buf_resize(&saved_compiler_id, 0); - if ((err = cache_hit(ch, &saved_compiler_id))) { - if (err != ErrorInvalidFormat) - return err; - } - if (buf_len(&saved_compiler_id) != 0) { - cache_release(ch); - *result = &saved_compiler_id; - return ErrorNone; - } - ZigList lib_paths = {}; - if ((err = os_self_exe_shared_libs(lib_paths))) - return err; - #if defined(ZIG_OS_DARWIN) - // only add the self exe path on mac os - Buf *lib_path = lib_paths.at(0); - if ((err = cache_add_file(ch, lib_path))) - return err; - #else - for (size_t i = 0; i < lib_paths.length; i += 1) { - Buf *lib_path = lib_paths.at(i); - if ((err = cache_add_file(ch, lib_path))) - return err; - } - #endif - - if ((err = cache_final(ch, &saved_compiler_id))) - return err; - - cache_release(ch); - - *result = &saved_compiler_id; - return ErrorNone; -} - -static bool test_zig_install_prefix(Buf *test_path, Buf *out_zig_lib_dir) { - { - Buf *test_zig_dir = buf_sprintf("%s" OS_SEP "lib" OS_SEP "zig", buf_ptr(test_path)); - Buf *test_index_file = buf_sprintf("%s" OS_SEP "std" OS_SEP "std.zig", buf_ptr(test_zig_dir)); - int err; - bool exists; - if ((err = os_file_exists(test_index_file, &exists))) { - exists = false; - } - if (exists) { - buf_init_from_buf(out_zig_lib_dir, test_zig_dir); - return true; - } - } - - // Also try without "zig" - { - Buf *test_zig_dir = buf_sprintf("%s" OS_SEP "lib", buf_ptr(test_path)); - Buf *test_index_file = buf_sprintf("%s" OS_SEP "std" OS_SEP "std.zig", buf_ptr(test_zig_dir)); - int err; - bool exists; - if ((err = os_file_exists(test_index_file, &exists))) { - exists = false; - } - if (exists) { - buf_init_from_buf(out_zig_lib_dir, test_zig_dir); - return true; - } - } - - return false; -} - -static int find_zig_lib_dir(Buf *out_path) { - int err; - - Buf self_exe_path = BUF_INIT; - buf_resize(&self_exe_path, 0); - if (!(err = os_self_exe_path(&self_exe_path))) { - Buf *cur_path = &self_exe_path; - - for (;;) { - Buf *test_dir = buf_alloc(); - os_path_dirname(cur_path, test_dir); - - if (buf_eql_buf(test_dir, cur_path)) { - break; - } - - if (test_zig_install_prefix(test_dir, out_path)) { - return 0; - } - - cur_path = test_dir; - } - } - - return ErrorFileNotFound; -} - -Buf *get_zig_lib_dir(void) { - static Buf saved_lib_dir = BUF_INIT; - if (saved_lib_dir.list.length != 0) - return &saved_lib_dir; - buf_resize(&saved_lib_dir, 0); - - int err; - if ((err = find_zig_lib_dir(&saved_lib_dir))) { - fprintf(stderr, "Unable to find zig lib directory\n"); - exit(EXIT_FAILURE); - } - return &saved_lib_dir; -} - -Buf *get_zig_std_dir(Buf *zig_lib_dir) { - static Buf saved_std_dir = BUF_INIT; - if (saved_std_dir.list.length != 0) - return &saved_std_dir; - buf_resize(&saved_std_dir, 0); - - os_path_join(zig_lib_dir, buf_create_from_str("std"), &saved_std_dir); - - return &saved_std_dir; -} - -Buf *get_zig_special_dir(Buf *zig_lib_dir) { - static Buf saved_special_dir = BUF_INIT; - if (saved_special_dir.list.length != 0) - return &saved_special_dir; - buf_resize(&saved_special_dir, 0); - - os_path_join(get_zig_std_dir(zig_lib_dir), buf_sprintf("special"), &saved_special_dir); - - return &saved_special_dir; -} - -Buf *get_global_cache_dir(void) { - static Buf saved_global_cache_dir = BUF_INIT; - if (saved_global_cache_dir.list.length != 0) - return &saved_global_cache_dir; - buf_resize(&saved_global_cache_dir, 0); - - Buf app_data_dir = BUF_INIT; - Error err; - if ((err = os_get_app_data_dir(&app_data_dir, "zig"))) { - fprintf(stderr, "Unable to get application data dir: %s\n", err_str(err)); - exit(1); - } - os_path_join(&app_data_dir, buf_create_from_str("stage1"), &saved_global_cache_dir); - buf_deinit(&app_data_dir); - return &saved_global_cache_dir; -} - -FileExt classify_file_ext(const char *filename_ptr, size_t filename_len) { - if (mem_ends_with_str(filename_ptr, filename_len, ".c")) { - return FileExtC; - } else if (mem_ends_with_str(filename_ptr, filename_len, ".C") || - mem_ends_with_str(filename_ptr, filename_len, ".cc") || - mem_ends_with_str(filename_ptr, filename_len, ".cpp") || - mem_ends_with_str(filename_ptr, filename_len, ".cxx")) - { - return FileExtCpp; - } else if (mem_ends_with_str(filename_ptr, filename_len, ".ll")) { - return FileExtLLVMIr; - } else if (mem_ends_with_str(filename_ptr, filename_len, ".bc")) { - return FileExtLLVMBitCode; - } else if (mem_ends_with_str(filename_ptr, filename_len, ".s") || - mem_ends_with_str(filename_ptr, filename_len, ".S")) - { - return FileExtAsm; - } - // TODO look for .so, .so.X, .so.X.Y, .so.X.Y.Z - return FileExtUnknown; -} diff --git a/src/compiler.hpp b/src/compiler.hpp deleted file mode 100644 index ae2e6e9c5eb92d117fe5825d95007a4007037087..0000000000000000000000000000000000000000 --- a/src/compiler.hpp +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 2018 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#ifndef ZIG_COMPILER_HPP -#define ZIG_COMPILER_HPP - -#include "all_types.hpp" - -Error get_compiler_id(Buf **result); - -Buf *get_zig_lib_dir(void); -Buf *get_zig_special_dir(Buf *zig_lib_dir); -Buf *get_zig_std_dir(Buf *zig_lib_dir); - -Buf *get_global_cache_dir(void); - - -FileExt classify_file_ext(const char *filename_ptr, size_t filename_len); - -#endif diff --git a/src/config.h.in b/src/config.h.in deleted file mode 100644 index 2ec6c25b381e03b24de92577a7bfcb20069da363..0000000000000000000000000000000000000000 --- a/src/config.h.in +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2016 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#ifndef ZIG_CONFIG_H -#define ZIG_CONFIG_H - -#define ZIG_VERSION_MAJOR @ZIG_VERSION_MAJOR@ -#define ZIG_VERSION_MINOR @ZIG_VERSION_MINOR@ -#define ZIG_VERSION_PATCH @ZIG_VERSION_PATCH@ -#define ZIG_VERSION_STRING "@ZIG_VERSION@" - -// Used for communicating build information to self hosted build. -#define ZIG_CMAKE_BINARY_DIR "@CMAKE_BINARY_DIR@" -#define ZIG_CXX_COMPILER "@CMAKE_CXX_COMPILER@" -#define ZIG_LLD_INCLUDE_PATH "@LLD_INCLUDE_DIRS@" -#define ZIG_LLD_LIBRARIES "@LLD_LIBRARIES@" -#define ZIG_CLANG_LIBRARIES "@CLANG_LIBRARIES@" -#define ZIG_LLVM_CONFIG_EXE "@LLVM_CONFIG_EXE@" -#define ZIG_DIA_GUIDS_LIB "@ZIG_DIA_GUIDS_LIB_ESCAPED@" - -#cmakedefine ZIG_ENABLE_MEM_PROFILE - -#endif diff --git a/src/config.zig.in b/src/config.zig.in index ccb618df2dc89bd280ff145b36882a1cbd5c1f86..9e574bc1e83eebd11424f9651726f3e893bfd2b5 100644 --- a/src/config.zig.in +++ b/src/config.zig.in @@ -1,3 +1,6 @@ +pub const have_llvm = true; pub const version: []const u8 = "@ZIG_VERSION@"; pub const log_scopes: []const []const u8 = &[_][]const u8{}; +pub const zir_dumps: []const []const u8 = &[_][]const u8{}; pub const enable_tracy = false; +pub const is_stage1 = true; diff --git a/src/dump_analysis.cpp b/src/dump_analysis.cpp deleted file mode 100644 index 2f41341b1499da89896135849ce4a9c4008677da..0000000000000000000000000000000000000000 --- a/src/dump_analysis.cpp +++ /dev/null @@ -1,1380 +0,0 @@ -/* - * Copyright (c) 2019 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#include "dump_analysis.hpp" -#include "compiler.hpp" -#include "analyze.hpp" -#include "config.h" -#include "ir.hpp" -#include "codegen.hpp" - -enum JsonWriterState { - JsonWriterStateInvalid, - JsonWriterStateValue, - JsonWriterStateArrayStart, - JsonWriterStateArray, - JsonWriterStateObjectStart, - JsonWriterStateObject, -}; - -#define JSON_MAX_DEPTH 10 - -struct JsonWriter { - size_t state_index; - FILE *f; - const char *one_indent; - const char *nl; - JsonWriterState state[JSON_MAX_DEPTH]; -}; - -static void jw_init(JsonWriter *jw, FILE *f, const char *one_indent, const char *nl) { - jw->state_index = 1; - jw->f = f; - jw->one_indent = one_indent; - jw->nl = nl; - jw->state[0] = JsonWriterStateInvalid; - jw->state[1] = JsonWriterStateValue; -} - -static void jw_nl_indent(JsonWriter *jw) { - assert(jw->state_index >= 1); - fprintf(jw->f, "%s", jw->nl); - for (size_t i = 0; i < jw->state_index - 1; i += 1) { - fprintf(jw->f, "%s", jw->one_indent); - } -} - -static void jw_push_state(JsonWriter *jw, JsonWriterState state) { - jw->state_index += 1; - assert(jw->state_index < JSON_MAX_DEPTH); - jw->state[jw->state_index] = state; -} - -static void jw_pop_state(JsonWriter *jw) { - assert(jw->state_index != 0); - jw->state_index -= 1; -} - -static void jw_begin_array(JsonWriter *jw) { - assert(jw->state[jw->state_index] == JsonWriterStateValue); - fprintf(jw->f, "["); - jw->state[jw->state_index] = JsonWriterStateArrayStart; -} - -static void jw_begin_object(JsonWriter *jw) { - assert(jw->state[jw->state_index] == JsonWriterStateValue); - fprintf(jw->f, "{"); - jw->state[jw->state_index] = JsonWriterStateObjectStart; -} - -static void jw_array_elem(JsonWriter *jw) { - switch (jw->state[jw->state_index]) { - case JsonWriterStateInvalid: - case JsonWriterStateValue: - case JsonWriterStateObjectStart: - case JsonWriterStateObject: - zig_unreachable(); - case JsonWriterStateArray: - fprintf(jw->f, ","); - ZIG_FALLTHROUGH; - case JsonWriterStateArrayStart: - jw->state[jw->state_index] = JsonWriterStateArray; - jw_push_state(jw, JsonWriterStateValue); - jw_nl_indent(jw); - return; - } - zig_unreachable(); -} - -static void jw_write_escaped_string(JsonWriter *jw, const char *s) { - fprintf(jw->f, "\""); - for (;; s += 1) { - switch (*s) { - case 0: - fprintf(jw->f, "\""); - return; - case '"': - fprintf(jw->f, "\\\""); - continue; - case '\t': - fprintf(jw->f, "\\t"); - continue; - case '\r': - fprintf(jw->f, "\\r"); - continue; - case '\n': - fprintf(jw->f, "\\n"); - continue; - case '\b': - fprintf(jw->f, "\\b"); - continue; - case '\f': - fprintf(jw->f, "\\f"); - continue; - case '\\': - fprintf(jw->f, "\\\\"); - continue; - default: - fprintf(jw->f, "%c", *s); - continue; - } - } -} - -static void jw_object_field(JsonWriter *jw, const char *name) { - switch (jw->state[jw->state_index]) { - case JsonWriterStateInvalid: - case JsonWriterStateValue: - case JsonWriterStateArray: - case JsonWriterStateArrayStart: - zig_unreachable(); - case JsonWriterStateObject: - fprintf(jw->f, ","); - ZIG_FALLTHROUGH; - case JsonWriterStateObjectStart: - jw->state[jw->state_index] = JsonWriterStateObject; - jw_push_state(jw, JsonWriterStateValue); - jw_nl_indent(jw); - jw_write_escaped_string(jw, name); - fprintf(jw->f, ": "); - return; - } - zig_unreachable(); -} - -static void jw_end_array(JsonWriter *jw) { - switch (jw->state[jw->state_index]) { - case JsonWriterStateInvalid: - case JsonWriterStateValue: - case JsonWriterStateObjectStart: - case JsonWriterStateObject: - zig_unreachable(); - case JsonWriterStateArrayStart: - fprintf(jw->f, "]"); - jw_pop_state(jw); - return; - case JsonWriterStateArray: - jw_nl_indent(jw); - jw_pop_state(jw); - fprintf(jw->f, "]"); - return; - } - zig_unreachable(); -} - - -static void jw_end_object(JsonWriter *jw) { - switch (jw->state[jw->state_index]) { - case JsonWriterStateInvalid: - zig_unreachable(); - case JsonWriterStateValue: - zig_unreachable(); - case JsonWriterStateArray: - zig_unreachable(); - case JsonWriterStateArrayStart: - zig_unreachable(); - case JsonWriterStateObjectStart: - fprintf(jw->f, "}"); - jw_pop_state(jw); - return; - case JsonWriterStateObject: - jw_nl_indent(jw); - jw_pop_state(jw); - fprintf(jw->f, "}"); - return; - } - zig_unreachable(); -} - -static void jw_null(JsonWriter *jw) { - assert(jw->state[jw->state_index] == JsonWriterStateValue); - fprintf(jw->f, "null"); - jw_pop_state(jw); -} - -static void jw_bool(JsonWriter *jw, bool x) { - assert(jw->state[jw->state_index] == JsonWriterStateValue); - if (x) { - fprintf(jw->f, "true"); - } else { - fprintf(jw->f, "false"); - } - jw_pop_state(jw); -} - -static void jw_int(JsonWriter *jw, int64_t x) { - assert(jw->state[jw->state_index] == JsonWriterStateValue); - if (x > 4503599627370496 || x < -4503599627370496) { - fprintf(jw->f, "\"%" ZIG_PRI_i64 "\"", x); - } else { - fprintf(jw->f, "%" ZIG_PRI_i64, x); - } - jw_pop_state(jw); -} - -static void jw_bigint(JsonWriter *jw, const BigInt *x) { - assert(jw->state[jw->state_index] == JsonWriterStateValue); - Buf *str = buf_alloc(); - bigint_append_buf(str, x, 10); - - if (bigint_fits_in_bits(x, 52, true)) { - fprintf(jw->f, "%s", buf_ptr(str)); - } else { - fprintf(jw->f, "\"%s\"", buf_ptr(str)); - } - jw_pop_state(jw); - - buf_destroy(str); -} - -static void jw_string(JsonWriter *jw, const char *s) { - assert(jw->state[jw->state_index] == JsonWriterStateValue); - jw_write_escaped_string(jw, s); - jw_pop_state(jw); -} - - -static void tree_print(FILE *f, ZigType *ty, size_t indent); - -static int compare_type_abi_sizes_desc(const void *a, const void *b) { - uint64_t size_a = (*(ZigType * const*)(a))->abi_size; - uint64_t size_b = (*(ZigType * const*)(b))->abi_size; - if (size_a > size_b) - return -1; - if (size_a < size_b) - return 1; - return 0; -} - -static void start_child(FILE *f, size_t indent) { - fprintf(f, "\n"); - for (size_t i = 0; i < indent; i += 1) { - fprintf(f, " "); - } -} - -static void start_peer(FILE *f, size_t indent) { - fprintf(f, ",\n"); - for (size_t i = 0; i < indent; i += 1) { - fprintf(f, " "); - } -} - -static void tree_print_struct(FILE *f, ZigType *struct_type, size_t indent) { - ZigList children = {}; - uint64_t sum_from_fields = 0; - for (size_t i = 0; i < struct_type->data.structure.src_field_count; i += 1) { - TypeStructField *field = struct_type->data.structure.fields[i]; - children.append(field->type_entry); - sum_from_fields += field->type_entry->abi_size; - } - qsort(children.items, children.length, sizeof(ZigType *), compare_type_abi_sizes_desc); - - start_peer(f, indent); - fprintf(f, "\"padding\": \"%" ZIG_PRI_u64 "\"", struct_type->abi_size - sum_from_fields); - - start_peer(f, indent); - fprintf(f, "\"fields\": ["); - - for (size_t i = 0; i < children.length; i += 1) { - if (i == 0) { - start_child(f, indent + 1); - } else { - start_peer(f, indent + 1); - } - fprintf(f, "{"); - - ZigType *child_type = children.at(i); - tree_print(f, child_type, indent + 2); - - start_child(f, indent + 1); - fprintf(f, "}"); - } - - start_child(f, indent); - fprintf(f, "]"); -} - -static void tree_print(FILE *f, ZigType *ty, size_t indent) { - start_child(f, indent); - fprintf(f, "\"type\": \"%s\"", buf_ptr(&ty->name)); - - start_peer(f, indent); - fprintf(f, "\"sizef\": \""); - zig_pretty_print_bytes(f, ty->abi_size); - fprintf(f, "\""); - - start_peer(f, indent); - fprintf(f, "\"size\": \"%" ZIG_PRI_usize "\"", ty->abi_size); - - switch (ty->id) { - case ZigTypeIdFnFrame: - return tree_print_struct(f, ty->data.frame.locals_struct, indent); - case ZigTypeIdStruct: - return tree_print_struct(f, ty, indent); - default: - start_child(f, indent); - return; - } -} - -void zig_print_stack_report(CodeGen *g, FILE *f) { - if (g->largest_frame_fn == nullptr) { - fprintf(f, "{\"error\": \"No async function frames in entire compilation.\"}\n"); - return; - } - fprintf(f, "{"); - tree_print(f, g->largest_frame_fn->frame_type, 1); - - start_child(f, 0); - fprintf(f, "}\n"); -} - -struct AnalDumpCtx { - CodeGen *g; - JsonWriter jw; - - ZigList type_list; - HashMap type_map; - - ZigList pkg_list; - HashMap pkg_map; - - ZigList file_list; - HashMap file_map; - - ZigList decl_list; - HashMap decl_map; - - ZigList fn_list; - HashMap fn_map; - - ZigList node_list; - HashMap node_map; - - ZigList err_list; - HashMap err_map; -}; - -static uint32_t anal_dump_get_type_id(AnalDumpCtx *ctx, ZigType *ty); -static void anal_dump_value(AnalDumpCtx *ctx, AstNode *source_node, ZigType *ty, ZigValue *value); - -static void anal_dump_poke_value(AnalDumpCtx *ctx, AstNode *source_node, ZigType *ty, ZigValue *value) { - Error err; - if (value->type != ty) { - return; - } - if ((err = ir_resolve_lazy(ctx->g, source_node, value))) { - codegen_report_errors_and_exit(ctx->g); - } - if (value->special == ConstValSpecialUndef) { - return; - } - if (value->special == ConstValSpecialRuntime) { - return; - } - switch (ty->id) { - case ZigTypeIdMetaType: { - ZigType *val_ty = value->data.x_type; - (void)anal_dump_get_type_id(ctx, val_ty); - return; - } - default: - return; - } - zig_unreachable(); -} - -static uint32_t anal_dump_get_type_id(AnalDumpCtx *ctx, ZigType *ty) { - uint32_t type_id = ctx->type_list.length; - auto existing_entry = ctx->type_map.put_unique(ty, type_id); - if (existing_entry == nullptr) { - ctx->type_list.append(ty); - } else { - type_id = existing_entry->value; - } - return type_id; -} - -static uint32_t anal_dump_get_pkg_id(AnalDumpCtx *ctx, ZigPackage *pkg) { - assert(pkg != nullptr); - uint32_t pkg_id = ctx->pkg_list.length; - auto existing_entry = ctx->pkg_map.put_unique(pkg, pkg_id); - if (existing_entry == nullptr) { - ctx->pkg_list.append(pkg); - } else { - pkg_id = existing_entry->value; - } - return pkg_id; -} - -static uint32_t anal_dump_get_file_id(AnalDumpCtx *ctx, Buf *file) { - uint32_t file_id = ctx->file_list.length; - auto existing_entry = ctx->file_map.put_unique(file, file_id); - if (existing_entry == nullptr) { - ctx->file_list.append(file); - } else { - file_id = existing_entry->value; - } - return file_id; -} - -static uint32_t anal_dump_get_node_id(AnalDumpCtx *ctx, AstNode *node) { - uint32_t node_id = ctx->node_list.length; - auto existing_entry = ctx->node_map.put_unique(node, node_id); - if (existing_entry == nullptr) { - ctx->node_list.append(node); - } else { - node_id = existing_entry->value; - } - return node_id; -} - -static uint32_t anal_dump_get_fn_id(AnalDumpCtx *ctx, ZigFn *fn) { - uint32_t fn_id = ctx->fn_list.length; - auto existing_entry = ctx->fn_map.put_unique(fn, fn_id); - if (existing_entry == nullptr) { - ctx->fn_list.append(fn); - - // poke the fn - (void)anal_dump_get_type_id(ctx, fn->type_entry); - (void)anal_dump_get_node_id(ctx, fn->proto_node); - } else { - fn_id = existing_entry->value; - } - return fn_id; -} - -static uint32_t anal_dump_get_err_id(AnalDumpCtx *ctx, ErrorTableEntry *err) { - uint32_t err_id = ctx->err_list.length; - auto existing_entry = ctx->err_map.put_unique(err, err_id); - if (existing_entry == nullptr) { - ctx->err_list.append(err); - } else { - err_id = existing_entry->value; - } - return err_id; -} - -static uint32_t anal_dump_get_decl_id(AnalDumpCtx *ctx, Tld *tld) { - uint32_t decl_id = ctx->decl_list.length; - auto existing_entry = ctx->decl_map.put_unique(tld, decl_id); - if (existing_entry == nullptr) { - ctx->decl_list.append(tld); - - if (tld->import != nullptr) { - (void)anal_dump_get_type_id(ctx, tld->import); - } - - // poke the types - switch (tld->id) { - case TldIdVar: { - TldVar *tld_var = reinterpret_cast(tld); - ZigVar *var = tld_var->var; - - if (var != nullptr) { - (void)anal_dump_get_type_id(ctx, var->var_type); - - if (var->const_value != nullptr) { - anal_dump_poke_value(ctx, var->decl_node, var->var_type, var->const_value); - } - } - break; - } - case TldIdFn: { - TldFn *tld_fn = reinterpret_cast(tld); - ZigFn *fn = tld_fn->fn_entry; - - if (fn != nullptr) { - (void)anal_dump_get_type_id(ctx, fn->type_entry); - } - break; - } - default: - break; - } - - } else { - decl_id = existing_entry->value; - } - return decl_id; -} - -static void anal_dump_type_ref(AnalDumpCtx *ctx, ZigType *ty) { - uint32_t type_id = anal_dump_get_type_id(ctx, ty); - jw_int(&ctx->jw, type_id); -} - -static void anal_dump_pkg_ref(AnalDumpCtx *ctx, ZigPackage *pkg) { - uint32_t pkg_id = anal_dump_get_pkg_id(ctx, pkg); - jw_int(&ctx->jw, pkg_id); -} - -static void anal_dump_file_ref(AnalDumpCtx *ctx, Buf *file) { - uint32_t file_id = anal_dump_get_file_id(ctx, file); - jw_int(&ctx->jw, file_id); -} - -static void anal_dump_node_ref(AnalDumpCtx *ctx, AstNode *node) { - uint32_t node_id = anal_dump_get_node_id(ctx, node); - jw_int(&ctx->jw, node_id); -} - -static void anal_dump_fn_ref(AnalDumpCtx *ctx, ZigFn *fn) { - uint32_t fn_id = anal_dump_get_fn_id(ctx, fn); - jw_int(&ctx->jw, fn_id); -} - -static void anal_dump_err_ref(AnalDumpCtx *ctx, ErrorTableEntry *err) { - uint32_t err_id = anal_dump_get_err_id(ctx, err); - jw_int(&ctx->jw, err_id); -} - -static void anal_dump_decl_ref(AnalDumpCtx *ctx, Tld *tld) { - uint32_t decl_id = anal_dump_get_decl_id(ctx, tld); - jw_int(&ctx->jw, decl_id); -} - -static void anal_dump_pkg(AnalDumpCtx *ctx, ZigPackage *pkg) { - JsonWriter *jw = &ctx->jw; - - Buf full_path_buf = BUF_INIT; - os_path_join(&pkg->root_src_dir, &pkg->root_src_path, &full_path_buf); - Buf *resolve_paths[] = { &full_path_buf, }; - Buf *resolved_path = buf_alloc(); - *resolved_path = os_path_resolve(resolve_paths, 1); - - auto import_entry = ctx->g->import_table.maybe_get(resolved_path); - if (!import_entry) { - return; - } - - jw_array_elem(jw); - jw_begin_object(jw); - - jw_object_field(jw, "name"); - jw_string(jw, buf_ptr(&pkg->pkg_path)); - - jw_object_field(jw, "file"); - anal_dump_file_ref(ctx, resolved_path); - - jw_object_field(jw, "main"); - anal_dump_type_ref(ctx, import_entry->value); - - jw_object_field(jw, "table"); - jw_begin_object(jw); - auto it = pkg->package_table.entry_iterator(); - for (;;) { - auto *entry = it.next(); - if (!entry) - break; - - ZigPackage *child_pkg = entry->value; - if (child_pkg != nullptr) { - jw_object_field(jw, buf_ptr(entry->key)); - anal_dump_pkg_ref(ctx, child_pkg); - } - } - jw_end_object(jw); - - jw_end_object(jw); -} - -static void anal_dump_decl(AnalDumpCtx *ctx, Tld *tld) { - JsonWriter *jw = &ctx->jw; - - bool make_obj = tld->id == TldIdVar || tld->id == TldIdFn; - if (make_obj) { - jw_array_elem(jw); - jw_begin_object(jw); - - jw_object_field(jw, "import"); - anal_dump_type_ref(ctx, tld->import); - - jw_object_field(jw, "src"); - anal_dump_node_ref(ctx, tld->source_node); - - jw_object_field(jw, "name"); - jw_string(jw, buf_ptr(tld->name)); - } - - switch (tld->id) { - case TldIdVar: { - TldVar *tld_var = reinterpret_cast(tld); - ZigVar *var = tld_var->var; - - if (var != nullptr) { - jw_object_field(jw, "kind"); - if (var->src_is_const) { - jw_string(jw, "const"); - } else { - jw_string(jw, "var"); - } - - if (var->is_thread_local) { - jw_object_field(jw, "threadlocal"); - jw_bool(jw, true); - } - - jw_object_field(jw, "type"); - anal_dump_type_ref(ctx, var->var_type); - - if (var->const_value != nullptr) { - jw_object_field(jw, "value"); - anal_dump_value(ctx, var->decl_node, var->var_type, var->const_value); - } - } - break; - } - case TldIdFn: { - TldFn *tld_fn = reinterpret_cast(tld); - ZigFn *fn = tld_fn->fn_entry; - - if (fn != nullptr) { - jw_object_field(jw, "kind"); - jw_string(jw, "const"); - - jw_object_field(jw, "type"); - anal_dump_type_ref(ctx, fn->type_entry); - - jw_object_field(jw, "value"); - anal_dump_fn_ref(ctx, fn); - } - break; - } - default: - break; - } - - if (make_obj) { - jw_end_object(jw); - } -} - -static void anal_dump_file(AnalDumpCtx *ctx, Buf *file) { - JsonWriter *jw = &ctx->jw; - jw_string(jw, buf_ptr(file)); -} - -static void anal_dump_value(AnalDumpCtx *ctx, AstNode *source_node, ZigType *ty, ZigValue *value) { - Error err; - - if (value->type != ty) { - jw_null(&ctx->jw); - return; - } - if ((err = ir_resolve_lazy(ctx->g, source_node, value))) { - codegen_report_errors_and_exit(ctx->g); - } - if (value->special == ConstValSpecialUndef) { - jw_string(&ctx->jw, "undefined"); - return; - } - if (value->special == ConstValSpecialRuntime) { - jw_null(&ctx->jw); - return; - } - switch (ty->id) { - case ZigTypeIdMetaType: { - ZigType *val_ty = value->data.x_type; - anal_dump_type_ref(ctx, val_ty); - return; - } - case ZigTypeIdFn: { - if (value->data.x_ptr.special == ConstPtrSpecialFunction) { - ZigFn *val_fn = value->data.x_ptr.data.fn.fn_entry; - anal_dump_fn_ref(ctx, val_fn); - } else { - jw_null(&ctx->jw); - } - return; - } - case ZigTypeIdOptional: { - if(optional_value_is_null(value)){ - jw_string(&ctx->jw, "null"); - } else { - jw_null(&ctx->jw); - } - return; - } - case ZigTypeIdInt: { - jw_bigint(&ctx->jw, &value->data.x_bigint); - return; - } - default: - jw_null(&ctx->jw); - return; - } - zig_unreachable(); -} - -static void anal_dump_pointer_attrs(AnalDumpCtx *ctx, ZigType *ty) { - JsonWriter *jw = &ctx->jw; - if (ty->data.pointer.explicit_alignment != 0) { - jw_object_field(jw, "align"); - jw_int(jw, ty->data.pointer.explicit_alignment); - } - if (ty->data.pointer.is_const) { - jw_object_field(jw, "const"); - jw_bool(jw, true); - } - if (ty->data.pointer.is_volatile) { - jw_object_field(jw, "volatile"); - jw_bool(jw, true); - } - if (ty->data.pointer.allow_zero) { - jw_object_field(jw, "allowZero"); - jw_bool(jw, true); - } - if (ty->data.pointer.host_int_bytes != 0) { - jw_object_field(jw, "hostIntBytes"); - jw_int(jw, ty->data.pointer.host_int_bytes); - - jw_object_field(jw, "bitOffsetInHost"); - jw_int(jw, ty->data.pointer.bit_offset_in_host); - } - - jw_object_field(jw, "elem"); - anal_dump_type_ref(ctx, ty->data.pointer.child_type); -} - -static void anal_dump_type(AnalDumpCtx *ctx, ZigType *ty) { - JsonWriter *jw = &ctx->jw; - jw_array_elem(jw); - jw_begin_object(jw); - - jw_object_field(jw, "kind"); - jw_int(jw, type_id_index(ty)); - - switch (ty->id) { - case ZigTypeIdMetaType: - case ZigTypeIdBool: - case ZigTypeIdEnumLiteral: - break; - case ZigTypeIdStruct: { - if (ty->data.structure.special == StructSpecialSlice) { - jw_object_field(jw, "len"); - jw_int(jw, 2); - anal_dump_pointer_attrs(ctx, ty->data.structure.fields[slice_ptr_index]->type_entry); - break; - } - - jw_object_field(jw, "name"); - jw_string(jw, buf_ptr(&ty->name)); - - jw_object_field(jw, "src"); - anal_dump_node_ref(ctx, ty->data.structure.decl_node); - - { - jw_object_field(jw, "pubDecls"); - jw_begin_array(jw); - - ScopeDecls *decls_scope = ty->data.structure.decls_scope; - auto it = decls_scope->decl_table.entry_iterator(); - for (;;) { - auto *entry = it.next(); - if (!entry) - break; - - Tld *tld = entry->value; - if (tld->visib_mod == VisibModPub) { - jw_array_elem(jw); - anal_dump_decl_ref(ctx, tld); - } - } - jw_end_array(jw); - } - - { - jw_object_field(jw, "privDecls"); - jw_begin_array(jw); - - ScopeDecls *decls_scope = ty->data.structure.decls_scope; - auto it = decls_scope->decl_table.entry_iterator(); - for (;;) { - auto *entry = it.next(); - if (!entry) - break; - - Tld *tld = entry->value; - if (tld->visib_mod == VisibModPrivate) { - jw_array_elem(jw); - anal_dump_decl_ref(ctx, tld); - } - } - jw_end_array(jw); - } - - if (ty->data.structure.src_field_count != 0) { - jw_object_field(jw, "fields"); - jw_begin_array(jw); - - for(size_t i = 0; i < ty->data.structure.src_field_count; i += 1) { - jw_array_elem(jw); - anal_dump_type_ref(ctx, ty->data.structure.fields[i]->type_entry); - } - jw_end_array(jw); - } - - if (ty->data.structure.root_struct != nullptr) { - Buf *path_buf = ty->data.structure.root_struct->path; - - jw_object_field(jw, "file"); - anal_dump_file_ref(ctx, path_buf); - } - break; - } - case ZigTypeIdUnion: { - jw_object_field(jw, "name"); - jw_string(jw, buf_ptr(&ty->name)); - - jw_object_field(jw, "src"); - anal_dump_node_ref(ctx, ty->data.unionation.decl_node); - - { - jw_object_field(jw, "pubDecls"); - jw_begin_array(jw); - - ScopeDecls *decls_scope = ty->data.unionation.decls_scope; - auto it = decls_scope->decl_table.entry_iterator(); - for (;;) { - auto *entry = it.next(); - if (!entry) - break; - - Tld *tld = entry->value; - if (tld->visib_mod == VisibModPub) { - jw_array_elem(jw); - anal_dump_decl_ref(ctx, tld); - } - } - jw_end_array(jw); - } - - { - jw_object_field(jw, "privDecls"); - jw_begin_array(jw); - - ScopeDecls *decls_scope = ty->data.unionation.decls_scope; - auto it = decls_scope->decl_table.entry_iterator(); - for (;;) { - auto *entry = it.next(); - if (!entry) - break; - - Tld *tld = entry->value; - if (tld->visib_mod == VisibModPrivate) { - jw_array_elem(jw); - anal_dump_decl_ref(ctx, tld); - } - } - jw_end_array(jw); - } - - if (ty->data.unionation.src_field_count != 0) { - jw_object_field(jw, "fields"); - jw_begin_array(jw); - - for(size_t i = 0; i < ty->data.unionation.src_field_count; i += 1) { - jw_array_elem(jw); - anal_dump_type_ref(ctx, ty->data.unionation.fields[i].type_entry); - } - jw_end_array(jw); - } - break; - } - case ZigTypeIdEnum: { - jw_object_field(jw, "name"); - jw_string(jw, buf_ptr(&ty->name)); - - jw_object_field(jw, "src"); - anal_dump_node_ref(ctx, ty->data.enumeration.decl_node); - - { - jw_object_field(jw, "pubDecls"); - jw_begin_array(jw); - - ScopeDecls *decls_scope = ty->data.enumeration.decls_scope; - auto it = decls_scope->decl_table.entry_iterator(); - for (;;) { - auto *entry = it.next(); - if (!entry) - break; - - Tld *tld = entry->value; - if (tld->visib_mod == VisibModPub) { - jw_array_elem(jw); - anal_dump_decl_ref(ctx, tld); - } - } - jw_end_array(jw); - } - - { - jw_object_field(jw, "privDecls"); - jw_begin_array(jw); - - ScopeDecls *decls_scope = ty->data.enumeration.decls_scope; - auto it = decls_scope->decl_table.entry_iterator(); - for (;;) { - auto *entry = it.next(); - if (!entry) - break; - - Tld *tld = entry->value; - if (tld->visib_mod == VisibModPrivate) { - jw_array_elem(jw); - anal_dump_decl_ref(ctx, tld); - } - } - jw_end_array(jw); - } - - if (ty->data.enumeration.src_field_count != 0) { - jw_object_field(jw, "fields"); - jw_begin_array(jw); - - for(size_t i = 0; i < ty->data.enumeration.src_field_count; i += 1) { - jw_array_elem(jw); - jw_bigint(jw, &ty->data.enumeration.fields[i].value); - } - jw_end_array(jw); - } - break; - } - case ZigTypeIdFloat: { - jw_object_field(jw, "bits"); - jw_int(jw, ty->data.floating.bit_count); - break; - } - case ZigTypeIdInt: { - if (ty->data.integral.is_signed) { - jw_object_field(jw, "i"); - } else { - jw_object_field(jw, "u"); - } - jw_int(jw, ty->data.integral.bit_count); - break; - } - case ZigTypeIdFn: { - jw_object_field(jw, "name"); - jw_string(jw, buf_ptr(&ty->name)); - - jw_object_field(jw, "generic"); - jw_bool(jw, ty->data.fn.is_generic); - - if (ty->data.fn.fn_type_id.return_type != nullptr) { - jw_object_field(jw, "ret"); - anal_dump_type_ref(ctx, ty->data.fn.fn_type_id.return_type); - } - - if (ty->data.fn.fn_type_id.param_count != 0) { - jw_object_field(jw, "args"); - jw_begin_array(jw); - for (size_t i = 0; i < ty->data.fn.fn_type_id.param_count; i += 1) { - jw_array_elem(jw); - if (ty->data.fn.fn_type_id.param_info[i].type != nullptr) { - anal_dump_type_ref(ctx, ty->data.fn.fn_type_id.param_info[i].type); - } else { - jw_null(jw); - } - } - jw_end_array(jw); - } - break; - } - case ZigTypeIdOptional: { - jw_object_field(jw, "child"); - anal_dump_type_ref(ctx, ty->data.maybe.child_type); - break; - } - case ZigTypeIdPointer: { - switch (ty->data.pointer.ptr_len) { - case PtrLenSingle: - break; - case PtrLenUnknown: - jw_object_field(jw, "len"); - jw_int(jw, 1); - break; - case PtrLenC: - jw_object_field(jw, "len"); - jw_int(jw, 3); - break; - } - anal_dump_pointer_attrs(ctx, ty); - break; - } - case ZigTypeIdErrorSet: { - if (type_is_global_error_set(ty)) { - break; - } - jw_object_field(jw, "name"); - jw_string(jw, buf_ptr(&ty->name)); - - if (ty->data.error_set.infer_fn != nullptr) { - jw_object_field(jw, "fn"); - anal_dump_fn_ref(ctx, ty->data.error_set.infer_fn); - } - jw_object_field(jw, "errors"); - jw_begin_array(jw); - for (uint32_t i = 0; i < ty->data.error_set.err_count; i += 1) { - jw_array_elem(jw); - ErrorTableEntry *err = ty->data.error_set.errors[i]; - anal_dump_err_ref(ctx, err); - } - jw_end_array(jw); - break; - } - case ZigTypeIdErrorUnion: { - jw_object_field(jw, "err"); - anal_dump_type_ref(ctx, ty->data.error_union.err_set_type); - - jw_object_field(jw, "payload"); - anal_dump_type_ref(ctx, ty->data.error_union.payload_type); - - break; - } - case ZigTypeIdArray: { - jw_object_field(jw, "len"); - jw_int(jw, ty->data.array.len); - - jw_object_field(jw, "elem"); - anal_dump_type_ref(ctx, ty->data.array.child_type); - break; - } - default: - jw_object_field(jw, "name"); - jw_string(jw, buf_ptr(&ty->name)); - break; - } - jw_end_object(jw); -} - -static void anal_dump_node(AnalDumpCtx *ctx, const AstNode *node) { - JsonWriter *jw = &ctx->jw; - - jw_begin_object(jw); - - jw_object_field(jw, "file"); - anal_dump_file_ref(ctx, node->owner->data.structure.root_struct->path); - - jw_object_field(jw, "line"); - jw_int(jw, node->line); - - jw_object_field(jw, "col"); - jw_int(jw, node->column); - - const Buf *doc_comments_buf = nullptr; - const Buf *name_buf = nullptr; - const ZigList *field_nodes = nullptr; - bool is_var_args = false; - bool is_noalias = false; - bool is_comptime = false; - - switch (node->type) { - case NodeTypeParamDecl: - doc_comments_buf = &node->data.param_decl.doc_comments; - name_buf = node->data.param_decl.name; - is_var_args = node->data.param_decl.is_var_args; - is_noalias = node->data.param_decl.is_noalias; - is_comptime = node->data.param_decl.is_comptime; - break; - case NodeTypeFnProto: - doc_comments_buf = &node->data.fn_proto.doc_comments; - field_nodes = &node->data.fn_proto.params; - is_var_args = node->data.fn_proto.is_var_args; - break; - case NodeTypeVariableDeclaration: - doc_comments_buf = &node->data.variable_declaration.doc_comments; - break; - case NodeTypeErrorSetField: - doc_comments_buf = &node->data.err_set_field.doc_comments; - break; - case NodeTypeStructField: - doc_comments_buf = &node->data.struct_field.doc_comments; - name_buf = node->data.struct_field.name; - break; - case NodeTypeContainerDecl: - field_nodes = &node->data.container_decl.fields; - doc_comments_buf = &node->data.container_decl.doc_comments; - break; - default: - break; - } - - if (doc_comments_buf != nullptr && doc_comments_buf->list.length != 0) { - jw_object_field(jw, "docs"); - jw_string(jw, buf_ptr(doc_comments_buf)); - } - - if (name_buf != nullptr) { - jw_object_field(jw, "name"); - jw_string(jw, buf_ptr(name_buf)); - } - - if (field_nodes != nullptr) { - jw_object_field(jw, "fields"); - jw_begin_array(jw); - for (size_t i = 0; i < field_nodes->length; i += 1) { - jw_array_elem(jw); - anal_dump_node_ref(ctx, field_nodes->at(i)); - } - jw_end_array(jw); - } - - if (is_var_args) { - jw_object_field(jw, "varArgs"); - jw_bool(jw, true); - } - - if (is_comptime) { - jw_object_field(jw, "comptime"); - jw_bool(jw, true); - } - - if (is_noalias) { - jw_object_field(jw, "noalias"); - jw_bool(jw, true); - } - - jw_end_object(jw); -} - -static void anal_dump_err(AnalDumpCtx *ctx, const ErrorTableEntry *err) { - JsonWriter *jw = &ctx->jw; - - jw_begin_object(jw); - - jw_object_field(jw, "src"); - anal_dump_node_ref(ctx, err->decl_node); - - jw_object_field(jw, "name"); - jw_string(jw, buf_ptr(&err->name)); - - jw_end_object(jw); -} - -static void anal_dump_fn(AnalDumpCtx *ctx, ZigFn *fn) { - JsonWriter *jw = &ctx->jw; - - jw_begin_object(jw); - - jw_object_field(jw, "src"); - anal_dump_node_ref(ctx, fn->proto_node); - - jw_object_field(jw, "type"); - anal_dump_type_ref(ctx, fn->type_entry); - - jw_end_object(jw); -} - -void zig_print_analysis_dump(CodeGen *g, FILE *f, const char *one_indent, const char *nl) { - Error err; - AnalDumpCtx ctx = {}; - ctx.g = g; - JsonWriter *jw = &ctx.jw; - jw_init(jw, f, one_indent, nl); - ctx.type_map.init(16); - ctx.pkg_map.init(16); - ctx.file_map.init(16); - ctx.decl_map.init(16); - ctx.node_map.init(16); - ctx.fn_map.init(16); - ctx.err_map.init(16); - - jw_begin_object(jw); - - jw_object_field(jw, "typeKinds"); - jw_begin_array(jw); - for (size_t i = 0; i < type_id_len(); i += 1) { - jw_array_elem(jw); - jw_string(jw, type_id_name(type_id_at_index(i))); - } - jw_end_array(jw); - - jw_object_field(jw, "params"); - jw_begin_object(jw); - { - jw_object_field(jw, "zigId"); - - Buf *compiler_id; - if ((err = get_compiler_id(&compiler_id))) { - fprintf(stderr, "Unable to determine compiler id: %s\n", err_str(err)); - exit(1); - } - jw_string(jw, buf_ptr(compiler_id)); - - jw_object_field(jw, "zigVersion"); - jw_string(jw, ZIG_VERSION_STRING); - - jw_object_field(jw, "builds"); - jw_begin_array(jw); - jw_array_elem(jw); - jw_begin_object(jw); - jw_object_field(jw, "target"); - Buf triple_buf = BUF_INIT; - target_triple_zig(&triple_buf, g->zig_target); - jw_string(jw, buf_ptr(&triple_buf)); - jw_end_object(jw); - jw_end_array(jw); - - jw_object_field(jw, "rootName"); - jw_string(jw, buf_ptr(g->root_out_name)); - } - jw_end_object(jw); - - jw_object_field(jw, "rootPkg"); - anal_dump_pkg_ref(&ctx, g->main_pkg); - - // FIXME: Remove this ugly workaround. - // Right now the code in docs/main.js relies on the root of the main package being itself. - g->main_pkg->package_table.put(buf_create_from_str("root"), g->main_pkg); - - // Poke the functions - for (size_t i = 0; i < g->fn_defs.length; i += 1) { - ZigFn *fn = g->fn_defs.at(i); - (void)anal_dump_get_fn_id(&ctx, fn); - } - - jw_object_field(jw, "calls"); - jw_begin_array(jw); - { - ZigList var_stack = {}; - - auto it = g->memoized_fn_eval_table.entry_iterator(); - for (;;) { - auto *entry = it.next(); - if (!entry) - break; - - var_stack.resize(0); - ZigFn *fn = nullptr; - - Scope *scope = entry->key; - while (scope != nullptr) { - if (scope->id == ScopeIdVarDecl) { - ZigVar *var = reinterpret_cast(scope)->var; - var_stack.append(var); - } else if (scope->id == ScopeIdFnDef) { - fn = reinterpret_cast(scope)->fn_entry; - break; - } - scope = scope->parent; - } - ZigValue *result = entry->value; - - assert(fn != nullptr); - - jw_array_elem(jw); - jw_begin_object(jw); - - jw_object_field(jw, "fn"); - anal_dump_fn_ref(&ctx, fn); - - jw_object_field(jw, "result"); - { - jw_begin_object(jw); - - jw_object_field(jw, "type"); - anal_dump_type_ref(&ctx, result->type); - - jw_object_field(jw, "value"); - anal_dump_value(&ctx, scope->source_node, result->type, result); - - jw_end_object(jw); - } - - if (var_stack.length != 0) { - jw_object_field(jw, "args"); - jw_begin_array(jw); - - while (var_stack.length != 0) { - ZigVar *var = var_stack.pop(); - - jw_array_elem(jw); - jw_begin_object(jw); - - jw_object_field(jw, "type"); - anal_dump_type_ref(&ctx, var->var_type); - - jw_object_field(jw, "value"); - anal_dump_value(&ctx, scope->source_node, var->var_type, var->const_value); - - jw_end_object(jw); - } - jw_end_array(jw); - } - - jw_end_object(jw); - } - - var_stack.deinit(); - } - jw_end_array(jw); - - jw_object_field(jw, "packages"); - jw_begin_array(jw); - for (uint32_t i = 0; i < ctx.pkg_list.length; i += 1) { - anal_dump_pkg(&ctx, ctx.pkg_list.at(i)); - } - jw_end_array(jw); - - jw_object_field(jw, "types"); - jw_begin_array(jw); - - for (uint32_t i = 0; i < ctx.type_list.length; i += 1) { - ZigType *ty = ctx.type_list.at(i); - anal_dump_type(&ctx, ty); - } - jw_end_array(jw); - - jw_object_field(jw, "decls"); - jw_begin_array(jw); - for (uint32_t i = 0; i < ctx.decl_list.length; i += 1) { - Tld *decl = ctx.decl_list.at(i); - anal_dump_decl(&ctx, decl); - } - jw_end_array(jw); - - jw_object_field(jw, "fns"); - jw_begin_array(jw); - for (uint32_t i = 0; i < ctx.fn_list.length; i += 1) { - ZigFn *fn = ctx.fn_list.at(i); - jw_array_elem(jw); - anal_dump_fn(&ctx, fn); - } - jw_end_array(jw); - - jw_object_field(jw, "errors"); - jw_begin_array(jw); - for (uint32_t i = 0; i < ctx.err_list.length; i += 1) { - const ErrorTableEntry *err = ctx.err_list.at(i); - jw_array_elem(jw); - anal_dump_err(&ctx, err); - } - jw_end_array(jw); - - jw_object_field(jw, "astNodes"); - jw_begin_array(jw); - for (uint32_t i = 0; i < ctx.node_list.length; i += 1) { - const AstNode *node = ctx.node_list.at(i); - jw_array_elem(jw); - anal_dump_node(&ctx, node); - } - jw_end_array(jw); - - jw_object_field(jw, "files"); - jw_begin_array(jw); - for (uint32_t i = 0; i < ctx.file_list.length; i += 1) { - Buf *file = ctx.file_list.at(i); - jw_array_elem(jw); - anal_dump_file(&ctx, file); - } - jw_end_array(jw); - - jw_end_object(jw); -} diff --git a/src/dump_analysis.hpp b/src/dump_analysis.hpp deleted file mode 100644 index 6d1c644ea208b5ccf6b51dbf92d3943a09d5b836..0000000000000000000000000000000000000000 --- a/src/dump_analysis.hpp +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2019 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#ifndef ZIG_DUMP_ANALYSIS_HPP -#define ZIG_DUMP_ANALYSIS_HPP - -#include "all_types.hpp" -#include - -void zig_print_stack_report(CodeGen *g, FILE *f); -void zig_print_analysis_dump(CodeGen *g, FILE *f, const char *one_indent, const char *nl); - -#endif diff --git a/src/errmsg.cpp b/src/errmsg.cpp deleted file mode 100644 index 7bf096547fdb00805179d95c295080a95624eda8..0000000000000000000000000000000000000000 --- a/src/errmsg.cpp +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (c) 2016 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#include "errmsg.hpp" -#include "os.hpp" - -#include - -enum ErrType { - ErrTypeError, - ErrTypeNote, -}; - -static void print_err_msg_type(ErrorMsg *err, ErrColor color, ErrType err_type) { - bool is_tty = os_stderr_tty(); - bool use_colors = color == ErrColorOn || (color == ErrColorAuto && is_tty); - - // Show the error location, if available - if (err->path != nullptr) { - const char *path = buf_ptr(err->path); - Slice pathslice{path, strlen(path)}; - - // Cache cwd - static Buf *cwdbuf{nullptr}; - static Slice cwd; - - if (cwdbuf == nullptr) { - cwdbuf = buf_alloc(); - Error err = os_get_cwd(cwdbuf); - if (err != ErrorNone) - zig_panic("get cwd failed"); - buf_append_char(cwdbuf, ZIG_OS_SEP_CHAR); - cwd.ptr = buf_ptr(cwdbuf); - cwd.len = strlen(cwd.ptr); - } - - const size_t line = err->line_start + 1; - const size_t col = err->column_start + 1; - if (use_colors) os_stderr_set_color(TermColorBold); - - // Strip cwd from path - if (memStartsWith(pathslice, cwd)) - fprintf(stderr, ".%c%s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize ": ", ZIG_OS_SEP_CHAR, path+cwd.len, line, col); - else - fprintf(stderr, "%s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize ": ", path, line, col); - } - - // Write out the error type - switch (err_type) { - case ErrTypeError: - if (use_colors) os_stderr_set_color(TermColorRed); - fprintf(stderr, "error: "); - break; - case ErrTypeNote: - if (use_colors) os_stderr_set_color(TermColorCyan); - fprintf(stderr, "note: "); - break; - default: - zig_unreachable(); - } - - // Write out the error message - if (use_colors) os_stderr_set_color(TermColorBold); - fputs(buf_ptr(err->msg), stderr); - if (use_colors) os_stderr_set_color(TermColorReset); - fputc('\n', stderr); - - if (buf_len(&err->line_buf) != 0){ - // Show the referenced line - fprintf(stderr, "%s\n", buf_ptr(&err->line_buf)); - for (size_t i = 0; i < err->column_start; i += 1) { - fprintf(stderr, " "); - } - // Draw the caret - if (use_colors) os_stderr_set_color(TermColorGreen); - fprintf(stderr, "^"); - if (use_colors) os_stderr_set_color(TermColorReset); - fprintf(stderr, "\n"); - } - - for (size_t i = 0; i < err->notes.length; i += 1) { - ErrorMsg *note = err->notes.at(i); - print_err_msg_type(note, color, ErrTypeNote); - } -} - -void print_err_msg(ErrorMsg *err, ErrColor color) { - print_err_msg_type(err, color, ErrTypeError); -} - -void err_msg_add_note(ErrorMsg *parent, ErrorMsg *note) { - parent->notes.append(note); -} - -ErrorMsg *err_msg_create_with_offset(Buf *path, size_t line, size_t column, size_t offset, - const char *source, Buf *msg) -{ - ErrorMsg *err_msg = heap::c_allocator.create(); - err_msg->path = path; - err_msg->line_start = line; - err_msg->column_start = column; - err_msg->msg = msg; - - if (source == nullptr) { - // Must initialize the buffer anyway - buf_init_from_str(&err_msg->line_buf, ""); - return err_msg; - } - - size_t line_start_offset = offset; - for (;;) { - if (line_start_offset == 0) { - break; - } - - line_start_offset -= 1; - - if (source[line_start_offset] == '\n') { - line_start_offset += 1; - break; - } - } - - size_t line_end_offset = offset; - while (source[line_end_offset] && source[line_end_offset] != '\n') { - line_end_offset += 1; - } - - buf_init_from_mem(&err_msg->line_buf, source + line_start_offset, line_end_offset - line_start_offset); - - return err_msg; -} - -ErrorMsg *err_msg_create_with_line(Buf *path, size_t line, size_t column, - Buf *source, ZigList *line_offsets, Buf *msg) -{ - ErrorMsg *err_msg = heap::c_allocator.create(); - err_msg->path = path; - err_msg->line_start = line; - err_msg->column_start = column; - err_msg->msg = msg; - - size_t line_start_offset = line_offsets->at(line); - size_t end_line = line + 1; - size_t line_end_offset = (end_line >= line_offsets->length) ? buf_len(source) : line_offsets->at(line + 1); - size_t len = (line_end_offset + 1 > line_start_offset) ? (line_end_offset - line_start_offset - 1) : 0; - if (len == SIZE_MAX) len = 0; - - buf_init_from_mem(&err_msg->line_buf, buf_ptr(source) + line_start_offset, len); - - return err_msg; -} diff --git a/src/errmsg.hpp b/src/errmsg.hpp deleted file mode 100644 index e8b2f5872d56aee8dbf0d824c425f226890c2e87..0000000000000000000000000000000000000000 --- a/src/errmsg.hpp +++ /dev/null @@ -1,39 +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_ERRMSG_HPP -#define ZIG_ERRMSG_HPP - -#include "buffer.hpp" -#include "list.hpp" - -enum ErrColor { - ErrColorAuto, - ErrColorOff, - ErrColorOn, -}; - -struct ErrorMsg { - size_t line_start; - size_t column_start; - Buf *msg; - Buf *path; - Buf line_buf; - - ZigList notes; -}; - -void print_err_msg(ErrorMsg *msg, ErrColor color); - -void err_msg_add_note(ErrorMsg *parent, ErrorMsg *note); -ErrorMsg *err_msg_create_with_offset(Buf *path, size_t line, size_t column, size_t offset, - const char *source, Buf *msg); - -ErrorMsg *err_msg_create_with_line(Buf *path, size_t line, size_t column, - Buf *source, ZigList *line_offsets, Buf *msg); - -#endif diff --git a/src/error.cpp b/src/error.cpp deleted file mode 100644 index d8bb4ac8a2b1a163f7590c3d14c89a923029255f..0000000000000000000000000000000000000000 --- a/src/error.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2016 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#include "error.hpp" - -const char *err_str(Error err) { - switch (err) { - case ErrorNone: return "(no error)"; - case ErrorNoMem: return "out of memory"; - case ErrorInvalidFormat: return "invalid format"; - case ErrorSemanticAnalyzeFail: return "semantic analyze failed"; - case ErrorAccess: return "access denied"; - case ErrorInterrupted: return "interrupted"; - case ErrorSystemResources: return "lack of system resources"; - case ErrorFileNotFound: return "file not found"; - case ErrorFileSystem: return "file system error"; - case ErrorFileTooBig: return "file too big"; - case ErrorDivByZero: return "division by zero"; - case ErrorOverflow: return "overflow"; - case ErrorPathAlreadyExists: return "path already exists"; - case ErrorUnexpected: return "unexpected error"; - case ErrorExactDivRemainder: return "exact division had a remainder"; - case ErrorNegativeDenominator: return "negative denominator"; - case ErrorShiftedOutOneBits: return "exact shift shifted out one bits"; - case ErrorCCompileErrors: return "C compile errors"; - case ErrorEndOfFile: return "end of file"; - case ErrorIsDir: return "is directory"; - case ErrorNotDir: return "not a directory"; - case ErrorUnsupportedOperatingSystem: return "unsupported operating system"; - case ErrorSharingViolation: return "sharing violation"; - case ErrorPipeBusy: return "pipe busy"; - case ErrorPrimitiveTypeNotFound: return "primitive type not found"; - case ErrorCacheUnavailable: return "cache unavailable"; - case ErrorPathTooLong: return "path too long"; - case ErrorCCompilerCannotFindFile: return "C compiler cannot find file"; - case ErrorReadingDepFile: return "failed to read .d file"; - case ErrorInvalidDepFile: return "invalid .d file"; - case ErrorMissingArchitecture: return "missing architecture"; - case ErrorMissingOperatingSystem: return "missing operating system"; - case ErrorUnknownArchitecture: return "unrecognized architecture"; - case ErrorUnknownOperatingSystem: return "unrecognized operating system"; - case ErrorUnknownABI: return "unrecognized C ABI"; - case ErrorInvalidFilename: return "invalid filename"; - case ErrorDiskQuota: return "disk space quota exceeded"; - case ErrorDiskSpace: return "out of disk space"; - case ErrorUnexpectedWriteFailure: return "unexpected write failure"; - case ErrorUnexpectedSeekFailure: return "unexpected seek failure"; - case ErrorUnexpectedFileTruncationFailure: return "unexpected file truncation failure"; - case ErrorUnimplemented: return "unimplemented"; - case ErrorOperationAborted: return "operation aborted"; - case ErrorBrokenPipe: return "broken pipe"; - case ErrorNoSpaceLeft: return "no space left"; - case ErrorNoCCompilerInstalled: return "no C compiler installed"; - case ErrorNotLazy: return "not lazy"; - case ErrorIsAsync: return "is async"; - case ErrorImportOutsidePkgPath: return "import of file outside package path"; - case ErrorUnknownCpu: return "unknown CPU"; - case ErrorUnknownCpuFeature: return "unknown CPU feature"; - case ErrorInvalidCpuFeatures: return "invalid CPU features"; - case ErrorInvalidLlvmCpuFeaturesFormat: return "invalid LLVM CPU features format"; - case ErrorUnknownApplicationBinaryInterface: return "unknown application binary interface"; - case ErrorASTUnitFailure: return "compiler bug: clang encountered a compile error, but the libclang API does not expose the error. See https://github.com/ziglang/zig/issues/4455 for more details"; - case ErrorBadPathName: return "bad path name"; - case ErrorSymLinkLoop: return "sym link loop"; - case ErrorProcessFdQuotaExceeded: return "process fd quota exceeded"; - case ErrorSystemFdQuotaExceeded: return "system fd quota exceeded"; - case ErrorNoDevice: return "no device"; - case ErrorDeviceBusy: return "device busy"; - case ErrorUnableToSpawnCCompiler: return "unable to spawn system C compiler"; - case ErrorCCompilerExitCode: return "system C compiler exited with failure code"; - case ErrorCCompilerCrashed: return "system C compiler crashed"; - case ErrorCCompilerCannotFindHeaders: return "system C compiler cannot find libc headers"; - case ErrorLibCRuntimeNotFound: return "libc runtime not found"; - case ErrorLibCStdLibHeaderNotFound: return "libc std lib headers not found"; - case ErrorLibCKernel32LibNotFound: return "kernel32 library not found"; - case ErrorUnsupportedArchitecture: return "unsupported architecture"; - case ErrorWindowsSdkNotFound: return "Windows SDK not found"; - case ErrorUnknownDynamicLinkerPath: return "unknown dynamic linker path"; - case ErrorTargetHasNoDynamicLinker: return "target has no dynamic linker"; - case ErrorInvalidAbiVersion: return "invalid C ABI version"; - case ErrorInvalidOperatingSystemVersion: return "invalid operating system version"; - case ErrorUnknownClangOption: return "unknown Clang option"; - case ErrorNestedResponseFile: return "nested response file"; - case ErrorZigIsTheCCompiler: return "Zig was not provided with libc installation information, and so it does not know where the libc paths are on the system. Zig attempted to use the system C compiler to find out where the libc paths are, but discovered that Zig is being used as the system C compiler."; - case ErrorFileBusy: return "file is busy"; - case ErrorLocked: return "file is locked by another process"; - } - return "(invalid error)"; -} diff --git a/src/error.hpp b/src/error.hpp deleted file mode 100644 index 90772df10814b6f8ce452675803e9847658eb5cf..0000000000000000000000000000000000000000 --- a/src/error.hpp +++ /dev/null @@ -1,17 +0,0 @@ -/* - * Copyright (c) 2015 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#ifndef ERROR_HPP -#define ERROR_HPP - -#include "stage2.h" - -const char *err_str(Error err); - -#define assertNoError(err) assert((err) == ErrorNone); - -#endif diff --git a/src/glibc.cpp b/src/glibc.cpp deleted file mode 100644 index 62f5604ba7ac3439d0f667ae1c7c55ec436165d2..0000000000000000000000000000000000000000 --- a/src/glibc.cpp +++ /dev/null @@ -1,392 +0,0 @@ -/* - * Copyright (c) 2019 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#include "glibc.hpp" -#include "compiler.hpp" -#include "cache_hash.hpp" -#include "codegen.hpp" - -static const ZigGLibCLib glibc_libs[] = { - {"c", 6}, - {"m", 6}, - {"pthread", 0}, - {"dl", 2}, - {"rt", 1}, - {"ld", 2}, - {"util", 1}, -}; - -Error glibc_load_metadata(ZigGLibCAbi **out_result, Buf *zig_lib_dir, bool verbose) { - Error err; - - ZigGLibCAbi *glibc_abi = heap::c_allocator.create(); - glibc_abi->vers_txt_path = buf_sprintf("%s" OS_SEP "libc" OS_SEP "glibc" OS_SEP "vers.txt", buf_ptr(zig_lib_dir)); - glibc_abi->fns_txt_path = buf_sprintf("%s" OS_SEP "libc" OS_SEP "glibc" OS_SEP "fns.txt", buf_ptr(zig_lib_dir)); - glibc_abi->abi_txt_path = buf_sprintf("%s" OS_SEP "libc" OS_SEP "glibc" OS_SEP "abi.txt", buf_ptr(zig_lib_dir)); - glibc_abi->version_table.init(16); - - Buf *vers_txt_contents = buf_alloc(); - if ((err = os_fetch_file_path(glibc_abi->vers_txt_path, vers_txt_contents))) { - if (verbose) { - fprintf(stderr, "Unable to read %s: %s\n", buf_ptr(glibc_abi->vers_txt_path), err_str(err)); - } - return err; - } - Buf *fns_txt_contents = buf_alloc(); - if ((err = os_fetch_file_path(glibc_abi->fns_txt_path, fns_txt_contents))) { - if (verbose) { - fprintf(stderr, "Unable to read %s: %s\n", buf_ptr(glibc_abi->fns_txt_path), err_str(err)); - } - return err; - } - Buf *abi_txt_contents = buf_alloc(); - if ((err = os_fetch_file_path(glibc_abi->abi_txt_path, abi_txt_contents))) { - if (verbose) { - fprintf(stderr, "Unable to read %s: %s\n", buf_ptr(glibc_abi->abi_txt_path), err_str(err)); - } - return err; - } - - { - SplitIterator it = memSplit(buf_to_slice(vers_txt_contents), str("\r\n")); - for (;;) { - Optional> opt_component = SplitIterator_next(&it); - if (!opt_component.is_some) break; - Buf *ver_buf = buf_create_from_slice(opt_component.value); - Stage2SemVer *this_ver = glibc_abi->all_versions.add_one(); - if ((err = target_parse_glibc_version(this_ver, buf_ptr(ver_buf)))) { - if (verbose) { - fprintf(stderr, "Unable to parse glibc version '%s': %s\n", buf_ptr(ver_buf), err_str(err)); - } - return err; - } - } - } - { - SplitIterator it = memSplit(buf_to_slice(fns_txt_contents), str("\r\n")); - for (;;) { - Optional> opt_component = SplitIterator_next(&it); - if (!opt_component.is_some) break; - SplitIterator line_it = memSplit(opt_component.value, str(" ")); - Optional> opt_fn_name = SplitIterator_next(&line_it); - if (!opt_fn_name.is_some) { - if (verbose) { - fprintf(stderr, "%s: Expected function name\n", buf_ptr(glibc_abi->fns_txt_path)); - } - return ErrorInvalidFormat; - } - Optional> opt_lib_name = SplitIterator_next(&line_it); - if (!opt_lib_name.is_some) { - if (verbose) { - fprintf(stderr, "%s: Expected lib name\n", buf_ptr(glibc_abi->fns_txt_path)); - } - return ErrorInvalidFormat; - } - - Buf *this_fn_name = buf_create_from_slice(opt_fn_name.value); - Buf *this_lib_name = buf_create_from_slice(opt_lib_name.value); - glibc_abi->all_functions.append({ this_fn_name, glibc_lib_find(buf_ptr(this_lib_name)) }); - } - } - { - SplitIterator it = memSplit(buf_to_slice(abi_txt_contents), str("\r\n")); - ZigGLibCVerList *ver_list_base = nullptr; - int line_num = 0; - for (;;) { - if (ver_list_base == nullptr) { - line_num += 1; - Optional> opt_line = SplitIterator_next_separate(&it); - if (!opt_line.is_some) break; - - ver_list_base = heap::c_allocator.allocate(glibc_abi->all_functions.length); - SplitIterator line_it = memSplit(opt_line.value, str(" ")); - for (;;) { - ZigTarget *target = heap::c_allocator.create(); - Optional> opt_target = SplitIterator_next(&line_it); - if (!opt_target.is_some) break; - - SplitIterator component_it = memSplit(opt_target.value, str("-")); - Optional> opt_arch = SplitIterator_next(&component_it); - assert(opt_arch.is_some); - Optional> opt_os = SplitIterator_next(&component_it); - assert(opt_os.is_some); // it's always "linux" so we ignore it - Optional> opt_abi = SplitIterator_next(&component_it); - assert(opt_abi.is_some); - - - err = target_parse_arch(&target->arch, (char*)opt_arch.value.ptr, opt_arch.value.len); - assert(err == ErrorNone); - - target->os = OsLinux; - - err = target_parse_abi(&target->abi, (char*)opt_abi.value.ptr, opt_abi.value.len); - if (err != ErrorNone) { - fprintf(stderr, "Error parsing %s:%d: %s\n", buf_ptr(glibc_abi->abi_txt_path), - line_num, err_str(err)); - fprintf(stderr, "arch: '%.*s', os: '%.*s', abi: '%.*s'\n", - (int)opt_arch.value.len, (const char*)opt_arch.value.ptr, - (int)opt_os.value.len, (const char*)opt_os.value.ptr, - (int)opt_abi.value.len, (const char*)opt_abi.value.ptr); - fprintf(stderr, "parsed from target: '%.*s'\n", - (int)opt_target.value.len, (const char*)opt_target.value.ptr); - fprintf(stderr, "parsed from line:\n%.*s\n", (int)opt_line.value.len, opt_line.value.ptr); - fprintf(stderr, "Zig installation appears to be corrupted.\n"); - exit(1); - } - - glibc_abi->version_table.put(target, ver_list_base); - } - continue; - } - for (size_t fn_i = 0; fn_i < glibc_abi->all_functions.length; fn_i += 1) { - ZigGLibCVerList *ver_list = &ver_list_base[fn_i]; - line_num += 1; - Optional> opt_line = SplitIterator_next_separate(&it); - assert(opt_line.is_some); - - SplitIterator line_it = memSplit(opt_line.value, str(" ")); - for (;;) { - Optional> opt_ver = SplitIterator_next(&line_it); - if (!opt_ver.is_some) break; - assert(ver_list->len < 8); // increase the array len in the type - - unsigned long ver_index = strtoul(buf_ptr(buf_create_from_slice(opt_ver.value)), nullptr, 10); - assert(ver_index < 255); // use a bigger integer in the type - ver_list->versions[ver_list->len] = ver_index; - ver_list->len += 1; - } - } - ver_list_base = nullptr; - } - } - - *out_result = glibc_abi; - return ErrorNone; -} - -Error glibc_build_dummies_and_maps(CodeGen *g, const ZigGLibCAbi *glibc_abi, const ZigTarget *target, - Buf **out_dir, bool verbose, Stage2ProgressNode *progress_node) -{ - Error err; - - Buf *cache_dir = get_global_cache_dir(); - CacheHash *cache_hash = heap::c_allocator.create(); - Buf *manifest_dir = buf_sprintf("%s" OS_SEP CACHE_HASH_SUBDIR, buf_ptr(cache_dir)); - cache_init(cache_hash, manifest_dir); - - Buf *compiler_id; - if ((err = get_compiler_id(&compiler_id))) { - if (verbose) { - fprintf(stderr, "unable to get compiler id: %s\n", err_str(err)); - } - return err; - } - cache_buf(cache_hash, compiler_id); - cache_int(cache_hash, target->arch); - cache_int(cache_hash, target->abi); - cache_int(cache_hash, target->glibc_or_darwin_version->major); - cache_int(cache_hash, target->glibc_or_darwin_version->minor); - cache_int(cache_hash, target->glibc_or_darwin_version->patch); - - Buf digest = BUF_INIT; - buf_resize(&digest, 0); - if ((err = cache_hit(cache_hash, &digest))) { - // Treat an invalid format error as a cache miss. - if (err != ErrorInvalidFormat) - return err; - } - // We should always get a cache hit because there are no - // files in the input hash. - assert(buf_len(&digest) != 0); - - Buf *dummy_dir = buf_alloc(); - os_path_join(manifest_dir, &digest, dummy_dir); - - if ((err = os_make_path(dummy_dir))) - return err; - - Buf *test_if_exists_path = buf_alloc(); - os_path_join(dummy_dir, buf_create_from_str("ok"), test_if_exists_path); - - bool hit; - if ((err = os_file_exists(test_if_exists_path, &hit))) - return err; - - if (hit) { - *out_dir = dummy_dir; - return ErrorNone; - } - - - ZigGLibCVerList *ver_list_base = glibc_abi->version_table.get(target); - - uint8_t target_ver_index = 0; - for (;target_ver_index < glibc_abi->all_versions.length; target_ver_index += 1) { - const Stage2SemVer *this_ver = &glibc_abi->all_versions.at(target_ver_index); - if (this_ver->major == target->glibc_or_darwin_version->major && - this_ver->minor == target->glibc_or_darwin_version->minor && - this_ver->patch == target->glibc_or_darwin_version->patch) - { - break; - } - } - if (target_ver_index == glibc_abi->all_versions.length) { - if (verbose) { - fprintf(stderr, "Unrecognized glibc version: %d.%d.%d\n", - target->glibc_or_darwin_version->major, - target->glibc_or_darwin_version->minor, - target->glibc_or_darwin_version->patch); - } - return ErrorUnknownABI; - } - - Buf *map_file_path = buf_sprintf("%s" OS_SEP "all.map", buf_ptr(dummy_dir)); - Buf *map_contents = buf_alloc(); - - for (uint8_t ver_i = 0; ver_i < glibc_abi->all_versions.length; ver_i += 1) { - const Stage2SemVer *ver = &glibc_abi->all_versions.at(ver_i); - if (ver->patch == 0) { - buf_appendf(map_contents, "GLIBC_%d.%d { };\n", ver->major, ver->minor); - } else { - buf_appendf(map_contents, "GLIBC_%d.%d.%d { };\n", ver->major, ver->minor, ver->patch); - } - } - - if ((err = os_write_file(map_file_path, map_contents))) { - if (verbose) { - fprintf(stderr, "unable to write %s: %s", buf_ptr(map_file_path), err_str(err)); - } - return err; - } - - - for (size_t lib_i = 0; lib_i < array_length(glibc_libs); lib_i += 1) { - const ZigGLibCLib *lib = &glibc_libs[lib_i]; - Buf *zig_file_path = buf_sprintf("%s" OS_SEP "%s.zig", buf_ptr(dummy_dir), lib->name); - Buf *zig_body = buf_alloc(); - Buf *zig_footer = buf_alloc(); - - buf_appendf(zig_body, "comptime {\n"); - buf_appendf(zig_body, " asm (\n"); - - for (size_t fn_i = 0; fn_i < glibc_abi->all_functions.length; fn_i += 1) { - const ZigGLibCFn *libc_fn = &glibc_abi->all_functions.at(fn_i); - if (libc_fn->lib != lib) continue; - ZigGLibCVerList *ver_list = &ver_list_base[fn_i]; - // Pick the default symbol version: - // - If there are no versions, don't emit it - // - Take the greatest one <= than the target one - // - If none of them is <= than the - // specified one don't pick any default version - if (ver_list->len == 0) continue; - uint8_t chosen_def_ver_index = 255; - for (uint8_t ver_i = 0; ver_i < ver_list->len; ver_i += 1) { - uint8_t ver_index = ver_list->versions[ver_i]; - if ((chosen_def_ver_index == 255 || ver_index > chosen_def_ver_index) && - target_ver_index >= ver_index) - { - chosen_def_ver_index = ver_index; - } - } - for (uint8_t ver_i = 0; ver_i < ver_list->len; ver_i += 1) { - uint8_t ver_index = ver_list->versions[ver_i]; - - Buf *stub_name; - const Stage2SemVer *ver = &glibc_abi->all_versions.at(ver_index); - const char *sym_name = buf_ptr(libc_fn->name); - if (ver->patch == 0) { - stub_name = buf_sprintf("%s_%d_%d", sym_name, ver->major, ver->minor); - } else { - stub_name = buf_sprintf("%s_%d_%d_%d", sym_name, ver->major, ver->minor, ver->patch); - } - - buf_appendf(zig_footer, "export fn %s() void {}\n", buf_ptr(stub_name)); - - // Default symbol version definition vs normal symbol version definition - const char *at_sign_str = (chosen_def_ver_index != 255 && - ver_index == chosen_def_ver_index) ? "@@" : "@"; - if (ver->patch == 0) { - buf_appendf(zig_body, " \\\\ .symver %s, %s%sGLIBC_%d.%d\n", - buf_ptr(stub_name), sym_name, at_sign_str, ver->major, ver->minor); - } else { - buf_appendf(zig_body, " \\\\ .symver %s, %s%sGLIBC_%d.%d.%d\n", - buf_ptr(stub_name), sym_name, at_sign_str, ver->major, ver->minor, ver->patch); - } - // Hide the stub to keep the symbol table clean - buf_appendf(zig_body, " \\\\ .hidden %s\n", buf_ptr(stub_name)); - } - } - - buf_appendf(zig_body, " );\n"); - buf_appendf(zig_body, "}\n"); - buf_append_buf(zig_body, zig_footer); - - if ((err = os_write_file(zig_file_path, zig_body))) { - if (verbose) { - fprintf(stderr, "unable to write %s: %s", buf_ptr(zig_file_path), err_str(err)); - } - return err; - } - - bool is_ld = (strcmp(lib->name, "ld") == 0); - - CodeGen *child_gen = create_child_codegen(g, zig_file_path, OutTypeLib, nullptr, lib->name, progress_node); - codegen_set_lib_version(child_gen, true, lib->sover, 0, 0); - child_gen->is_dynamic = true; - child_gen->is_dummy_so = true; - child_gen->version_script_path = map_file_path; - child_gen->enable_cache = false; - child_gen->output_dir = dummy_dir; - if (is_ld) { - assert(g->zig_target->standard_dynamic_linker_path != nullptr); - Buf *ld_basename = buf_alloc(); - os_path_split(buf_create_from_str(g->zig_target->standard_dynamic_linker_path), - nullptr, ld_basename); - child_gen->override_soname = ld_basename; - } - codegen_build_and_link(child_gen); - } - - if ((err = os_write_file(test_if_exists_path, buf_alloc()))) { - if (verbose) { - fprintf(stderr, "unable to write %s: %s", buf_ptr(test_if_exists_path), err_str(err)); - } - return err; - } - *out_dir = dummy_dir; - return ErrorNone; -} - -uint32_t hash_glibc_target(const ZigTarget *x) { - return x->arch * (uint32_t)3250106448 + - x->os * (uint32_t)542534372 + - x->abi * (uint32_t)59162639; -} - -bool eql_glibc_target(const ZigTarget *a, const ZigTarget *b) { - return a->arch == b->arch && - a->os == b->os && - a->abi == b->abi; -} - -size_t glibc_lib_count(void) { - return array_length(glibc_libs); -} - -const ZigGLibCLib *glibc_lib_enum(size_t index) { - assert(index < array_length(glibc_libs)); - return &glibc_libs[index]; -} - -const ZigGLibCLib *glibc_lib_find(const char *name) { - for (size_t i = 0; i < array_length(glibc_libs); i += 1) { - if (strcmp(glibc_libs[i].name, name) == 0) { - return &glibc_libs[i]; - } - } - return nullptr; -} diff --git a/src/glibc.hpp b/src/glibc.hpp deleted file mode 100644 index c04dcb46295bc4cd42df0a7220dc9d7d44632333..0000000000000000000000000000000000000000 --- a/src/glibc.hpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (c) 2019 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#ifndef ZIG_GLIBC_HPP -#define ZIG_GLIBC_HPP - -#include "all_types.hpp" - -struct ZigGLibCLib { - const char *name; - uint8_t sover; -}; - -struct ZigGLibCFn { - Buf *name; - const ZigGLibCLib *lib; -}; - -struct ZigGLibCVerList { - uint8_t versions[8]; // 8 is just the max number, we know statically it's big enough - uint8_t len; -}; - -uint32_t hash_glibc_target(const ZigTarget *x); -bool eql_glibc_target(const ZigTarget *a, const ZigTarget *b); - -struct ZigGLibCAbi { - Buf *abi_txt_path; - Buf *vers_txt_path; - Buf *fns_txt_path; - ZigList all_versions; - ZigList all_functions; - // The value is a pointer to all_functions.length items and each item is an index - // into all_functions. - HashMap version_table; -}; - -Error glibc_load_metadata(ZigGLibCAbi **out_result, Buf *zig_lib_dir, bool verbose); -Error glibc_build_dummies_and_maps(CodeGen *codegen, const ZigGLibCAbi *glibc_abi, const ZigTarget *target, - Buf **out_dir, bool verbose, Stage2ProgressNode *progress_node); - -size_t glibc_lib_count(void); -const ZigGLibCLib *glibc_lib_enum(size_t index); -const ZigGLibCLib *glibc_lib_find(const char *name); - -#endif diff --git a/src/glibc.zig b/src/glibc.zig new file mode 100644 index 0000000000000000000000000000000000000000..1860726f93eb263e7d5d31d435bbd68794c53b71 --- /dev/null +++ b/src/glibc.zig @@ -0,0 +1,956 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; +const mem = std.mem; +const path = std.fs.path; +const assert = std.debug.assert; + +const target_util = @import("target.zig"); +const Compilation = @import("Compilation.zig"); +const build_options = @import("build_options"); +const trace = @import("tracy.zig").trace; +const Cache = @import("Cache.zig"); +const Package = @import("Package.zig"); + +pub const Lib = struct { + name: []const u8, + sover: u8, +}; + +pub const Fn = struct { + name: []const u8, + lib: *const Lib, +}; + +pub const VerList = struct { + /// 7 is just the max number, we know statically it's big enough. + versions: [7]u8, + len: u8, +}; + +pub const ABI = struct { + all_versions: []const std.builtin.Version, + all_functions: []const Fn, + /// The value is a pointer to all_functions.len items and each item is an index into all_functions. + version_table: std.AutoHashMapUnmanaged(target_util.ArchOsAbi, [*]VerList), + arena_state: std.heap.ArenaAllocator.State, + + pub fn destroy(abi: *ABI, gpa: *Allocator) void { + abi.version_table.deinit(gpa); + abi.arena_state.promote(gpa).deinit(); // Frees the ABI memory too. + } +}; + +pub const libs = [_]Lib{ + .{ .name = "c", .sover = 6 }, + .{ .name = "m", .sover = 6 }, + .{ .name = "pthread", .sover = 0 }, + .{ .name = "dl", .sover = 2 }, + .{ .name = "rt", .sover = 1 }, + .{ .name = "ld", .sover = 2 }, + .{ .name = "util", .sover = 1 }, +}; + +pub const LoadMetaDataError = error{ + /// The files that ship with the Zig compiler were unable to be read, or otherwise had malformed data. + ZigInstallationCorrupt, + OutOfMemory, +}; + +/// This function will emit a log error when there is a problem with the zig installation and then return +/// `error.ZigInstallationCorrupt`. +pub fn loadMetaData(gpa: *Allocator, zig_lib_dir: std.fs.Dir) LoadMetaDataError!*ABI { + const tracy = trace(@src()); + defer tracy.end(); + + var arena_allocator = std.heap.ArenaAllocator.init(gpa); + errdefer arena_allocator.deinit(); + const arena = &arena_allocator.allocator; + + var all_versions = std.ArrayListUnmanaged(std.builtin.Version){}; + var all_functions = std.ArrayListUnmanaged(Fn){}; + var version_table = std.AutoHashMapUnmanaged(target_util.ArchOsAbi, [*]VerList){}; + errdefer version_table.deinit(gpa); + + var glibc_dir = zig_lib_dir.openDir("libc" ++ path.sep_str ++ "glibc", .{}) catch |err| { + std.log.err("unable to open glibc dir: {}", .{@errorName(err)}); + return error.ZigInstallationCorrupt; + }; + defer glibc_dir.close(); + + const max_txt_size = 500 * 1024; // Bigger than this and something is definitely borked. + const vers_txt_contents = glibc_dir.readFileAlloc(gpa, "vers.txt", max_txt_size) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + else => { + std.log.err("unable to read vers.txt: {}", .{@errorName(err)}); + return error.ZigInstallationCorrupt; + }, + }; + defer gpa.free(vers_txt_contents); + + // Arena allocated because the result contains references to function names. + const fns_txt_contents = glibc_dir.readFileAlloc(arena, "fns.txt", max_txt_size) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + else => { + std.log.err("unable to read fns.txt: {}", .{@errorName(err)}); + return error.ZigInstallationCorrupt; + }, + }; + + const abi_txt_contents = glibc_dir.readFileAlloc(gpa, "abi.txt", max_txt_size) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + else => { + std.log.err("unable to read abi.txt: {}", .{@errorName(err)}); + return error.ZigInstallationCorrupt; + }, + }; + defer gpa.free(abi_txt_contents); + + { + var it = mem.tokenize(vers_txt_contents, "\r\n"); + var line_i: usize = 1; + while (it.next()) |line| : (line_i += 1) { + const prefix = "GLIBC_"; + if (!mem.startsWith(u8, line, prefix)) { + std.log.err("vers.txt:{}: expected 'GLIBC_' prefix", .{line_i}); + return error.ZigInstallationCorrupt; + } + const adjusted_line = line[prefix.len..]; + const ver = std.builtin.Version.parse(adjusted_line) catch |err| { + std.log.err("vers.txt:{}: unable to parse glibc version '{}': {}", .{ line_i, line, @errorName(err) }); + return error.ZigInstallationCorrupt; + }; + try all_versions.append(arena, ver); + } + } + { + var file_it = mem.tokenize(fns_txt_contents, "\r\n"); + var line_i: usize = 1; + while (file_it.next()) |line| : (line_i += 1) { + var line_it = mem.tokenize(line, " "); + const fn_name = line_it.next() orelse { + std.log.err("fns.txt:{}: expected function name", .{line_i}); + return error.ZigInstallationCorrupt; + }; + const lib_name = line_it.next() orelse { + std.log.err("fns.txt:{}: expected library name", .{line_i}); + return error.ZigInstallationCorrupt; + }; + const lib = findLib(lib_name) orelse { + std.log.err("fns.txt:{}: unknown library name: {}", .{ line_i, lib_name }); + return error.ZigInstallationCorrupt; + }; + try all_functions.append(arena, .{ + .name = fn_name, + .lib = lib, + }); + } + } + { + var file_it = mem.split(abi_txt_contents, "\n"); + var line_i: usize = 0; + while (true) { + const ver_list_base: []VerList = blk: { + const line = file_it.next() orelse break; + if (line.len == 0) break; + line_i += 1; + const ver_list_base = try arena.alloc(VerList, all_functions.items.len); + var line_it = mem.tokenize(line, " "); + while (line_it.next()) |target_string| { + var component_it = mem.tokenize(target_string, "-"); + const arch_name = component_it.next() orelse { + std.log.err("abi.txt:{}: expected arch name", .{line_i}); + return error.ZigInstallationCorrupt; + }; + const os_name = component_it.next() orelse { + std.log.err("abi.txt:{}: expected OS name", .{line_i}); + return error.ZigInstallationCorrupt; + }; + const abi_name = component_it.next() orelse { + std.log.err("abi.txt:{}: expected ABI name", .{line_i}); + return error.ZigInstallationCorrupt; + }; + const arch_tag = std.meta.stringToEnum(std.Target.Cpu.Arch, arch_name) orelse { + std.log.err("abi.txt:{}: unrecognized arch: '{}'", .{ line_i, arch_name }); + return error.ZigInstallationCorrupt; + }; + if (!mem.eql(u8, os_name, "linux")) { + std.log.err("abi.txt:{}: expected OS 'linux', found '{}'", .{ line_i, os_name }); + return error.ZigInstallationCorrupt; + } + const abi_tag = std.meta.stringToEnum(std.Target.Abi, abi_name) orelse { + std.log.err("abi.txt:{}: unrecognized ABI: '{}'", .{ line_i, abi_name }); + return error.ZigInstallationCorrupt; + }; + + const triple = target_util.ArchOsAbi{ + .arch = arch_tag, + .os = .linux, + .abi = abi_tag, + }; + try version_table.put(gpa, triple, ver_list_base.ptr); + } + break :blk ver_list_base; + }; + for (ver_list_base) |*ver_list| { + const line = file_it.next() orelse { + std.log.err("abi.txt:{}: missing version number line", .{line_i}); + return error.ZigInstallationCorrupt; + }; + line_i += 1; + + ver_list.* = .{ + .versions = undefined, + .len = 0, + }; + var line_it = mem.tokenize(line, " "); + while (line_it.next()) |version_index_string| { + if (ver_list.len >= ver_list.versions.len) { + // If this happens with legit data, increase the array len in the type. + std.log.err("abi.txt:{}: too many versions", .{line_i}); + return error.ZigInstallationCorrupt; + } + const version_index = std.fmt.parseInt(u8, version_index_string, 10) catch |err| { + // If this happens with legit data, increase the size of the integer type in the struct. + std.log.err("abi.txt:{}: unable to parse version: {}", .{ line_i, @errorName(err) }); + return error.ZigInstallationCorrupt; + }; + + ver_list.versions[ver_list.len] = version_index; + ver_list.len += 1; + } + } + } + } + + const abi = try arena.create(ABI); + abi.* = .{ + .all_versions = all_versions.items, + .all_functions = all_functions.items, + .version_table = version_table, + .arena_state = arena_allocator.state, + }; + return abi; +} + +fn findLib(name: []const u8) ?*const Lib { + for (libs) |*lib| { + if (mem.eql(u8, lib.name, name)) { + return lib; + } + } + return null; +} + +pub const CRTFile = enum { + crti_o, + crtn_o, + scrt1_o, + libc_nonshared_a, +}; + +pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { + if (!build_options.have_llvm) { + return error.ZigCompilerNotBuiltWithLLVMExtensions; + } + const gpa = comp.gpa; + var arena_allocator = std.heap.ArenaAllocator.init(gpa); + defer arena_allocator.deinit(); + const arena = &arena_allocator.allocator; + + switch (crt_file) { + .crti_o => { + var args = std.ArrayList([]const u8).init(arena); + try add_include_dirs(comp, arena, &args); + try args.appendSlice(&[_][]const u8{ + "-D_LIBC_REENTRANT", + "-include", + try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-modules.h"), + "-DMODULE_NAME=libc", + "-Wno-nonportable-include-path", + "-include", + try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-symbols.h"), + "-DTOP_NAMESPACE=glibc", + "-DASSEMBLER", + "-g", + "-Wa,--noexecstack", + }); + return comp.build_crt_file("crti", .Obj, &[1]Compilation.CSourceFile{ + .{ + .src_path = try start_asm_path(comp, arena, "crti.S"), + .extra_flags = args.items, + }, + }); + }, + .crtn_o => { + var args = std.ArrayList([]const u8).init(arena); + try add_include_dirs(comp, arena, &args); + try args.appendSlice(&[_][]const u8{ + "-D_LIBC_REENTRANT", + "-DMODULE_NAME=libc", + "-DTOP_NAMESPACE=glibc", + "-DASSEMBLER", + "-g", + "-Wa,--noexecstack", + }); + return comp.build_crt_file("crtn", .Obj, &[1]Compilation.CSourceFile{ + .{ + .src_path = try start_asm_path(comp, arena, "crtn.S"), + .extra_flags = args.items, + }, + }); + }, + .scrt1_o => { + const start_os: Compilation.CSourceFile = blk: { + var args = std.ArrayList([]const u8).init(arena); + try add_include_dirs(comp, arena, &args); + try args.appendSlice(&[_][]const u8{ + "-D_LIBC_REENTRANT", + "-include", + try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-modules.h"), + "-DMODULE_NAME=libc", + "-Wno-nonportable-include-path", + "-include", + try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-symbols.h"), + "-DPIC", + "-DSHARED", + "-DTOP_NAMESPACE=glibc", + "-DASSEMBLER", + "-g", + "-Wa,--noexecstack", + }); + break :blk .{ + .src_path = try start_asm_path(comp, arena, "start.S"), + .extra_flags = args.items, + }; + }; + const abi_note_o: Compilation.CSourceFile = blk: { + var args = std.ArrayList([]const u8).init(arena); + try args.appendSlice(&[_][]const u8{ + "-I", + try lib_path(comp, arena, lib_libc_glibc ++ "csu"), + }); + try add_include_dirs(comp, arena, &args); + try args.appendSlice(&[_][]const u8{ + "-D_LIBC_REENTRANT", + "-DMODULE_NAME=libc", + "-DTOP_NAMESPACE=glibc", + "-DASSEMBLER", + "-g", + "-Wa,--noexecstack", + }); + break :blk .{ + .src_path = try lib_path(comp, arena, lib_libc_glibc ++ "csu" ++ path.sep_str ++ "abi-note.S"), + .extra_flags = args.items, + }; + }; + return comp.build_crt_file("Scrt1", .Obj, &[_]Compilation.CSourceFile{ start_os, abi_note_o }); + }, + .libc_nonshared_a => { + const deps = [_][]const u8{ + lib_libc_glibc ++ "stdlib" ++ path.sep_str ++ "atexit.c", + lib_libc_glibc ++ "stdlib" ++ path.sep_str ++ "at_quick_exit.c", + lib_libc_glibc ++ "io" ++ path.sep_str ++ "stat.c", + lib_libc_glibc ++ "io" ++ path.sep_str ++ "fstat.c", + lib_libc_glibc ++ "io" ++ path.sep_str ++ "lstat.c", + lib_libc_glibc ++ "io" ++ path.sep_str ++ "stat64.c", + lib_libc_glibc ++ "io" ++ path.sep_str ++ "fstat64.c", + lib_libc_glibc ++ "io" ++ path.sep_str ++ "lstat64.c", + lib_libc_glibc ++ "io" ++ path.sep_str ++ "fstatat.c", + lib_libc_glibc ++ "io" ++ path.sep_str ++ "fstatat64.c", + lib_libc_glibc ++ "io" ++ path.sep_str ++ "mknod.c", + lib_libc_glibc ++ "io" ++ path.sep_str ++ "mknodat.c", + lib_libc_glibc ++ "nptl" ++ path.sep_str ++ "pthread_atfork.c", + lib_libc_glibc ++ "debug" ++ path.sep_str ++ "stack_chk_fail_local.c", + }; + + var c_source_files: [deps.len + 1]Compilation.CSourceFile = undefined; + + c_source_files[0] = blk: { + var args = std.ArrayList([]const u8).init(arena); + try args.appendSlice(&[_][]const u8{ + "-std=gnu11", + "-fgnu89-inline", + "-g", + "-O2", + "-fmerge-all-constants", + "-fno-stack-protector", + "-fmath-errno", + "-fno-stack-protector", + "-I", + try lib_path(comp, arena, lib_libc_glibc ++ "csu"), + }); + try add_include_dirs(comp, arena, &args); + try args.appendSlice(&[_][]const u8{ + "-DSTACK_PROTECTOR_LEVEL=0", + "-fPIC", + "-fno-stack-protector", + "-ftls-model=initial-exec", + "-D_LIBC_REENTRANT", + "-include", + try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-modules.h"), + "-DMODULE_NAME=libc", + "-Wno-nonportable-include-path", + "-include", + try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-symbols.h"), + "-DPIC", + "-DLIBC_NONSHARED=1", + "-DTOP_NAMESPACE=glibc", + }); + break :blk .{ + .src_path = try lib_path(comp, arena, lib_libc_glibc ++ "csu" ++ path.sep_str ++ "elf-init.c"), + .extra_flags = args.items, + }; + }; + + for (deps) |dep, i| { + var args = std.ArrayList([]const u8).init(arena); + try args.appendSlice(&[_][]const u8{ + "-std=gnu11", + "-fgnu89-inline", + "-g", + "-O2", + "-fmerge-all-constants", + "-fno-stack-protector", + "-fmath-errno", + "-ftls-model=initial-exec", + "-Wno-ignored-attributes", + }); + try add_include_dirs(comp, arena, &args); + try args.appendSlice(&[_][]const u8{ + "-D_LIBC_REENTRANT", + "-include", + try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-modules.h"), + "-DMODULE_NAME=libc", + "-Wno-nonportable-include-path", + "-include", + try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-symbols.h"), + "-DPIC", + "-DLIBC_NONSHARED=1", + "-DTOP_NAMESPACE=glibc", + }); + c_source_files[i + 1] = .{ + .src_path = try lib_path(comp, arena, dep), + .extra_flags = args.items, + }; + } + return comp.build_crt_file("c_nonshared", .Lib, &c_source_files); + }, + } +} + +fn start_asm_path(comp: *Compilation, arena: *Allocator, basename: []const u8) ![]const u8 { + const arch = comp.getTarget().cpu.arch; + const is_ppc = arch == .powerpc or arch == .powerpc64 or arch == .powerpc64le; + const is_aarch64 = arch == .aarch64 or arch == .aarch64_be; + const is_sparc = arch == .sparc or arch == .sparcel or arch == .sparcv9; + const is_64 = arch.ptrBitWidth() == 64; + + const s = path.sep_str; + + var result = std.ArrayList(u8).init(arena); + try result.appendSlice(comp.zig_lib_directory.path.?); + try result.appendSlice(s ++ "libc" ++ s ++ "glibc" ++ s ++ "sysdeps" ++ s); + if (is_sparc) { + if (is_64) { + try result.appendSlice("sparc" ++ s ++ "sparc64"); + } else { + try result.appendSlice("sparc" ++ s ++ "sparc32"); + } + } else if (arch.isARM()) { + try result.appendSlice("arm"); + } else if (arch.isMIPS()) { + try result.appendSlice("mips"); + } else if (arch == .x86_64) { + try result.appendSlice("x86_64"); + } else if (arch == .i386) { + try result.appendSlice("i386"); + } else if (is_aarch64) { + try result.appendSlice("aarch64"); + } else if (arch.isRISCV()) { + try result.appendSlice("riscv"); + } else if (is_ppc) { + if (is_64) { + try result.appendSlice("powerpc" ++ s ++ "powerpc64"); + } else { + try result.appendSlice("powerpc" ++ s ++ "powerpc32"); + } + } + + try result.appendSlice(s); + try result.appendSlice(basename); + return result.items; +} + +fn add_include_dirs(comp: *Compilation, arena: *Allocator, args: *std.ArrayList([]const u8)) error{OutOfMemory}!void { + const target = comp.getTarget(); + const arch = target.cpu.arch; + const opt_nptl: ?[]const u8 = if (target.os.tag == .linux) "nptl" else "htl"; + const glibc = try lib_path(comp, arena, lib_libc ++ "glibc"); + + const s = path.sep_str; + + try args.append("-I"); + try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "include")); + + if (target.os.tag == .linux) { + try add_include_dirs_arch(arena, args, arch, null, try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "unix" ++ s ++ "sysv" ++ s ++ "linux")); + } + + if (opt_nptl) |nptl| { + try add_include_dirs_arch(arena, args, arch, nptl, try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps")); + } + + if (target.os.tag == .linux) { + try args.append("-I"); + try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ + "unix" ++ s ++ "sysv" ++ s ++ "linux" ++ s ++ "generic")); + + try args.append("-I"); + try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ + "unix" ++ s ++ "sysv" ++ s ++ "linux" ++ s ++ "include")); + try args.append("-I"); + try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ + "unix" ++ s ++ "sysv" ++ s ++ "linux")); + } + if (opt_nptl) |nptl| { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, lib_libc_glibc ++ "sysdeps", nptl })); + } + + try args.append("-I"); + try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "pthread")); + + try args.append("-I"); + try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "unix" ++ s ++ "sysv")); + + try add_include_dirs_arch(arena, args, arch, null, try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "unix")); + + try args.append("-I"); + try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "unix")); + + try add_include_dirs_arch(arena, args, arch, null, try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps")); + + try args.append("-I"); + try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "generic")); + + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, lib_libc ++ "glibc" })); + + try args.append("-I"); + try args.append(try std.fmt.allocPrint(arena, "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{}-{}-{}", .{ + comp.zig_lib_directory.path.?, @tagName(arch), @tagName(target.os.tag), @tagName(target.abi), + })); + + try args.append("-I"); + try args.append(try lib_path(comp, arena, lib_libc ++ "include" ++ s ++ "generic-glibc")); + + try args.append("-I"); + try args.append(try std.fmt.allocPrint(arena, "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{}-linux-any", .{ + comp.zig_lib_directory.path.?, @tagName(arch), + })); + + try args.append("-I"); + try args.append(try lib_path(comp, arena, lib_libc ++ "include" ++ s ++ "any-linux-any")); +} + +fn add_include_dirs_arch( + arena: *Allocator, + args: *std.ArrayList([]const u8), + arch: std.Target.Cpu.Arch, + opt_nptl: ?[]const u8, + dir: []const u8, +) error{OutOfMemory}!void { + const is_x86 = arch == .i386 or arch == .x86_64; + const is_aarch64 = arch == .aarch64 or arch == .aarch64_be; + const is_ppc = arch == .powerpc or arch == .powerpc64 or arch == .powerpc64le; + const is_sparc = arch == .sparc or arch == .sparcel or arch == .sparcv9; + const is_64 = arch.ptrBitWidth() == 64; + + const s = path.sep_str; + + if (is_x86) { + if (arch == .x86_64) { + if (opt_nptl) |nptl| { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "x86_64", nptl })); + } else { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "x86_64" })); + } + } else if (arch == .i386) { + if (opt_nptl) |nptl| { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "i386", nptl })); + } else { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "i386" })); + } + } + if (opt_nptl) |nptl| { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "x86", nptl })); + } else { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "x86" })); + } + } else if (arch.isARM()) { + if (opt_nptl) |nptl| { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "arm", nptl })); + } else { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "arm" })); + } + } else if (arch.isMIPS()) { + if (opt_nptl) |nptl| { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "mips", nptl })); + } else { + if (is_64) { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "mips" ++ s ++ "mips64" })); + } else { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "mips" ++ s ++ "mips32" })); + } + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "mips" })); + } + } else if (is_sparc) { + if (opt_nptl) |nptl| { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "sparc", nptl })); + } else { + if (is_64) { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "sparc" ++ s ++ "sparc64" })); + } else { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "sparc" ++ s ++ "sparc32" })); + } + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "sparc" })); + } + } else if (is_aarch64) { + if (opt_nptl) |nptl| { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "aarch64", nptl })); + } else { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "aarch64" })); + } + } else if (is_ppc) { + if (opt_nptl) |nptl| { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "powerpc", nptl })); + } else { + if (is_64) { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "powerpc" ++ s ++ "powerpc64" })); + } else { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "powerpc" ++ s ++ "powerpc32" })); + } + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "powerpc" })); + } + } else if (arch.isRISCV()) { + if (opt_nptl) |nptl| { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "riscv", nptl })); + } else { + try args.append("-I"); + try args.append(try path.join(arena, &[_][]const u8{ dir, "riscv" })); + } + } +} + +fn path_from_lib(comp: *Compilation, arena: *Allocator, sub_path: []const u8) ![]const u8 { + return path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, sub_path }); +} + +const lib_libc = "libc" ++ path.sep_str; +const lib_libc_glibc = lib_libc ++ "glibc" ++ path.sep_str; + +fn lib_path(comp: *Compilation, arena: *Allocator, sub_path: []const u8) ![]const u8 { + return path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, sub_path }); +} + +pub const BuiltSharedObjects = struct { + lock: Cache.Lock, + dir_path: []u8, + + pub fn deinit(self: *BuiltSharedObjects, gpa: *Allocator) void { + self.lock.release(); + gpa.free(self.dir_path); + self.* = undefined; + } +}; + +const all_map_basename = "all.map"; + +// TODO Turn back on zig fmt when https://github.com/ziglang/zig/issues/5948 is implemented. +// zig fmt: off + +pub fn buildSharedObjects(comp: *Compilation) !void { + const tracy = trace(@src()); + defer tracy.end(); + + if (!build_options.have_llvm) { + return error.ZigCompilerNotBuiltWithLLVMExtensions; + } + + var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); + defer arena_allocator.deinit(); + const arena = &arena_allocator.allocator; + + const target = comp.getTarget(); + const target_version = target.os.version_range.linux.glibc; + + // Use the global cache directory. + var cache_parent: Cache = .{ + .gpa = comp.gpa, + .manifest_dir = try comp.global_cache_directory.handle.makeOpenPath("h", .{}), + }; + defer cache_parent.manifest_dir.close(); + + var cache = cache_parent.obtain(); + defer cache.deinit(); + cache.hash.addBytes(build_options.version); + cache.hash.addBytes(comp.zig_lib_directory.path orelse "."); + cache.hash.add(target.cpu.arch); + cache.hash.add(target.abi); + cache.hash.add(target_version); + + const hit = try cache.hit(); + const digest = cache.final(); + const o_sub_path = try path.join(arena, &[_][]const u8{ "o", &digest }); + + // Even if we get a hit, it doesn't guarantee that we finished the job last time. + // We use the presence of an "ok" file to determine if it is a true hit. + + var o_directory: Compilation.Directory = .{ + .handle = try comp.global_cache_directory.handle.makeOpenPath(o_sub_path, .{}), + .path = try path.join(arena, &[_][]const u8{ comp.global_cache_directory.path.?, o_sub_path }), + }; + defer o_directory.handle.close(); + + const ok_basename = "ok"; + const actual_hit = if (hit) blk: { + o_directory.handle.access(ok_basename, .{}) catch |err| switch (err) { + error.FileNotFound => break :blk false, + else => |e| return e, + }; + break :blk true; + } else false; + + if (!actual_hit) { + const metadata = try loadMetaData(comp.gpa, comp.zig_lib_directory.handle); + defer metadata.destroy(comp.gpa); + + const ver_list_base = metadata.version_table.get(.{ + .arch = target.cpu.arch, + .os = target.os.tag, + .abi = target.abi, + }) orelse return error.GLibCUnavailableForThisTarget; + const target_ver_index = for (metadata.all_versions) |ver, i| { + switch (ver.order(target_version)) { + .eq => break i, + .lt => continue, + .gt => { + // TODO Expose via compile error mechanism instead of log. + std.log.warn("invalid target glibc version: {}", .{target_version}); + return error.InvalidTargetGLibCVersion; + }, + } + } else blk: { + const latest_index = metadata.all_versions.len - 1; + std.log.warn("zig cannot build new glibc version {}; providing instead {}", .{ + target_version, metadata.all_versions[latest_index], + }); + break :blk latest_index; + }; + { + var map_contents = std.ArrayList(u8).init(arena); + for (metadata.all_versions) |ver| { + if (ver.patch == 0) { + try map_contents.writer().print("GLIBC_{d}.{d} {{ }};\n", .{ ver.major, ver.minor }); + } else { + try map_contents.writer().print("GLIBC_{d}.{d}.{d} {{ }};\n", .{ ver.major, ver.minor, ver.patch }); + } + } + try o_directory.handle.writeFile(all_map_basename, map_contents.items); + map_contents.deinit(); // The most recent allocation of an arena can be freed :) + } + var zig_body = std.ArrayList(u8).init(comp.gpa); + defer zig_body.deinit(); + for (libs) |*lib| { + zig_body.shrinkRetainingCapacity(0); + + for (metadata.all_functions) |*libc_fn, fn_i| { + if (libc_fn.lib != lib) continue; + + const ver_list = ver_list_base[fn_i]; + // Pick the default symbol version: + // - If there are no versions, don't emit it + // - Take the greatest one <= than the target one + // - If none of them is <= than the + // specified one don't pick any default version + if (ver_list.len == 0) continue; + var chosen_def_ver_index: u8 = 255; + { + var ver_i: u8 = 0; + while (ver_i < ver_list.len) : (ver_i += 1) { + const ver_index = ver_list.versions[ver_i]; + if ((chosen_def_ver_index == 255 or ver_index > chosen_def_ver_index) and + target_ver_index >= ver_index) + { + chosen_def_ver_index = ver_index; + } + } + } + { + var ver_i: u8 = 0; + while (ver_i < ver_list.len) : (ver_i += 1) { + // Example: + // .globl _Exit_2_2_5 + // .type _Exit_2_2_5, @function; + // .symver _Exit_2_2_5, _Exit@@GLIBC_2.2.5 + // .hidden _Exit_2_2_5 + // _Exit_2_2_5: + const ver_index = ver_list.versions[ver_i]; + const ver = metadata.all_versions[ver_index]; + const sym_name = libc_fn.name; + // Default symbol version definition vs normal symbol version definition + const want_two_ats = chosen_def_ver_index != 255 and ver_index == chosen_def_ver_index; + const at_sign_str = "@@"[0 .. @boolToInt(want_two_ats) + @as(usize, 1)]; + + if (ver.patch == 0) { + const sym_plus_ver = try std.fmt.allocPrint( + arena, "{s}_{d}_{d}", + .{sym_name, ver.major, ver.minor}, + ); + try zig_body.writer().print( + \\.globl {s} + \\.type {s}, @function; + \\.symver {s}, {s}{s}GLIBC_{d}.{d} + \\.hidden {s} + \\{s}: + \\ + , .{ + sym_plus_ver, + sym_plus_ver, + sym_plus_ver, sym_name, at_sign_str, ver.major, ver.minor, + sym_plus_ver, + sym_plus_ver, + }); + } else { + const sym_plus_ver = try std.fmt.allocPrint(arena, "{s}_{d}_{d}_{d}", + .{sym_name, ver.major, ver.minor, ver.patch}, + ); + try zig_body.writer().print( + \\.globl {s} + \\.type {s}, @function; + \\.symver {s}, {s}{s}GLIBC_{d}.{d}.{d} + \\.hidden {s} + \\{s}: + \\ + , .{ + sym_plus_ver, + sym_plus_ver, + sym_plus_ver, sym_name, at_sign_str, ver.major, ver.minor, ver.patch, + sym_plus_ver, + sym_plus_ver, + }); + } + } + } + } + + var lib_name_buf: [32]u8 = undefined; // Larger than each of the names "c", "pthread", etc. + const asm_file_basename = std.fmt.bufPrint(&lib_name_buf, "{s}.s", .{lib.name}) catch unreachable; + try o_directory.handle.writeFile(asm_file_basename, zig_body.items); + + try buildSharedLib(comp, arena, comp.global_cache_directory, o_directory, asm_file_basename, lib); + } + // No need to write the manifest because there are no file inputs associated with this cache hash. + // However we do need to write the ok file now. + if (o_directory.handle.createFile(ok_basename, .{})) |file| { + file.close(); + } else |err| { + std.log.warn("glibc shared objects: failed to mark completion: {}", .{@errorName(err)}); + } + } + + assert(comp.glibc_so_files == null); + comp.glibc_so_files = BuiltSharedObjects{ + .lock = cache.toOwnedLock(), + .dir_path = try path.join(comp.gpa, &[_][]const u8{ comp.global_cache_directory.path.?, o_sub_path }), + }; +} + +// zig fmt: on + +fn buildSharedLib( + comp: *Compilation, + arena: *Allocator, + zig_cache_directory: Compilation.Directory, + bin_directory: Compilation.Directory, + asm_file_basename: []const u8, + lib: *const Lib, +) !void { + const tracy = trace(@src()); + defer tracy.end(); + + const emit_bin = Compilation.EmitLoc{ + .directory = bin_directory, + .basename = try std.fmt.allocPrint(arena, "lib{s}.so.{d}", .{ lib.name, lib.sover }), + }; + const version: std.builtin.Version = .{ .major = lib.sover, .minor = 0, .patch = 0 }; + const ld_basename = path.basename(comp.getTarget().standardDynamicLinkerPath().get().?); + const override_soname = if (mem.eql(u8, lib.name, "ld")) ld_basename else null; + const map_file_path = try path.join(arena, &[_][]const u8{ bin_directory.path.?, all_map_basename }); + const c_source_files = [1]Compilation.CSourceFile{ + .{ + .src_path = try path.join(arena, &[_][]const u8{ bin_directory.path.?, asm_file_basename }), + }, + }; + const sub_compilation = try Compilation.create(comp.gpa, .{ + .local_cache_directory = zig_cache_directory, + .global_cache_directory = comp.global_cache_directory, + .zig_lib_directory = comp.zig_lib_directory, + .target = comp.getTarget(), + .root_name = lib.name, + .root_pkg = null, + .output_mode = .Lib, + .link_mode = .Dynamic, + .rand = comp.rand, + .libc_installation = comp.bin_file.options.libc_installation, + .emit_bin = emit_bin, + .optimize_mode = comp.bin_file.options.optimize_mode, + .want_sanitize_c = false, + .want_stack_check = false, + .want_valgrind = false, + .emit_h = null, + .strip = comp.bin_file.options.strip, + .is_native_os = false, + .self_exe_path = comp.self_exe_path, + .verbose_cc = comp.verbose_cc, + .verbose_link = comp.bin_file.options.verbose_link, + .verbose_tokenize = comp.verbose_tokenize, + .verbose_ast = comp.verbose_ast, + .verbose_ir = comp.verbose_ir, + .verbose_llvm_ir = comp.verbose_llvm_ir, + .verbose_cimport = comp.verbose_cimport, + .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, + .clang_passthrough_mode = comp.clang_passthrough_mode, + .version = version, + .version_script = map_file_path, + .override_soname = override_soname, + .c_source_files = &c_source_files, + .is_compiler_rt_or_libc = true, + }); + defer sub_compilation.destroy(); + + try sub_compilation.updateSubCompilation(); +} diff --git a/src/hash_map.hpp b/src/hash_map.hpp deleted file mode 100644 index 8681e5b7613453b1d1b3446e363d257581e8dc4b..0000000000000000000000000000000000000000 --- a/src/hash_map.hpp +++ /dev/null @@ -1,420 +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_HASH_MAP_HPP -#define ZIG_HASH_MAP_HPP - -#include "util.hpp" - -#include - -template -class HashMap { -public: - void init(int capacity) { - init_capacity(capacity); - } - void deinit(void) { - _entries.deinit(); - heap::c_allocator.deallocate(_index_bytes, - _indexes_len * capacity_index_size(_indexes_len)); - } - - struct Entry { - uint32_t hash; - uint32_t distance_from_start_index; - K key; - V value; - }; - - void clear() { - _entries.clear(); - memset(_index_bytes, 0, _indexes_len * capacity_index_size(_indexes_len)); - _max_distance_from_start_index = 0; - _modification_count += 1; - } - - size_t size() const { - return _entries.length; - } - - void put(const K &key, const V &value) { - _modification_count += 1; - - // This allows us to take a pointer to an entry in `internal_put` which - // will not become a dead pointer when the array list is appended. - _entries.ensure_capacity(_entries.length + 1); - - if (_index_bytes == nullptr) { - if (_entries.length < 16) { - _entries.append({HashFunction(key), 0, key, value}); - return; - } else { - _indexes_len = 32; - _index_bytes = heap::c_allocator.allocate(_indexes_len); - _max_distance_from_start_index = 0; - for (size_t i = 0; i < _entries.length; i += 1) { - Entry *entry = &_entries.items[i]; - put_index(entry, i, _index_bytes); - } - return internal_put(key, value, _index_bytes); - } - } - - // if we would get too full (60%), double the indexes size - if ((_entries.length + 1) * 5 >= _indexes_len * 3) { - heap::c_allocator.deallocate(_index_bytes, - _indexes_len * capacity_index_size(_indexes_len)); - _indexes_len *= 2; - size_t sz = capacity_index_size(_indexes_len); - // This zero initializes the bytes, setting them all empty. - _index_bytes = heap::c_allocator.allocate(_indexes_len * sz); - _max_distance_from_start_index = 0; - for (size_t i = 0; i < _entries.length; i += 1) { - Entry *entry = &_entries.items[i]; - switch (sz) { - case 1: - put_index(entry, i, (uint8_t*)_index_bytes); - continue; - case 2: - put_index(entry, i, (uint16_t*)_index_bytes); - continue; - case 4: - put_index(entry, i, (uint32_t*)_index_bytes); - continue; - default: - put_index(entry, i, (size_t*)_index_bytes); - continue; - } - } - } - - switch (capacity_index_size(_indexes_len)) { - case 1: return internal_put(key, value, (uint8_t*)_index_bytes); - case 2: return internal_put(key, value, (uint16_t*)_index_bytes); - case 4: return internal_put(key, value, (uint32_t*)_index_bytes); - default: return internal_put(key, value, (size_t*)_index_bytes); - } - } - - Entry *put_unique(const K &key, const V &value) { - // TODO make this more efficient - Entry *entry = internal_get(key); - if (entry) - return entry; - put(key, value); - return nullptr; - } - - const V &get(const K &key) const { - Entry *entry = internal_get(key); - if (!entry) - zig_panic("key not found"); - return entry->value; - } - - Entry *maybe_get(const K &key) const { - return internal_get(key); - } - - bool remove(const K &key) { - bool deleted_something = maybe_remove(key); - if (!deleted_something) - zig_panic("key not found"); - return deleted_something; - } - - bool maybe_remove(const K &key) { - _modification_count += 1; - if (_index_bytes == nullptr) { - uint32_t hash = HashFunction(key); - for (size_t i = 0; i < _entries.length; i += 1) { - if (_entries.items[i].hash == hash && EqualFn(_entries.items[i].key, key)) { - _entries.swap_remove(i); - return true; - } - } - return false; - } - switch (capacity_index_size(_indexes_len)) { - case 1: return internal_remove(key, (uint8_t*)_index_bytes); - case 2: return internal_remove(key, (uint16_t*)_index_bytes); - case 4: return internal_remove(key, (uint32_t*)_index_bytes); - default: return internal_remove(key, (size_t*)_index_bytes); - } - } - - class Iterator { - public: - Entry *next() { - if (_inital_modification_count != _table->_modification_count) - zig_panic("concurrent modification"); - if (_index >= _table->_entries.length) - return nullptr; - Entry *entry = &_table->_entries.items[_index]; - _index += 1; - return entry; - } - private: - const HashMap * _table; - // iterator through the entry array - size_t _index = 0; - // used to detect concurrent modification - uint32_t _inital_modification_count; - Iterator(const HashMap * table) : - _table(table), _inital_modification_count(table->_modification_count) { - } - friend HashMap; - }; - - // you must not modify the underlying HashMap while this iterator is still in use - Iterator entry_iterator() const { - return Iterator(this); - } - -private: - // Maintains insertion order. - ZigList _entries; - // If _indexes_len is less than 2**8, this is an array of uint8_t. - // If _indexes_len is less than 2**16, it is an array of uint16_t. - // If _indexes_len is less than 2**32, it is an array of uint32_t. - // Otherwise it is size_t. - // It's off by 1. 0 means empty slot, 1 means index 0, etc. - uint8_t *_index_bytes; - // This is the number of indexes. When indexes are bytes, it equals number of bytes. - // When indexes are uint16_t, _indexes_len is half the number of bytes. - size_t _indexes_len; - - size_t _max_distance_from_start_index; - // This is used to detect bugs where a hashtable is edited while an iterator is running. - uint32_t _modification_count; - - void init_capacity(size_t capacity) { - _entries = {}; - _entries.ensure_capacity(capacity); - _indexes_len = 0; - if (capacity >= 16) { - // So that at capacity it will only be 60% full. - _indexes_len = capacity * 5 / 3; - size_t sz = capacity_index_size(_indexes_len); - // This zero initializes _index_bytes which sets them all to empty. - _index_bytes = heap::c_allocator.allocate(_indexes_len * sz); - } else { - _index_bytes = nullptr; - } - - _max_distance_from_start_index = 0; - _modification_count = 0; - } - - static size_t capacity_index_size(size_t len) { - if (len < UINT8_MAX) - return 1; - if (len < UINT16_MAX) - return 2; - if (len < UINT32_MAX) - return 4; - return sizeof(size_t); - } - - template - void internal_put(const K &key, const V &value, I *indexes) { - uint32_t hash = HashFunction(key); - uint32_t distance_from_start_index = 0; - size_t start_index = hash_to_index(hash); - for (size_t roll_over = 0; roll_over < _indexes_len; - roll_over += 1, distance_from_start_index += 1) - { - size_t index_index = (start_index + roll_over) % _indexes_len; - I index_data = indexes[index_index]; - if (index_data == 0) { - _entries.append_assuming_capacity({ hash, distance_from_start_index, key, value }); - indexes[index_index] = _entries.length; - if (distance_from_start_index > _max_distance_from_start_index) - _max_distance_from_start_index = distance_from_start_index; - return; - } - // This pointer survives the following append because we call - // _entries.ensure_capacity before internal_put. - Entry *entry = &_entries.items[index_data - 1]; - if (entry->hash == hash && EqualFn(entry->key, key)) { - *entry = {hash, distance_from_start_index, key, value}; - if (distance_from_start_index > _max_distance_from_start_index) - _max_distance_from_start_index = distance_from_start_index; - return; - } - if (entry->distance_from_start_index < distance_from_start_index) { - // In this case, we did not find the item. We will put a new entry. - // However, we will use this index for the new entry, and move - // the previous index down the line, to keep the _max_distance_from_start_index - // as small as possible. - _entries.append_assuming_capacity({ hash, distance_from_start_index, key, value }); - indexes[index_index] = _entries.length; - if (distance_from_start_index > _max_distance_from_start_index) - _max_distance_from_start_index = distance_from_start_index; - - distance_from_start_index = entry->distance_from_start_index; - - // Find somewhere to put the index we replaced by shifting - // following indexes backwards. - roll_over += 1; - distance_from_start_index += 1; - for (; roll_over < _indexes_len; roll_over += 1, distance_from_start_index += 1) { - size_t index_index = (start_index + roll_over) % _indexes_len; - I next_index_data = indexes[index_index]; - if (next_index_data == 0) { - if (distance_from_start_index > _max_distance_from_start_index) - _max_distance_from_start_index = distance_from_start_index; - entry->distance_from_start_index = distance_from_start_index; - indexes[index_index] = index_data; - return; - } - Entry *next_entry = &_entries.items[next_index_data - 1]; - if (next_entry->distance_from_start_index < distance_from_start_index) { - if (distance_from_start_index > _max_distance_from_start_index) - _max_distance_from_start_index = distance_from_start_index; - entry->distance_from_start_index = distance_from_start_index; - indexes[index_index] = index_data; - distance_from_start_index = next_entry->distance_from_start_index; - entry = next_entry; - index_data = next_index_data; - } - } - zig_unreachable(); - } - } - zig_unreachable(); - } - - template - void put_index(Entry *entry, size_t entry_index, I *indexes) { - size_t start_index = hash_to_index(entry->hash); - size_t index_data = entry_index + 1; - for (size_t roll_over = 0, distance_from_start_index = 0; - roll_over < _indexes_len; roll_over += 1, distance_from_start_index += 1) - { - size_t index_index = (start_index + roll_over) % _indexes_len; - size_t next_index_data = indexes[index_index]; - if (next_index_data == 0) { - if (distance_from_start_index > _max_distance_from_start_index) - _max_distance_from_start_index = distance_from_start_index; - entry->distance_from_start_index = distance_from_start_index; - indexes[index_index] = index_data; - return; - } - Entry *next_entry = &_entries.items[next_index_data - 1]; - if (next_entry->distance_from_start_index < distance_from_start_index) { - if (distance_from_start_index > _max_distance_from_start_index) - _max_distance_from_start_index = distance_from_start_index; - entry->distance_from_start_index = distance_from_start_index; - indexes[index_index] = index_data; - distance_from_start_index = next_entry->distance_from_start_index; - entry = next_entry; - index_data = next_index_data; - } - } - zig_unreachable(); - } - - Entry *internal_get(const K &key) const { - if (_index_bytes == nullptr) { - uint32_t hash = HashFunction(key); - for (size_t i = 0; i < _entries.length; i += 1) { - if (_entries.items[i].hash == hash && EqualFn(_entries.items[i].key, key)) { - return &_entries.items[i]; - } - } - return nullptr; - } - switch (capacity_index_size(_indexes_len)) { - case 1: return internal_get2(key, (uint8_t*)_index_bytes); - case 2: return internal_get2(key, (uint16_t*)_index_bytes); - case 4: return internal_get2(key, (uint32_t*)_index_bytes); - default: return internal_get2(key, (size_t*)_index_bytes); - } - } - - template - Entry *internal_get2(const K &key, I *indexes) const { - uint32_t hash = HashFunction(key); - size_t start_index = hash_to_index(hash); - for (size_t roll_over = 0; roll_over <= _max_distance_from_start_index; roll_over += 1) { - size_t index_index = (start_index + roll_over) % _indexes_len; - size_t index_data = indexes[index_index]; - if (index_data == 0) - return nullptr; - - Entry *entry = &_entries.items[index_data - 1]; - if (entry->hash == hash && EqualFn(entry->key, key)) - return entry; - } - return nullptr; - } - - size_t hash_to_index(uint32_t hash) const { - return ((size_t)hash) % _indexes_len; - } - - template - bool internal_remove(const K &key, I *indexes) { - uint32_t hash = HashFunction(key); - size_t start_index = hash_to_index(hash); - for (size_t roll_over = 0; roll_over <= _max_distance_from_start_index; roll_over += 1) { - size_t index_index = (start_index + roll_over) % _indexes_len; - size_t index_data = indexes[index_index]; - if (index_data == 0) - return false; - - size_t index = index_data - 1; - Entry *entry = &_entries.items[index]; - if (entry->hash != hash || !EqualFn(entry->key, key)) - continue; - - size_t prev_index = index_index; - _entries.swap_remove(index); - if (_entries.length > 0 && _entries.length != index) { - // Because of the swap remove, now we need to update the index that was - // pointing to the last entry and is now pointing to this removed item slot. - update_entry_index(_entries.length, index, indexes); - } - - // Now we have to shift over the following indexes. - roll_over += 1; - for (; roll_over < _indexes_len; roll_over += 1) { - size_t next_index = (start_index + roll_over) % _indexes_len; - if (indexes[next_index] == 0) { - indexes[prev_index] = 0; - return true; - } - Entry *next_entry = &_entries.items[indexes[next_index] - 1]; - if (next_entry->distance_from_start_index == 0) { - indexes[prev_index] = 0; - return true; - } - indexes[prev_index] = indexes[next_index]; - prev_index = next_index; - next_entry->distance_from_start_index -= 1; - } - zig_unreachable(); - } - return false; - } - - template - void update_entry_index(size_t old_entry_index, size_t new_entry_index, I *indexes) { - size_t start_index = hash_to_index(_entries.items[new_entry_index].hash); - for (size_t roll_over = 0; roll_over <= _max_distance_from_start_index; roll_over += 1) { - size_t index_index = (start_index + roll_over) % _indexes_len; - if (indexes[index_index] == old_entry_index + 1) { - indexes[index_index] = new_entry_index + 1; - return; - } - } - zig_unreachable(); - } -}; -#endif diff --git a/src/heap.cpp b/src/heap.cpp deleted file mode 100644 index 79c44d13dcd41536b5046e8bac1c5eac912c9b5d..0000000000000000000000000000000000000000 --- a/src/heap.cpp +++ /dev/null @@ -1,377 +0,0 @@ -/* - * Copyright (c) 2020 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#include -#include - -#include "config.h" -#include "heap.hpp" -#include "mem_profile.hpp" - -namespace heap { - -extern mem::Allocator &bootstrap_allocator; - -// -// BootstrapAllocator implementation is identical to CAllocator minus -// profile profile functionality. Splitting off to a base interface doesn't -// seem worthwhile. -// - -void BootstrapAllocator::init(const char *name) {} -void BootstrapAllocator::deinit() {} - -void *BootstrapAllocator::internal_allocate(const mem::TypeInfo &info, size_t count) { - return mem::os::calloc(count, info.size); -} - -void *BootstrapAllocator::internal_allocate_nonzero(const mem::TypeInfo &info, size_t count) { - return mem::os::malloc(count * info.size); -} - -void *BootstrapAllocator::internal_reallocate(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) { - auto new_ptr = this->internal_reallocate_nonzero(info, old_ptr, old_count, new_count); - if (new_count > old_count) - memset(reinterpret_cast(new_ptr) + (old_count * info.size), 0, (new_count - old_count) * info.size); - return new_ptr; -} - -void *BootstrapAllocator::internal_reallocate_nonzero(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) { - return mem::os::realloc(old_ptr, new_count * info.size); -} - -void BootstrapAllocator::internal_deallocate(const mem::TypeInfo &info, void *ptr, size_t count) { - mem::os::free(ptr); -} - -void CAllocator::init(const char *name) { -#ifdef ZIG_ENABLE_MEM_PROFILE - this->profile = bootstrap_allocator.create(); - this->profile->init(name, "CAllocator"); -#endif -} - -void CAllocator::deinit() { -#ifdef ZIG_ENABLE_MEM_PROFILE - assert(this->profile); - this->profile->deinit(); - bootstrap_allocator.destroy(this->profile); - this->profile = nullptr; -#endif -} - -CAllocator *CAllocator::construct(mem::Allocator *allocator, const char *name) { - auto p = new(allocator->create()) CAllocator(); - p->init(name); - return p; -} - -void CAllocator::destruct(mem::Allocator *allocator) { - this->deinit(); - allocator->destroy(this); -} - -#ifdef ZIG_ENABLE_MEM_PROFILE -void CAllocator::print_report(FILE *file) { - this->profile->print_report(file); -} -#endif - -void *CAllocator::internal_allocate(const mem::TypeInfo &info, size_t count) { -#ifdef ZIG_ENABLE_MEM_PROFILE - this->profile->record_alloc(info, count); -#endif - return mem::os::calloc(count, info.size); -} - -void *CAllocator::internal_allocate_nonzero(const mem::TypeInfo &info, size_t count) { -#ifdef ZIG_ENABLE_MEM_PROFILE - this->profile->record_alloc(info, count); -#endif - return mem::os::malloc(count * info.size); -} - -void *CAllocator::internal_reallocate(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) { - auto new_ptr = this->internal_reallocate_nonzero(info, old_ptr, old_count, new_count); - if (new_count > old_count) - memset(reinterpret_cast(new_ptr) + (old_count * info.size), 0, (new_count - old_count) * info.size); - return new_ptr; -} - -void *CAllocator::internal_reallocate_nonzero(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) { -#ifdef ZIG_ENABLE_MEM_PROFILE - this->profile->record_dealloc(info, old_count); - this->profile->record_alloc(info, new_count); -#endif - return mem::os::realloc(old_ptr, new_count * info.size); -} - -void CAllocator::internal_deallocate(const mem::TypeInfo &info, void *ptr, size_t count) { -#ifdef ZIG_ENABLE_MEM_PROFILE - this->profile->record_dealloc(info, count); -#endif - mem::os::free(ptr); -} - -struct ArenaAllocator::Impl { - Allocator *backing; - - // regular allocations bump through a segment of static size - struct Segment { - static constexpr size_t size = 65536; - static constexpr size_t object_threshold = 4096; - - uint8_t data[size]; - }; - - // active segment - Segment *segment; - size_t segment_offset; - - // keep track of segments - struct SegmentTrack { - static constexpr size_t size = (4096 - sizeof(SegmentTrack *)) / sizeof(Segment *); - - // null if first - SegmentTrack *prev; - Segment *segments[size]; - }; - static_assert(sizeof(SegmentTrack) <= 4096, "unwanted struct padding"); - - // active segment track - SegmentTrack *segment_track; - size_t segment_track_remain; - - // individual allocations punted to backing allocator - struct Object { - uint8_t *ptr; - size_t len; - }; - - // keep track of objects - struct ObjectTrack { - static constexpr size_t size = (4096 - sizeof(ObjectTrack *)) / sizeof(Object); - - // null if first - ObjectTrack *prev; - Object objects[size]; - }; - static_assert(sizeof(ObjectTrack) <= 4096, "unwanted struct padding"); - - // active object track - ObjectTrack *object_track; - size_t object_track_remain; - - ATTRIBUTE_RETURNS_NOALIAS inline void *allocate(const mem::TypeInfo& info, size_t count); - inline void *reallocate(const mem::TypeInfo& info, void *old_ptr, size_t old_count, size_t new_count); - - inline void new_segment(); - inline void track_segment(); - inline void track_object(Object object); -}; - -void *ArenaAllocator::Impl::allocate(const mem::TypeInfo& info, size_t count) { -#ifndef NDEBUG - // make behavior when size == 0 portable - if (info.size == 0 || count == 0) - return nullptr; -#endif - const size_t nbytes = info.size * count; - this->segment_offset = (this->segment_offset + (info.alignment - 1)) & ~(info.alignment - 1); - if (nbytes >= Segment::object_threshold) { - auto ptr = this->backing->allocate(nbytes); - this->track_object({ptr, nbytes}); - return ptr; - } - if (this->segment_offset + nbytes > Segment::size) - this->new_segment(); - auto ptr = &this->segment->data[this->segment_offset]; - this->segment_offset += nbytes; - return ptr; -} - -void *ArenaAllocator::Impl::reallocate(const mem::TypeInfo& info, void *old_ptr, size_t old_count, size_t new_count) { -#ifndef NDEBUG - // make behavior when size == 0 portable - if (info.size == 0 && old_ptr == nullptr) - return nullptr; -#endif - const size_t new_nbytes = info.size * new_count; - if (new_nbytes <= info.size * old_count) - return old_ptr; - const size_t old_nbytes = info.size * old_count; - this->segment_offset = (this->segment_offset + (info.alignment - 1)) & ~(info.alignment - 1); - if (new_nbytes >= Segment::object_threshold) { - auto new_ptr = this->backing->allocate(new_nbytes); - this->track_object({new_ptr, new_nbytes}); - memcpy(new_ptr, old_ptr, old_nbytes); - return new_ptr; - } - if (this->segment_offset + new_nbytes > Segment::size) - this->new_segment(); - auto new_ptr = &this->segment->data[this->segment_offset]; - this->segment_offset += new_nbytes; - memcpy(new_ptr, old_ptr, old_nbytes); - return new_ptr; -} - -void ArenaAllocator::Impl::new_segment() { - this->segment = this->backing->create(); - this->segment_offset = 0; - this->track_segment(); -} - -void ArenaAllocator::Impl::track_segment() { - assert(this->segment != nullptr); - if (this->segment_track_remain < 1) { - auto prev = this->segment_track; - this->segment_track = this->backing->create(); - this->segment_track->prev = prev; - this->segment_track_remain = SegmentTrack::size; - } - this->segment_track_remain -= 1; - this->segment_track->segments[this->segment_track_remain] = this->segment; -} - -void ArenaAllocator::Impl::track_object(Object object) { - if (this->object_track_remain < 1) { - auto prev = this->object_track; - this->object_track = this->backing->create(); - this->object_track->prev = prev; - this->object_track_remain = ObjectTrack::size; - } - this->object_track_remain -= 1; - this->object_track->objects[this->object_track_remain] = object; -} - -void ArenaAllocator::init(Allocator *backing, const char *name) { -#ifdef ZIG_ENABLE_MEM_PROFILE - this->profile = bootstrap_allocator.create(); - this->profile->init(name, "ArenaAllocator"); -#endif - this->impl = bootstrap_allocator.create(); - { - auto &r = *this->impl; - r.backing = backing; - r.segment_offset = Impl::Segment::size; - } -} - -void ArenaAllocator::deinit() { - auto &backing = *this->impl->backing; - - // segments - if (this->impl->segment_track) { - // active track is not full and bounded by track_remain - auto prev = this->impl->segment_track->prev; - { - auto t = this->impl->segment_track; - for (size_t i = this->impl->segment_track_remain; i < Impl::SegmentTrack::size; ++i) - backing.destroy(t->segments[i]); - backing.destroy(t); - } - - // previous tracks are full - for (auto t = prev; t != nullptr;) { - for (size_t i = 0; i < Impl::SegmentTrack::size; ++i) - backing.destroy(t->segments[i]); - prev = t->prev; - backing.destroy(t); - t = prev; - } - } - - // objects - if (this->impl->object_track) { - // active track is not full and bounded by track_remain - auto prev = this->impl->object_track->prev; - { - auto t = this->impl->object_track; - for (size_t i = this->impl->object_track_remain; i < Impl::ObjectTrack::size; ++i) { - auto &obj = t->objects[i]; - backing.deallocate(obj.ptr, obj.len); - } - backing.destroy(t); - } - - // previous tracks are full - for (auto t = prev; t != nullptr;) { - for (size_t i = 0; i < Impl::ObjectTrack::size; ++i) { - auto &obj = t->objects[i]; - backing.deallocate(obj.ptr, obj.len); - } - prev = t->prev; - backing.destroy(t); - t = prev; - } - } - -#ifdef ZIG_ENABLE_MEM_PROFILE - assert(this->profile); - this->profile->deinit(); - bootstrap_allocator.destroy(this->profile); - this->profile = nullptr; -#endif -} - -ArenaAllocator *ArenaAllocator::construct(mem::Allocator *allocator, mem::Allocator *backing, const char *name) { - auto p = new(allocator->create()) ArenaAllocator; - p->init(backing, name); - return p; -} - -void ArenaAllocator::destruct(mem::Allocator *allocator) { - this->deinit(); - allocator->destroy(this); -} - -#ifdef ZIG_ENABLE_MEM_PROFILE -void ArenaAllocator::print_report(FILE *file) { - this->profile->print_report(file); -} -#endif - -void *ArenaAllocator::internal_allocate(const mem::TypeInfo &info, size_t count) { -#ifdef ZIG_ENABLE_MEM_PROFILE - this->profile->record_alloc(info, count); -#endif - return this->impl->allocate(info, count); -} - -void *ArenaAllocator::internal_allocate_nonzero(const mem::TypeInfo &info, size_t count) { -#ifdef ZIG_ENABLE_MEM_PROFILE - this->profile->record_alloc(info, count); -#endif - return this->impl->allocate(info, count); -} - -void *ArenaAllocator::internal_reallocate(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) { - return this->internal_reallocate_nonzero(info, old_ptr, old_count, new_count); -} - -void *ArenaAllocator::internal_reallocate_nonzero(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) { -#ifdef ZIG_ENABLE_MEM_PROFILE - this->profile->record_dealloc(info, old_count); - this->profile->record_alloc(info, new_count); -#endif - return this->impl->reallocate(info, old_ptr, old_count, new_count); -} - -void ArenaAllocator::internal_deallocate(const mem::TypeInfo &info, void *ptr, size_t count) { -#ifdef ZIG_ENABLE_MEM_PROFILE - this->profile->record_dealloc(info, count); -#endif - // noop -} - -BootstrapAllocator bootstrap_allocator_state; -mem::Allocator &bootstrap_allocator = bootstrap_allocator_state; - -CAllocator c_allocator_state; -mem::Allocator &c_allocator = c_allocator_state; - -} // namespace heap diff --git a/src/heap.hpp b/src/heap.hpp deleted file mode 100644 index e22ec42967b0c30c7886f0aa8409952c0e4d4928..0000000000000000000000000000000000000000 --- a/src/heap.hpp +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (c) 2020 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#ifndef ZIG_HEAP_HPP -#define ZIG_HEAP_HPP - -#include "config.h" -#include "util_base.hpp" -#include "mem.hpp" - -#ifdef ZIG_ENABLE_MEM_PROFILE -namespace mem { - struct Profile; -} -#endif - -namespace heap { - -struct BootstrapAllocator final : mem::Allocator { - void init(const char *name); - void deinit(); - void destruct(Allocator *allocator) {} - -private: - ATTRIBUTE_RETURNS_NOALIAS void *internal_allocate(const mem::TypeInfo &info, size_t count) final; - ATTRIBUTE_RETURNS_NOALIAS void *internal_allocate_nonzero(const mem::TypeInfo &info, size_t count) final; - void *internal_reallocate(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) final; - void *internal_reallocate_nonzero(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) final; - void internal_deallocate(const mem::TypeInfo &info, void *ptr, size_t count) final; -}; - -struct CAllocator final : mem::Allocator { - void init(const char *name); - void deinit(); - - static CAllocator *construct(mem::Allocator *allocator, const char *name); - void destruct(mem::Allocator *allocator) final; - -#ifdef ZIG_ENABLE_MEM_PROFILE - void print_report(FILE *file = nullptr); -#endif - -private: - ATTRIBUTE_RETURNS_NOALIAS void *internal_allocate(const mem::TypeInfo &info, size_t count) final; - ATTRIBUTE_RETURNS_NOALIAS void *internal_allocate_nonzero(const mem::TypeInfo &info, size_t count) final; - void *internal_reallocate(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) final; - void *internal_reallocate_nonzero(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) final; - void internal_deallocate(const mem::TypeInfo &info, void *ptr, size_t count) final; - -#ifdef ZIG_ENABLE_MEM_PROFILE - mem::Profile *profile; -#endif -}; - -// -// arena allocator -// -// - allocations are backed by the underlying allocator's memory -// - allocations are N:1 relationship to underlying allocations -// - dellocations are noops -// - deinit() releases all underlying memory -// -struct ArenaAllocator final : mem::Allocator { - void init(Allocator *backing, const char *name); - void deinit(); - - static ArenaAllocator *construct(mem::Allocator *allocator, mem::Allocator *backing, const char *name); - void destruct(mem::Allocator *allocator) final; - -#ifdef ZIG_ENABLE_MEM_PROFILE - void print_report(FILE *file = nullptr); -#endif - -private: - ATTRIBUTE_RETURNS_NOALIAS void *internal_allocate(const mem::TypeInfo &info, size_t count) final; - ATTRIBUTE_RETURNS_NOALIAS void *internal_allocate_nonzero(const mem::TypeInfo &info, size_t count) final; - void *internal_reallocate(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) final; - void *internal_reallocate_nonzero(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) final; - void internal_deallocate(const mem::TypeInfo &info, void *ptr, size_t count) final; - -#ifdef ZIG_ENABLE_MEM_PROFILE - mem::Profile *profile; -#endif - - struct Impl; - Impl *impl; -}; - -extern BootstrapAllocator bootstrap_allocator_state; -extern mem::Allocator &bootstrap_allocator; - -extern CAllocator c_allocator_state; -extern mem::Allocator &c_allocator; - -} // namespace heap - -#endif diff --git a/src/install_files.h b/src/install_files.h deleted file mode 100644 index 8e7431145e71e5409d7ccce87c1421c75a1d292d..0000000000000000000000000000000000000000 --- a/src/install_files.h +++ /dev/null @@ -1,1907 +0,0 @@ -#ifndef ZIG_INSTALL_FILES_H -#define ZIG_INSTALL_FILES_H -static const char *ZIG_MUSL_SRC_FILES[] = { -"musl/src/aio/aio.c", -"musl/src/aio/aio_suspend.c", -"musl/src/aio/lio_listio.c", -"musl/src/complex/__cexp.c", -"musl/src/complex/__cexpf.c", -"musl/src/complex/cabs.c", -"musl/src/complex/cabsf.c", -"musl/src/complex/cabsl.c", -"musl/src/complex/cacos.c", -"musl/src/complex/cacosf.c", -"musl/src/complex/cacosh.c", -"musl/src/complex/cacoshf.c", -"musl/src/complex/cacoshl.c", -"musl/src/complex/cacosl.c", -"musl/src/complex/carg.c", -"musl/src/complex/cargf.c", -"musl/src/complex/cargl.c", -"musl/src/complex/casin.c", -"musl/src/complex/casinf.c", -"musl/src/complex/casinh.c", -"musl/src/complex/casinhf.c", -"musl/src/complex/casinhl.c", -"musl/src/complex/casinl.c", -"musl/src/complex/catan.c", -"musl/src/complex/catanf.c", -"musl/src/complex/catanh.c", -"musl/src/complex/catanhf.c", -"musl/src/complex/catanhl.c", -"musl/src/complex/catanl.c", -"musl/src/complex/ccos.c", -"musl/src/complex/ccosf.c", -"musl/src/complex/ccosh.c", -"musl/src/complex/ccoshf.c", -"musl/src/complex/ccoshl.c", -"musl/src/complex/ccosl.c", -"musl/src/complex/cexp.c", -"musl/src/complex/cexpf.c", -"musl/src/complex/cexpl.c", -"musl/src/complex/cimag.c", -"musl/src/complex/cimagf.c", -"musl/src/complex/cimagl.c", -"musl/src/complex/clog.c", -"musl/src/complex/clogf.c", -"musl/src/complex/clogl.c", -"musl/src/complex/conj.c", -"musl/src/complex/conjf.c", -"musl/src/complex/conjl.c", -"musl/src/complex/cpow.c", -"musl/src/complex/cpowf.c", -"musl/src/complex/cpowl.c", -"musl/src/complex/cproj.c", -"musl/src/complex/cprojf.c", -"musl/src/complex/cprojl.c", -"musl/src/complex/creal.c", -"musl/src/complex/crealf.c", -"musl/src/complex/creall.c", -"musl/src/complex/csin.c", -"musl/src/complex/csinf.c", -"musl/src/complex/csinh.c", -"musl/src/complex/csinhf.c", -"musl/src/complex/csinhl.c", -"musl/src/complex/csinl.c", -"musl/src/complex/csqrt.c", -"musl/src/complex/csqrtf.c", -"musl/src/complex/csqrtl.c", -"musl/src/complex/ctan.c", -"musl/src/complex/ctanf.c", -"musl/src/complex/ctanh.c", -"musl/src/complex/ctanhf.c", -"musl/src/complex/ctanhl.c", -"musl/src/complex/ctanl.c", -"musl/src/conf/confstr.c", -"musl/src/conf/fpathconf.c", -"musl/src/conf/legacy.c", -"musl/src/conf/pathconf.c", -"musl/src/conf/sysconf.c", -"musl/src/crypt/crypt.c", -"musl/src/crypt/crypt_blowfish.c", -"musl/src/crypt/crypt_des.c", -"musl/src/crypt/crypt_md5.c", -"musl/src/crypt/crypt_r.c", -"musl/src/crypt/crypt_sha256.c", -"musl/src/crypt/crypt_sha512.c", -"musl/src/crypt/encrypt.c", -"musl/src/ctype/__ctype_b_loc.c", -"musl/src/ctype/__ctype_get_mb_cur_max.c", -"musl/src/ctype/__ctype_tolower_loc.c", -"musl/src/ctype/__ctype_toupper_loc.c", -"musl/src/ctype/isalnum.c", -"musl/src/ctype/isalpha.c", -"musl/src/ctype/isascii.c", -"musl/src/ctype/isblank.c", -"musl/src/ctype/iscntrl.c", -"musl/src/ctype/isdigit.c", -"musl/src/ctype/isgraph.c", -"musl/src/ctype/islower.c", -"musl/src/ctype/isprint.c", -"musl/src/ctype/ispunct.c", -"musl/src/ctype/isspace.c", -"musl/src/ctype/isupper.c", -"musl/src/ctype/iswalnum.c", -"musl/src/ctype/iswalpha.c", -"musl/src/ctype/iswblank.c", -"musl/src/ctype/iswcntrl.c", -"musl/src/ctype/iswctype.c", -"musl/src/ctype/iswdigit.c", -"musl/src/ctype/iswgraph.c", -"musl/src/ctype/iswlower.c", -"musl/src/ctype/iswprint.c", -"musl/src/ctype/iswpunct.c", -"musl/src/ctype/iswspace.c", -"musl/src/ctype/iswupper.c", -"musl/src/ctype/iswxdigit.c", -"musl/src/ctype/isxdigit.c", -"musl/src/ctype/toascii.c", -"musl/src/ctype/tolower.c", -"musl/src/ctype/toupper.c", -"musl/src/ctype/towctrans.c", -"musl/src/ctype/wcswidth.c", -"musl/src/ctype/wctrans.c", -"musl/src/ctype/wcwidth.c", -"musl/src/dirent/alphasort.c", -"musl/src/dirent/closedir.c", -"musl/src/dirent/dirfd.c", -"musl/src/dirent/fdopendir.c", -"musl/src/dirent/opendir.c", -"musl/src/dirent/readdir.c", -"musl/src/dirent/readdir_r.c", -"musl/src/dirent/rewinddir.c", -"musl/src/dirent/scandir.c", -"musl/src/dirent/seekdir.c", -"musl/src/dirent/telldir.c", -"musl/src/dirent/versionsort.c", -"musl/src/env/__environ.c", -"musl/src/env/__init_tls.c", -"musl/src/env/__libc_start_main.c", -"musl/src/env/__reset_tls.c", -"musl/src/env/__stack_chk_fail.c", -"musl/src/env/clearenv.c", -"musl/src/env/getenv.c", -"musl/src/env/putenv.c", -"musl/src/env/secure_getenv.c", -"musl/src/env/setenv.c", -"musl/src/env/unsetenv.c", -"musl/src/errno/__errno_location.c", -"musl/src/errno/strerror.c", -"musl/src/exit/_Exit.c", -"musl/src/exit/abort.c", -"musl/src/exit/arm/__aeabi_atexit.c", -"musl/src/exit/assert.c", -"musl/src/exit/at_quick_exit.c", -"musl/src/exit/atexit.c", -"musl/src/exit/exit.c", -"musl/src/exit/quick_exit.c", -"musl/src/fcntl/creat.c", -"musl/src/fcntl/fcntl.c", -"musl/src/fcntl/open.c", -"musl/src/fcntl/openat.c", -"musl/src/fcntl/posix_fadvise.c", -"musl/src/fcntl/posix_fallocate.c", -"musl/src/fenv/__flt_rounds.c", -"musl/src/fenv/aarch64/fenv.s", -"musl/src/fenv/arm/fenv-hf.S", -"musl/src/fenv/arm/fenv.c", -"musl/src/fenv/fegetexceptflag.c", -"musl/src/fenv/feholdexcept.c", -"musl/src/fenv/fenv.c", -"musl/src/fenv/fesetexceptflag.c", -"musl/src/fenv/fesetround.c", -"musl/src/fenv/feupdateenv.c", -"musl/src/fenv/i386/fenv.s", -"musl/src/fenv/m68k/fenv.c", -"musl/src/fenv/mips/fenv-sf.c", -"musl/src/fenv/mips/fenv.S", -"musl/src/fenv/mips64/fenv-sf.c", -"musl/src/fenv/mips64/fenv.S", -"musl/src/fenv/mipsn32/fenv-sf.c", -"musl/src/fenv/mipsn32/fenv.S", -"musl/src/fenv/powerpc/fenv-sf.c", -"musl/src/fenv/powerpc/fenv.S", -"musl/src/fenv/powerpc64/fenv.c", -"musl/src/fenv/riscv64/fenv-sf.c", -"musl/src/fenv/riscv64/fenv.S", -"musl/src/fenv/s390x/fenv.c", -"musl/src/fenv/sh/fenv-nofpu.c", -"musl/src/fenv/sh/fenv.S", -"musl/src/fenv/x32/fenv.s", -"musl/src/fenv/x86_64/fenv.s", -"musl/src/internal/defsysinfo.c", -"musl/src/internal/floatscan.c", -"musl/src/internal/i386/defsysinfo.s", -"musl/src/internal/intscan.c", -"musl/src/internal/libc.c", -"musl/src/internal/procfdname.c", -"musl/src/internal/sh/__shcall.c", -"musl/src/internal/shgetc.c", -"musl/src/internal/syscall_ret.c", -"musl/src/internal/vdso.c", -"musl/src/internal/version.c", -"musl/src/ipc/ftok.c", -"musl/src/ipc/msgctl.c", -"musl/src/ipc/msgget.c", -"musl/src/ipc/msgrcv.c", -"musl/src/ipc/msgsnd.c", -"musl/src/ipc/semctl.c", -"musl/src/ipc/semget.c", -"musl/src/ipc/semop.c", -"musl/src/ipc/semtimedop.c", -"musl/src/ipc/shmat.c", -"musl/src/ipc/shmctl.c", -"musl/src/ipc/shmdt.c", -"musl/src/ipc/shmget.c", -"musl/src/ldso/__dlsym.c", -"musl/src/ldso/aarch64/dlsym.s", -"musl/src/ldso/aarch64/tlsdesc.s", -"musl/src/ldso/arm/dlsym.s", -"musl/src/ldso/arm/dlsym_time64.S", -"musl/src/ldso/arm/find_exidx.c", -"musl/src/ldso/arm/tlsdesc.S", -"musl/src/ldso/dl_iterate_phdr.c", -"musl/src/ldso/dladdr.c", -"musl/src/ldso/dlclose.c", -"musl/src/ldso/dlerror.c", -"musl/src/ldso/dlinfo.c", -"musl/src/ldso/dlopen.c", -"musl/src/ldso/dlsym.c", -"musl/src/ldso/i386/dlsym.s", -"musl/src/ldso/i386/dlsym_time64.S", -"musl/src/ldso/i386/tlsdesc.s", -"musl/src/ldso/m68k/dlsym.s", -"musl/src/ldso/m68k/dlsym_time64.S", -"musl/src/ldso/microblaze/dlsym.s", -"musl/src/ldso/microblaze/dlsym_time64.S", -"musl/src/ldso/mips/dlsym.s", -"musl/src/ldso/mips/dlsym_time64.S", -"musl/src/ldso/mips64/dlsym.s", -"musl/src/ldso/mipsn32/dlsym.s", -"musl/src/ldso/mipsn32/dlsym_time64.S", -"musl/src/ldso/or1k/dlsym.s", -"musl/src/ldso/or1k/dlsym_time64.S", -"musl/src/ldso/powerpc/dlsym.s", -"musl/src/ldso/powerpc/dlsym_time64.S", -"musl/src/ldso/powerpc64/dlsym.s", -"musl/src/ldso/riscv64/dlsym.s", -"musl/src/ldso/s390x/dlsym.s", -"musl/src/ldso/sh/dlsym.s", -"musl/src/ldso/sh/dlsym_time64.S", -"musl/src/ldso/tlsdesc.c", -"musl/src/ldso/x32/dlsym.s", -"musl/src/ldso/x86_64/dlsym.s", -"musl/src/ldso/x86_64/tlsdesc.s", -"musl/src/legacy/cuserid.c", -"musl/src/legacy/daemon.c", -"musl/src/legacy/err.c", -"musl/src/legacy/euidaccess.c", -"musl/src/legacy/ftw.c", -"musl/src/legacy/futimes.c", -"musl/src/legacy/getdtablesize.c", -"musl/src/legacy/getloadavg.c", -"musl/src/legacy/getpagesize.c", -"musl/src/legacy/getpass.c", -"musl/src/legacy/getusershell.c", -"musl/src/legacy/isastream.c", -"musl/src/legacy/lutimes.c", -"musl/src/legacy/ulimit.c", -"musl/src/legacy/utmpx.c", -"musl/src/legacy/valloc.c", -"musl/src/linux/adjtime.c", -"musl/src/linux/adjtimex.c", -"musl/src/linux/arch_prctl.c", -"musl/src/linux/brk.c", -"musl/src/linux/cache.c", -"musl/src/linux/cap.c", -"musl/src/linux/chroot.c", -"musl/src/linux/clock_adjtime.c", -"musl/src/linux/clone.c", -"musl/src/linux/copy_file_range.c", -"musl/src/linux/epoll.c", -"musl/src/linux/eventfd.c", -"musl/src/linux/fallocate.c", -"musl/src/linux/fanotify.c", -"musl/src/linux/flock.c", -"musl/src/linux/getdents.c", -"musl/src/linux/getrandom.c", -"musl/src/linux/inotify.c", -"musl/src/linux/ioperm.c", -"musl/src/linux/iopl.c", -"musl/src/linux/klogctl.c", -"musl/src/linux/membarrier.c", -"musl/src/linux/memfd_create.c", -"musl/src/linux/mlock2.c", -"musl/src/linux/module.c", -"musl/src/linux/mount.c", -"musl/src/linux/name_to_handle_at.c", -"musl/src/linux/open_by_handle_at.c", -"musl/src/linux/personality.c", -"musl/src/linux/pivot_root.c", -"musl/src/linux/ppoll.c", -"musl/src/linux/prctl.c", -"musl/src/linux/prlimit.c", -"musl/src/linux/process_vm.c", -"musl/src/linux/ptrace.c", -"musl/src/linux/quotactl.c", -"musl/src/linux/readahead.c", -"musl/src/linux/reboot.c", -"musl/src/linux/remap_file_pages.c", -"musl/src/linux/sbrk.c", -"musl/src/linux/sendfile.c", -"musl/src/linux/setfsgid.c", -"musl/src/linux/setfsuid.c", -"musl/src/linux/setgroups.c", -"musl/src/linux/sethostname.c", -"musl/src/linux/setns.c", -"musl/src/linux/settimeofday.c", -"musl/src/linux/signalfd.c", -"musl/src/linux/splice.c", -"musl/src/linux/stime.c", -"musl/src/linux/swap.c", -"musl/src/linux/sync_file_range.c", -"musl/src/linux/syncfs.c", -"musl/src/linux/sysinfo.c", -"musl/src/linux/tee.c", -"musl/src/linux/timerfd.c", -"musl/src/linux/unshare.c", -"musl/src/linux/utimes.c", -"musl/src/linux/vhangup.c", -"musl/src/linux/vmsplice.c", -"musl/src/linux/wait3.c", -"musl/src/linux/wait4.c", -"musl/src/linux/x32/sysinfo.c", -"musl/src/linux/xattr.c", -"musl/src/locale/__lctrans.c", -"musl/src/locale/__mo_lookup.c", -"musl/src/locale/bind_textdomain_codeset.c", -"musl/src/locale/c_locale.c", -"musl/src/locale/catclose.c", -"musl/src/locale/catgets.c", -"musl/src/locale/catopen.c", -"musl/src/locale/dcngettext.c", -"musl/src/locale/duplocale.c", -"musl/src/locale/freelocale.c", -"musl/src/locale/iconv.c", -"musl/src/locale/iconv_close.c", -"musl/src/locale/langinfo.c", -"musl/src/locale/locale_map.c", -"musl/src/locale/localeconv.c", -"musl/src/locale/newlocale.c", -"musl/src/locale/pleval.c", -"musl/src/locale/setlocale.c", -"musl/src/locale/strcoll.c", -"musl/src/locale/strfmon.c", -"musl/src/locale/strxfrm.c", -"musl/src/locale/textdomain.c", -"musl/src/locale/uselocale.c", -"musl/src/locale/wcscoll.c", -"musl/src/locale/wcsxfrm.c", -"musl/src/malloc/aligned_alloc.c", -"musl/src/malloc/expand_heap.c", -"musl/src/malloc/lite_malloc.c", -"musl/src/malloc/malloc.c", -"musl/src/malloc/malloc_usable_size.c", -"musl/src/malloc/memalign.c", -"musl/src/malloc/posix_memalign.c", -"musl/src/math/__cos.c", -"musl/src/math/__cosdf.c", -"musl/src/math/__cosl.c", -"musl/src/math/__expo2.c", -"musl/src/math/__expo2f.c", -"musl/src/math/__fpclassify.c", -"musl/src/math/__fpclassifyf.c", -"musl/src/math/__fpclassifyl.c", -"musl/src/math/__invtrigl.c", -"musl/src/math/__math_divzero.c", -"musl/src/math/__math_divzerof.c", -"musl/src/math/__math_invalid.c", -"musl/src/math/__math_invalidf.c", -"musl/src/math/__math_oflow.c", -"musl/src/math/__math_oflowf.c", -"musl/src/math/__math_uflow.c", -"musl/src/math/__math_uflowf.c", -"musl/src/math/__math_xflow.c", -"musl/src/math/__math_xflowf.c", -"musl/src/math/__polevll.c", -"musl/src/math/__rem_pio2.c", -"musl/src/math/__rem_pio2_large.c", -"musl/src/math/__rem_pio2f.c", -"musl/src/math/__rem_pio2l.c", -"musl/src/math/__signbit.c", -"musl/src/math/__signbitf.c", -"musl/src/math/__signbitl.c", -"musl/src/math/__sin.c", -"musl/src/math/__sindf.c", -"musl/src/math/__sinl.c", -"musl/src/math/__tan.c", -"musl/src/math/__tandf.c", -"musl/src/math/__tanl.c", -"musl/src/math/aarch64/ceil.c", -"musl/src/math/aarch64/ceilf.c", -"musl/src/math/aarch64/fabs.c", -"musl/src/math/aarch64/fabsf.c", -"musl/src/math/aarch64/floor.c", -"musl/src/math/aarch64/floorf.c", -"musl/src/math/aarch64/fma.c", -"musl/src/math/aarch64/fmaf.c", -"musl/src/math/aarch64/fmax.c", -"musl/src/math/aarch64/fmaxf.c", -"musl/src/math/aarch64/fmin.c", -"musl/src/math/aarch64/fminf.c", -"musl/src/math/aarch64/llrint.c", -"musl/src/math/aarch64/llrintf.c", -"musl/src/math/aarch64/llround.c", -"musl/src/math/aarch64/llroundf.c", -"musl/src/math/aarch64/lrint.c", -"musl/src/math/aarch64/lrintf.c", -"musl/src/math/aarch64/lround.c", -"musl/src/math/aarch64/lroundf.c", -"musl/src/math/aarch64/nearbyint.c", -"musl/src/math/aarch64/nearbyintf.c", -"musl/src/math/aarch64/rint.c", -"musl/src/math/aarch64/rintf.c", -"musl/src/math/aarch64/round.c", -"musl/src/math/aarch64/roundf.c", -"musl/src/math/aarch64/sqrt.c", -"musl/src/math/aarch64/sqrtf.c", -"musl/src/math/aarch64/trunc.c", -"musl/src/math/aarch64/truncf.c", -"musl/src/math/acos.c", -"musl/src/math/acosf.c", -"musl/src/math/acosh.c", -"musl/src/math/acoshf.c", -"musl/src/math/acoshl.c", -"musl/src/math/acosl.c", -"musl/src/math/arm/fabs.c", -"musl/src/math/arm/fabsf.c", -"musl/src/math/arm/fma.c", -"musl/src/math/arm/fmaf.c", -"musl/src/math/arm/sqrt.c", -"musl/src/math/arm/sqrtf.c", -"musl/src/math/asin.c", -"musl/src/math/asinf.c", -"musl/src/math/asinh.c", -"musl/src/math/asinhf.c", -"musl/src/math/asinhl.c", -"musl/src/math/asinl.c", -"musl/src/math/atan.c", -"musl/src/math/atan2.c", -"musl/src/math/atan2f.c", -"musl/src/math/atan2l.c", -"musl/src/math/atanf.c", -"musl/src/math/atanh.c", -"musl/src/math/atanhf.c", -"musl/src/math/atanhl.c", -"musl/src/math/atanl.c", -"musl/src/math/cbrt.c", -"musl/src/math/cbrtf.c", -"musl/src/math/cbrtl.c", -"musl/src/math/ceil.c", -"musl/src/math/ceilf.c", -"musl/src/math/ceill.c", -"musl/src/math/copysign.c", -"musl/src/math/copysignf.c", -"musl/src/math/copysignl.c", -"musl/src/math/cos.c", -"musl/src/math/cosf.c", -"musl/src/math/cosh.c", -"musl/src/math/coshf.c", -"musl/src/math/coshl.c", -"musl/src/math/cosl.c", -"musl/src/math/erf.c", -"musl/src/math/erff.c", -"musl/src/math/erfl.c", -"musl/src/math/exp.c", -"musl/src/math/exp10.c", -"musl/src/math/exp10f.c", -"musl/src/math/exp10l.c", -"musl/src/math/exp2.c", -"musl/src/math/exp2f.c", -"musl/src/math/exp2f_data.c", -"musl/src/math/exp2l.c", -"musl/src/math/exp_data.c", -"musl/src/math/expf.c", -"musl/src/math/expl.c", -"musl/src/math/expm1.c", -"musl/src/math/expm1f.c", -"musl/src/math/expm1l.c", -"musl/src/math/fabs.c", -"musl/src/math/fabsf.c", -"musl/src/math/fabsl.c", -"musl/src/math/fdim.c", -"musl/src/math/fdimf.c", -"musl/src/math/fdiml.c", -"musl/src/math/finite.c", -"musl/src/math/finitef.c", -"musl/src/math/floor.c", -"musl/src/math/floorf.c", -"musl/src/math/floorl.c", -"musl/src/math/fma.c", -"musl/src/math/fmaf.c", -"musl/src/math/fmal.c", -"musl/src/math/fmax.c", -"musl/src/math/fmaxf.c", -"musl/src/math/fmaxl.c", -"musl/src/math/fmin.c", -"musl/src/math/fminf.c", -"musl/src/math/fminl.c", -"musl/src/math/fmod.c", -"musl/src/math/fmodf.c", -"musl/src/math/fmodl.c", -"musl/src/math/frexp.c", -"musl/src/math/frexpf.c", -"musl/src/math/frexpl.c", -"musl/src/math/hypot.c", -"musl/src/math/hypotf.c", -"musl/src/math/hypotl.c", -"musl/src/math/i386/__invtrigl.s", -"musl/src/math/i386/acos.s", -"musl/src/math/i386/acosf.s", -"musl/src/math/i386/acosl.s", -"musl/src/math/i386/asin.s", -"musl/src/math/i386/asinf.s", -"musl/src/math/i386/asinl.s", -"musl/src/math/i386/atan.s", -"musl/src/math/i386/atan2.s", -"musl/src/math/i386/atan2f.s", -"musl/src/math/i386/atan2l.s", -"musl/src/math/i386/atanf.s", -"musl/src/math/i386/atanl.s", -"musl/src/math/i386/ceil.s", -"musl/src/math/i386/ceilf.s", -"musl/src/math/i386/ceill.s", -"musl/src/math/i386/exp2l.s", -"musl/src/math/i386/exp_ld.s", -"musl/src/math/i386/expl.s", -"musl/src/math/i386/expm1l.s", -"musl/src/math/i386/fabs.s", -"musl/src/math/i386/fabsf.s", -"musl/src/math/i386/fabsl.s", -"musl/src/math/i386/floor.s", -"musl/src/math/i386/floorf.s", -"musl/src/math/i386/floorl.s", -"musl/src/math/i386/fmod.s", -"musl/src/math/i386/fmodf.s", -"musl/src/math/i386/fmodl.s", -"musl/src/math/i386/hypot.s", -"musl/src/math/i386/hypotf.s", -"musl/src/math/i386/ldexp.s", -"musl/src/math/i386/ldexpf.s", -"musl/src/math/i386/ldexpl.s", -"musl/src/math/i386/llrint.s", -"musl/src/math/i386/llrintf.s", -"musl/src/math/i386/llrintl.s", -"musl/src/math/i386/log.s", -"musl/src/math/i386/log10.s", -"musl/src/math/i386/log10f.s", -"musl/src/math/i386/log10l.s", -"musl/src/math/i386/log1p.s", -"musl/src/math/i386/log1pf.s", -"musl/src/math/i386/log1pl.s", -"musl/src/math/i386/log2.s", -"musl/src/math/i386/log2f.s", -"musl/src/math/i386/log2l.s", -"musl/src/math/i386/logf.s", -"musl/src/math/i386/logl.s", -"musl/src/math/i386/lrint.s", -"musl/src/math/i386/lrintf.s", -"musl/src/math/i386/lrintl.s", -"musl/src/math/i386/remainder.s", -"musl/src/math/i386/remainderf.s", -"musl/src/math/i386/remainderl.s", -"musl/src/math/i386/remquo.s", -"musl/src/math/i386/remquof.s", -"musl/src/math/i386/remquol.s", -"musl/src/math/i386/rint.s", -"musl/src/math/i386/rintf.s", -"musl/src/math/i386/rintl.s", -"musl/src/math/i386/scalbln.s", -"musl/src/math/i386/scalblnf.s", -"musl/src/math/i386/scalblnl.s", -"musl/src/math/i386/scalbn.s", -"musl/src/math/i386/scalbnf.s", -"musl/src/math/i386/scalbnl.s", -"musl/src/math/i386/sqrt.s", -"musl/src/math/i386/sqrtf.s", -"musl/src/math/i386/sqrtl.s", -"musl/src/math/i386/trunc.s", -"musl/src/math/i386/truncf.s", -"musl/src/math/i386/truncl.s", -"musl/src/math/ilogb.c", -"musl/src/math/ilogbf.c", -"musl/src/math/ilogbl.c", -"musl/src/math/j0.c", -"musl/src/math/j0f.c", -"musl/src/math/j1.c", -"musl/src/math/j1f.c", -"musl/src/math/jn.c", -"musl/src/math/jnf.c", -"musl/src/math/ldexp.c", -"musl/src/math/ldexpf.c", -"musl/src/math/ldexpl.c", -"musl/src/math/lgamma.c", -"musl/src/math/lgamma_r.c", -"musl/src/math/lgammaf.c", -"musl/src/math/lgammaf_r.c", -"musl/src/math/lgammal.c", -"musl/src/math/llrint.c", -"musl/src/math/llrintf.c", -"musl/src/math/llrintl.c", -"musl/src/math/llround.c", -"musl/src/math/llroundf.c", -"musl/src/math/llroundl.c", -"musl/src/math/log.c", -"musl/src/math/log10.c", -"musl/src/math/log10f.c", -"musl/src/math/log10l.c", -"musl/src/math/log1p.c", -"musl/src/math/log1pf.c", -"musl/src/math/log1pl.c", -"musl/src/math/log2.c", -"musl/src/math/log2_data.c", -"musl/src/math/log2f.c", -"musl/src/math/log2f_data.c", -"musl/src/math/log2l.c", -"musl/src/math/log_data.c", -"musl/src/math/logb.c", -"musl/src/math/logbf.c", -"musl/src/math/logbl.c", -"musl/src/math/logf.c", -"musl/src/math/logf_data.c", -"musl/src/math/logl.c", -"musl/src/math/lrint.c", -"musl/src/math/lrintf.c", -"musl/src/math/lrintl.c", -"musl/src/math/lround.c", -"musl/src/math/lroundf.c", -"musl/src/math/lroundl.c", -"musl/src/math/mips/fabs.c", -"musl/src/math/mips/fabsf.c", -"musl/src/math/mips/sqrt.c", -"musl/src/math/mips/sqrtf.c", -"musl/src/math/modf.c", -"musl/src/math/modff.c", -"musl/src/math/modfl.c", -"musl/src/math/nan.c", -"musl/src/math/nanf.c", -"musl/src/math/nanl.c", -"musl/src/math/nearbyint.c", -"musl/src/math/nearbyintf.c", -"musl/src/math/nearbyintl.c", -"musl/src/math/nextafter.c", -"musl/src/math/nextafterf.c", -"musl/src/math/nextafterl.c", -"musl/src/math/nexttoward.c", -"musl/src/math/nexttowardf.c", -"musl/src/math/nexttowardl.c", -"musl/src/math/pow.c", -"musl/src/math/pow_data.c", -"musl/src/math/powerpc/fabs.c", -"musl/src/math/powerpc/fabsf.c", -"musl/src/math/powerpc/fma.c", -"musl/src/math/powerpc/fmaf.c", -"musl/src/math/powerpc/sqrt.c", -"musl/src/math/powerpc/sqrtf.c", -"musl/src/math/powerpc64/ceil.c", -"musl/src/math/powerpc64/ceilf.c", -"musl/src/math/powerpc64/fabs.c", -"musl/src/math/powerpc64/fabsf.c", -"musl/src/math/powerpc64/floor.c", -"musl/src/math/powerpc64/floorf.c", -"musl/src/math/powerpc64/fma.c", -"musl/src/math/powerpc64/fmaf.c", -"musl/src/math/powerpc64/fmax.c", -"musl/src/math/powerpc64/fmaxf.c", -"musl/src/math/powerpc64/fmin.c", -"musl/src/math/powerpc64/fminf.c", -"musl/src/math/powerpc64/lrint.c", -"musl/src/math/powerpc64/lrintf.c", -"musl/src/math/powerpc64/lround.c", -"musl/src/math/powerpc64/lroundf.c", -"musl/src/math/powerpc64/round.c", -"musl/src/math/powerpc64/roundf.c", -"musl/src/math/powerpc64/sqrt.c", -"musl/src/math/powerpc64/sqrtf.c", -"musl/src/math/powerpc64/trunc.c", -"musl/src/math/powerpc64/truncf.c", -"musl/src/math/powf.c", -"musl/src/math/powf_data.c", -"musl/src/math/powl.c", -"musl/src/math/remainder.c", -"musl/src/math/remainderf.c", -"musl/src/math/remainderl.c", -"musl/src/math/remquo.c", -"musl/src/math/remquof.c", -"musl/src/math/remquol.c", -"musl/src/math/rint.c", -"musl/src/math/rintf.c", -"musl/src/math/rintl.c", -"musl/src/math/riscv64/copysign.c", -"musl/src/math/riscv64/copysignf.c", -"musl/src/math/riscv64/fabs.c", -"musl/src/math/riscv64/fabsf.c", -"musl/src/math/riscv64/fma.c", -"musl/src/math/riscv64/fmaf.c", -"musl/src/math/riscv64/fmax.c", -"musl/src/math/riscv64/fmaxf.c", -"musl/src/math/riscv64/fmin.c", -"musl/src/math/riscv64/fminf.c", -"musl/src/math/riscv64/sqrt.c", -"musl/src/math/riscv64/sqrtf.c", -"musl/src/math/round.c", -"musl/src/math/roundf.c", -"musl/src/math/roundl.c", -"musl/src/math/s390x/ceil.c", -"musl/src/math/s390x/ceilf.c", -"musl/src/math/s390x/ceill.c", -"musl/src/math/s390x/fabs.c", -"musl/src/math/s390x/fabsf.c", -"musl/src/math/s390x/fabsl.c", -"musl/src/math/s390x/floor.c", -"musl/src/math/s390x/floorf.c", -"musl/src/math/s390x/floorl.c", -"musl/src/math/s390x/fma.c", -"musl/src/math/s390x/fmaf.c", -"musl/src/math/s390x/nearbyint.c", -"musl/src/math/s390x/nearbyintf.c", -"musl/src/math/s390x/nearbyintl.c", -"musl/src/math/s390x/rint.c", -"musl/src/math/s390x/rintf.c", -"musl/src/math/s390x/rintl.c", -"musl/src/math/s390x/round.c", -"musl/src/math/s390x/roundf.c", -"musl/src/math/s390x/roundl.c", -"musl/src/math/s390x/sqrt.c", -"musl/src/math/s390x/sqrtf.c", -"musl/src/math/s390x/sqrtl.c", -"musl/src/math/s390x/trunc.c", -"musl/src/math/s390x/truncf.c", -"musl/src/math/s390x/truncl.c", -"musl/src/math/scalb.c", -"musl/src/math/scalbf.c", -"musl/src/math/scalbln.c", -"musl/src/math/scalblnf.c", -"musl/src/math/scalblnl.c", -"musl/src/math/scalbn.c", -"musl/src/math/scalbnf.c", -"musl/src/math/scalbnl.c", -"musl/src/math/signgam.c", -"musl/src/math/significand.c", -"musl/src/math/significandf.c", -"musl/src/math/sin.c", -"musl/src/math/sincos.c", -"musl/src/math/sincosf.c", -"musl/src/math/sincosl.c", -"musl/src/math/sinf.c", -"musl/src/math/sinh.c", -"musl/src/math/sinhf.c", -"musl/src/math/sinhl.c", -"musl/src/math/sinl.c", -"musl/src/math/sqrt.c", -"musl/src/math/sqrtf.c", -"musl/src/math/sqrtl.c", -"musl/src/math/tan.c", -"musl/src/math/tanf.c", -"musl/src/math/tanh.c", -"musl/src/math/tanhf.c", -"musl/src/math/tanhl.c", -"musl/src/math/tanl.c", -"musl/src/math/tgamma.c", -"musl/src/math/tgammaf.c", -"musl/src/math/tgammal.c", -"musl/src/math/trunc.c", -"musl/src/math/truncf.c", -"musl/src/math/truncl.c", -"musl/src/math/x32/__invtrigl.s", -"musl/src/math/x32/acosl.s", -"musl/src/math/x32/asinl.s", -"musl/src/math/x32/atan2l.s", -"musl/src/math/x32/atanl.s", -"musl/src/math/x32/ceill.s", -"musl/src/math/x32/exp2l.s", -"musl/src/math/x32/expl.s", -"musl/src/math/x32/expm1l.s", -"musl/src/math/x32/fabs.s", -"musl/src/math/x32/fabsf.s", -"musl/src/math/x32/fabsl.s", -"musl/src/math/x32/floorl.s", -"musl/src/math/x32/fma.c", -"musl/src/math/x32/fmaf.c", -"musl/src/math/x32/fmodl.s", -"musl/src/math/x32/llrint.s", -"musl/src/math/x32/llrintf.s", -"musl/src/math/x32/llrintl.s", -"musl/src/math/x32/log10l.s", -"musl/src/math/x32/log1pl.s", -"musl/src/math/x32/log2l.s", -"musl/src/math/x32/logl.s", -"musl/src/math/x32/lrint.s", -"musl/src/math/x32/lrintf.s", -"musl/src/math/x32/lrintl.s", -"musl/src/math/x32/remainderl.s", -"musl/src/math/x32/rintl.s", -"musl/src/math/x32/sqrt.s", -"musl/src/math/x32/sqrtf.s", -"musl/src/math/x32/sqrtl.s", -"musl/src/math/x32/truncl.s", -"musl/src/math/x86_64/__invtrigl.s", -"musl/src/math/x86_64/acosl.s", -"musl/src/math/x86_64/asinl.s", -"musl/src/math/x86_64/atan2l.s", -"musl/src/math/x86_64/atanl.s", -"musl/src/math/x86_64/ceill.s", -"musl/src/math/x86_64/exp2l.s", -"musl/src/math/x86_64/expl.s", -"musl/src/math/x86_64/expm1l.s", -"musl/src/math/x86_64/fabs.s", -"musl/src/math/x86_64/fabsf.s", -"musl/src/math/x86_64/fabsl.s", -"musl/src/math/x86_64/floorl.s", -"musl/src/math/x86_64/fma.c", -"musl/src/math/x86_64/fmaf.c", -"musl/src/math/x86_64/fmodl.s", -"musl/src/math/x86_64/llrint.s", -"musl/src/math/x86_64/llrintf.s", -"musl/src/math/x86_64/llrintl.s", -"musl/src/math/x86_64/log10l.s", -"musl/src/math/x86_64/log1pl.s", -"musl/src/math/x86_64/log2l.s", -"musl/src/math/x86_64/logl.s", -"musl/src/math/x86_64/lrint.s", -"musl/src/math/x86_64/lrintf.s", -"musl/src/math/x86_64/lrintl.s", -"musl/src/math/x86_64/remainderl.s", -"musl/src/math/x86_64/rintl.s", -"musl/src/math/x86_64/sqrt.s", -"musl/src/math/x86_64/sqrtf.s", -"musl/src/math/x86_64/sqrtl.s", -"musl/src/math/x86_64/truncl.s", -"musl/src/misc/a64l.c", -"musl/src/misc/basename.c", -"musl/src/misc/dirname.c", -"musl/src/misc/ffs.c", -"musl/src/misc/ffsl.c", -"musl/src/misc/ffsll.c", -"musl/src/misc/fmtmsg.c", -"musl/src/misc/forkpty.c", -"musl/src/misc/get_current_dir_name.c", -"musl/src/misc/getauxval.c", -"musl/src/misc/getdomainname.c", -"musl/src/misc/getentropy.c", -"musl/src/misc/gethostid.c", -"musl/src/misc/getopt.c", -"musl/src/misc/getopt_long.c", -"musl/src/misc/getpriority.c", -"musl/src/misc/getresgid.c", -"musl/src/misc/getresuid.c", -"musl/src/misc/getrlimit.c", -"musl/src/misc/getrusage.c", -"musl/src/misc/getsubopt.c", -"musl/src/misc/initgroups.c", -"musl/src/misc/ioctl.c", -"musl/src/misc/issetugid.c", -"musl/src/misc/lockf.c", -"musl/src/misc/login_tty.c", -"musl/src/misc/mntent.c", -"musl/src/misc/nftw.c", -"musl/src/misc/openpty.c", -"musl/src/misc/ptsname.c", -"musl/src/misc/pty.c", -"musl/src/misc/realpath.c", -"musl/src/misc/setdomainname.c", -"musl/src/misc/setpriority.c", -"musl/src/misc/setrlimit.c", -"musl/src/misc/syscall.c", -"musl/src/misc/syslog.c", -"musl/src/misc/uname.c", -"musl/src/misc/wordexp.c", -"musl/src/mman/madvise.c", -"musl/src/mman/mincore.c", -"musl/src/mman/mlock.c", -"musl/src/mman/mlockall.c", -"musl/src/mman/mmap.c", -"musl/src/mman/mprotect.c", -"musl/src/mman/mremap.c", -"musl/src/mman/msync.c", -"musl/src/mman/munlock.c", -"musl/src/mman/munlockall.c", -"musl/src/mman/munmap.c", -"musl/src/mman/posix_madvise.c", -"musl/src/mman/shm_open.c", -"musl/src/mq/mq_close.c", -"musl/src/mq/mq_getattr.c", -"musl/src/mq/mq_notify.c", -"musl/src/mq/mq_open.c", -"musl/src/mq/mq_receive.c", -"musl/src/mq/mq_send.c", -"musl/src/mq/mq_setattr.c", -"musl/src/mq/mq_timedreceive.c", -"musl/src/mq/mq_timedsend.c", -"musl/src/mq/mq_unlink.c", -"musl/src/multibyte/btowc.c", -"musl/src/multibyte/c16rtomb.c", -"musl/src/multibyte/c32rtomb.c", -"musl/src/multibyte/internal.c", -"musl/src/multibyte/mblen.c", -"musl/src/multibyte/mbrlen.c", -"musl/src/multibyte/mbrtoc16.c", -"musl/src/multibyte/mbrtoc32.c", -"musl/src/multibyte/mbrtowc.c", -"musl/src/multibyte/mbsinit.c", -"musl/src/multibyte/mbsnrtowcs.c", -"musl/src/multibyte/mbsrtowcs.c", -"musl/src/multibyte/mbstowcs.c", -"musl/src/multibyte/mbtowc.c", -"musl/src/multibyte/wcrtomb.c", -"musl/src/multibyte/wcsnrtombs.c", -"musl/src/multibyte/wcsrtombs.c", -"musl/src/multibyte/wcstombs.c", -"musl/src/multibyte/wctob.c", -"musl/src/multibyte/wctomb.c", -"musl/src/network/accept.c", -"musl/src/network/accept4.c", -"musl/src/network/bind.c", -"musl/src/network/connect.c", -"musl/src/network/dn_comp.c", -"musl/src/network/dn_expand.c", -"musl/src/network/dn_skipname.c", -"musl/src/network/dns_parse.c", -"musl/src/network/ent.c", -"musl/src/network/ether.c", -"musl/src/network/freeaddrinfo.c", -"musl/src/network/gai_strerror.c", -"musl/src/network/getaddrinfo.c", -"musl/src/network/gethostbyaddr.c", -"musl/src/network/gethostbyaddr_r.c", -"musl/src/network/gethostbyname.c", -"musl/src/network/gethostbyname2.c", -"musl/src/network/gethostbyname2_r.c", -"musl/src/network/gethostbyname_r.c", -"musl/src/network/getifaddrs.c", -"musl/src/network/getnameinfo.c", -"musl/src/network/getpeername.c", -"musl/src/network/getservbyname.c", -"musl/src/network/getservbyname_r.c", -"musl/src/network/getservbyport.c", -"musl/src/network/getservbyport_r.c", -"musl/src/network/getsockname.c", -"musl/src/network/getsockopt.c", -"musl/src/network/h_errno.c", -"musl/src/network/herror.c", -"musl/src/network/hstrerror.c", -"musl/src/network/htonl.c", -"musl/src/network/htons.c", -"musl/src/network/if_freenameindex.c", -"musl/src/network/if_indextoname.c", -"musl/src/network/if_nameindex.c", -"musl/src/network/if_nametoindex.c", -"musl/src/network/in6addr_any.c", -"musl/src/network/in6addr_loopback.c", -"musl/src/network/inet_addr.c", -"musl/src/network/inet_aton.c", -"musl/src/network/inet_legacy.c", -"musl/src/network/inet_ntoa.c", -"musl/src/network/inet_ntop.c", -"musl/src/network/inet_pton.c", -"musl/src/network/listen.c", -"musl/src/network/lookup_ipliteral.c", -"musl/src/network/lookup_name.c", -"musl/src/network/lookup_serv.c", -"musl/src/network/netlink.c", -"musl/src/network/netname.c", -"musl/src/network/ns_parse.c", -"musl/src/network/ntohl.c", -"musl/src/network/ntohs.c", -"musl/src/network/proto.c", -"musl/src/network/recv.c", -"musl/src/network/recvfrom.c", -"musl/src/network/recvmmsg.c", -"musl/src/network/recvmsg.c", -"musl/src/network/res_init.c", -"musl/src/network/res_mkquery.c", -"musl/src/network/res_msend.c", -"musl/src/network/res_query.c", -"musl/src/network/res_querydomain.c", -"musl/src/network/res_send.c", -"musl/src/network/res_state.c", -"musl/src/network/resolvconf.c", -"musl/src/network/send.c", -"musl/src/network/sendmmsg.c", -"musl/src/network/sendmsg.c", -"musl/src/network/sendto.c", -"musl/src/network/serv.c", -"musl/src/network/setsockopt.c", -"musl/src/network/shutdown.c", -"musl/src/network/sockatmark.c", -"musl/src/network/socket.c", -"musl/src/network/socketpair.c", -"musl/src/passwd/fgetgrent.c", -"musl/src/passwd/fgetpwent.c", -"musl/src/passwd/fgetspent.c", -"musl/src/passwd/getgr_a.c", -"musl/src/passwd/getgr_r.c", -"musl/src/passwd/getgrent.c", -"musl/src/passwd/getgrent_a.c", -"musl/src/passwd/getgrouplist.c", -"musl/src/passwd/getpw_a.c", -"musl/src/passwd/getpw_r.c", -"musl/src/passwd/getpwent.c", -"musl/src/passwd/getpwent_a.c", -"musl/src/passwd/getspent.c", -"musl/src/passwd/getspnam.c", -"musl/src/passwd/getspnam_r.c", -"musl/src/passwd/lckpwdf.c", -"musl/src/passwd/nscd_query.c", -"musl/src/passwd/putgrent.c", -"musl/src/passwd/putpwent.c", -"musl/src/passwd/putspent.c", -"musl/src/prng/__rand48_step.c", -"musl/src/prng/__seed48.c", -"musl/src/prng/drand48.c", -"musl/src/prng/lcong48.c", -"musl/src/prng/lrand48.c", -"musl/src/prng/mrand48.c", -"musl/src/prng/rand.c", -"musl/src/prng/rand_r.c", -"musl/src/prng/random.c", -"musl/src/prng/seed48.c", -"musl/src/prng/srand48.c", -"musl/src/process/arm/vfork.s", -"musl/src/process/execl.c", -"musl/src/process/execle.c", -"musl/src/process/execlp.c", -"musl/src/process/execv.c", -"musl/src/process/execve.c", -"musl/src/process/execvp.c", -"musl/src/process/fexecve.c", -"musl/src/process/fork.c", -"musl/src/process/i386/vfork.s", -"musl/src/process/posix_spawn.c", -"musl/src/process/posix_spawn_file_actions_addchdir.c", -"musl/src/process/posix_spawn_file_actions_addclose.c", -"musl/src/process/posix_spawn_file_actions_adddup2.c", -"musl/src/process/posix_spawn_file_actions_addfchdir.c", -"musl/src/process/posix_spawn_file_actions_addopen.c", -"musl/src/process/posix_spawn_file_actions_destroy.c", -"musl/src/process/posix_spawn_file_actions_init.c", -"musl/src/process/posix_spawnattr_destroy.c", -"musl/src/process/posix_spawnattr_getflags.c", -"musl/src/process/posix_spawnattr_getpgroup.c", -"musl/src/process/posix_spawnattr_getsigdefault.c", -"musl/src/process/posix_spawnattr_getsigmask.c", -"musl/src/process/posix_spawnattr_init.c", -"musl/src/process/posix_spawnattr_sched.c", -"musl/src/process/posix_spawnattr_setflags.c", -"musl/src/process/posix_spawnattr_setpgroup.c", -"musl/src/process/posix_spawnattr_setsigdefault.c", -"musl/src/process/posix_spawnattr_setsigmask.c", -"musl/src/process/posix_spawnp.c", -"musl/src/process/s390x/vfork.s", -"musl/src/process/sh/vfork.s", -"musl/src/process/system.c", -"musl/src/process/vfork.c", -"musl/src/process/wait.c", -"musl/src/process/waitid.c", -"musl/src/process/waitpid.c", -"musl/src/process/x32/vfork.s", -"musl/src/process/x86_64/vfork.s", -"musl/src/regex/fnmatch.c", -"musl/src/regex/glob.c", -"musl/src/regex/regcomp.c", -"musl/src/regex/regerror.c", -"musl/src/regex/regexec.c", -"musl/src/regex/tre-mem.c", -"musl/src/sched/affinity.c", -"musl/src/sched/sched_cpucount.c", -"musl/src/sched/sched_get_priority_max.c", -"musl/src/sched/sched_getcpu.c", -"musl/src/sched/sched_getparam.c", -"musl/src/sched/sched_getscheduler.c", -"musl/src/sched/sched_rr_get_interval.c", -"musl/src/sched/sched_setparam.c", -"musl/src/sched/sched_setscheduler.c", -"musl/src/sched/sched_yield.c", -"musl/src/search/hsearch.c", -"musl/src/search/insque.c", -"musl/src/search/lsearch.c", -"musl/src/search/tdelete.c", -"musl/src/search/tdestroy.c", -"musl/src/search/tfind.c", -"musl/src/search/tsearch.c", -"musl/src/search/twalk.c", -"musl/src/select/poll.c", -"musl/src/select/pselect.c", -"musl/src/select/select.c", -"musl/src/setjmp/aarch64/longjmp.s", -"musl/src/setjmp/aarch64/setjmp.s", -"musl/src/setjmp/arm/longjmp.S", -"musl/src/setjmp/arm/setjmp.S", -"musl/src/setjmp/i386/longjmp.s", -"musl/src/setjmp/i386/setjmp.s", -"musl/src/setjmp/longjmp.c", -"musl/src/setjmp/m68k/longjmp.s", -"musl/src/setjmp/m68k/setjmp.s", -"musl/src/setjmp/microblaze/longjmp.s", -"musl/src/setjmp/microblaze/setjmp.s", -"musl/src/setjmp/mips/longjmp.S", -"musl/src/setjmp/mips/setjmp.S", -"musl/src/setjmp/mips64/longjmp.S", -"musl/src/setjmp/mips64/setjmp.S", -"musl/src/setjmp/mipsn32/longjmp.S", -"musl/src/setjmp/mipsn32/setjmp.S", -"musl/src/setjmp/or1k/longjmp.s", -"musl/src/setjmp/or1k/setjmp.s", -"musl/src/setjmp/powerpc/longjmp.S", -"musl/src/setjmp/powerpc/setjmp.S", -"musl/src/setjmp/powerpc64/longjmp.s", -"musl/src/setjmp/powerpc64/setjmp.s", -"musl/src/setjmp/riscv64/longjmp.S", -"musl/src/setjmp/riscv64/setjmp.S", -"musl/src/setjmp/s390x/longjmp.s", -"musl/src/setjmp/s390x/setjmp.s", -"musl/src/setjmp/setjmp.c", -"musl/src/setjmp/sh/longjmp.S", -"musl/src/setjmp/sh/setjmp.S", -"musl/src/setjmp/x32/longjmp.s", -"musl/src/setjmp/x32/setjmp.s", -"musl/src/setjmp/x86_64/longjmp.s", -"musl/src/setjmp/x86_64/setjmp.s", -"musl/src/signal/aarch64/restore.s", -"musl/src/signal/aarch64/sigsetjmp.s", -"musl/src/signal/arm/restore.s", -"musl/src/signal/arm/sigsetjmp.s", -"musl/src/signal/block.c", -"musl/src/signal/getitimer.c", -"musl/src/signal/i386/restore.s", -"musl/src/signal/i386/sigsetjmp.s", -"musl/src/signal/kill.c", -"musl/src/signal/killpg.c", -"musl/src/signal/m68k/sigsetjmp.s", -"musl/src/signal/microblaze/restore.s", -"musl/src/signal/microblaze/sigsetjmp.s", -"musl/src/signal/mips/restore.s", -"musl/src/signal/mips/sigsetjmp.s", -"musl/src/signal/mips64/restore.s", -"musl/src/signal/mips64/sigsetjmp.s", -"musl/src/signal/mipsn32/restore.s", -"musl/src/signal/mipsn32/sigsetjmp.s", -"musl/src/signal/or1k/sigsetjmp.s", -"musl/src/signal/powerpc/restore.s", -"musl/src/signal/powerpc/sigsetjmp.s", -"musl/src/signal/powerpc64/restore.s", -"musl/src/signal/powerpc64/sigsetjmp.s", -"musl/src/signal/psiginfo.c", -"musl/src/signal/psignal.c", -"musl/src/signal/raise.c", -"musl/src/signal/restore.c", -"musl/src/signal/riscv64/restore.s", -"musl/src/signal/riscv64/sigsetjmp.s", -"musl/src/signal/s390x/restore.s", -"musl/src/signal/s390x/sigsetjmp.s", -"musl/src/signal/setitimer.c", -"musl/src/signal/sh/restore.s", -"musl/src/signal/sh/sigsetjmp.s", -"musl/src/signal/sigaction.c", -"musl/src/signal/sigaddset.c", -"musl/src/signal/sigaltstack.c", -"musl/src/signal/sigandset.c", -"musl/src/signal/sigdelset.c", -"musl/src/signal/sigemptyset.c", -"musl/src/signal/sigfillset.c", -"musl/src/signal/sighold.c", -"musl/src/signal/sigignore.c", -"musl/src/signal/siginterrupt.c", -"musl/src/signal/sigisemptyset.c", -"musl/src/signal/sigismember.c", -"musl/src/signal/siglongjmp.c", -"musl/src/signal/signal.c", -"musl/src/signal/sigorset.c", -"musl/src/signal/sigpause.c", -"musl/src/signal/sigpending.c", -"musl/src/signal/sigprocmask.c", -"musl/src/signal/sigqueue.c", -"musl/src/signal/sigrelse.c", -"musl/src/signal/sigrtmax.c", -"musl/src/signal/sigrtmin.c", -"musl/src/signal/sigset.c", -"musl/src/signal/sigsetjmp.c", -"musl/src/signal/sigsetjmp_tail.c", -"musl/src/signal/sigsuspend.c", -"musl/src/signal/sigtimedwait.c", -"musl/src/signal/sigwait.c", -"musl/src/signal/sigwaitinfo.c", -"musl/src/signal/x32/getitimer.c", -"musl/src/signal/x32/restore.s", -"musl/src/signal/x32/setitimer.c", -"musl/src/signal/x32/sigsetjmp.s", -"musl/src/signal/x86_64/restore.s", -"musl/src/signal/x86_64/sigsetjmp.s", -"musl/src/stat/__xstat.c", -"musl/src/stat/chmod.c", -"musl/src/stat/fchmod.c", -"musl/src/stat/fchmodat.c", -"musl/src/stat/fstat.c", -"musl/src/stat/fstatat.c", -"musl/src/stat/futimens.c", -"musl/src/stat/futimesat.c", -"musl/src/stat/lchmod.c", -"musl/src/stat/lstat.c", -"musl/src/stat/mkdir.c", -"musl/src/stat/mkdirat.c", -"musl/src/stat/mkfifo.c", -"musl/src/stat/mkfifoat.c", -"musl/src/stat/mknod.c", -"musl/src/stat/mknodat.c", -"musl/src/stat/stat.c", -"musl/src/stat/statvfs.c", -"musl/src/stat/umask.c", -"musl/src/stat/utimensat.c", -"musl/src/stdio/__fclose_ca.c", -"musl/src/stdio/__fdopen.c", -"musl/src/stdio/__fmodeflags.c", -"musl/src/stdio/__fopen_rb_ca.c", -"musl/src/stdio/__lockfile.c", -"musl/src/stdio/__overflow.c", -"musl/src/stdio/__stdio_close.c", -"musl/src/stdio/__stdio_exit.c", -"musl/src/stdio/__stdio_read.c", -"musl/src/stdio/__stdio_seek.c", -"musl/src/stdio/__stdio_write.c", -"musl/src/stdio/__stdout_write.c", -"musl/src/stdio/__string_read.c", -"musl/src/stdio/__toread.c", -"musl/src/stdio/__towrite.c", -"musl/src/stdio/__uflow.c", -"musl/src/stdio/asprintf.c", -"musl/src/stdio/clearerr.c", -"musl/src/stdio/dprintf.c", -"musl/src/stdio/ext.c", -"musl/src/stdio/ext2.c", -"musl/src/stdio/fclose.c", -"musl/src/stdio/feof.c", -"musl/src/stdio/ferror.c", -"musl/src/stdio/fflush.c", -"musl/src/stdio/fgetc.c", -"musl/src/stdio/fgetln.c", -"musl/src/stdio/fgetpos.c", -"musl/src/stdio/fgets.c", -"musl/src/stdio/fgetwc.c", -"musl/src/stdio/fgetws.c", -"musl/src/stdio/fileno.c", -"musl/src/stdio/flockfile.c", -"musl/src/stdio/fmemopen.c", -"musl/src/stdio/fopen.c", -"musl/src/stdio/fopencookie.c", -"musl/src/stdio/fprintf.c", -"musl/src/stdio/fputc.c", -"musl/src/stdio/fputs.c", -"musl/src/stdio/fputwc.c", -"musl/src/stdio/fputws.c", -"musl/src/stdio/fread.c", -"musl/src/stdio/freopen.c", -"musl/src/stdio/fscanf.c", -"musl/src/stdio/fseek.c", -"musl/src/stdio/fsetpos.c", -"musl/src/stdio/ftell.c", -"musl/src/stdio/ftrylockfile.c", -"musl/src/stdio/funlockfile.c", -"musl/src/stdio/fwide.c", -"musl/src/stdio/fwprintf.c", -"musl/src/stdio/fwrite.c", -"musl/src/stdio/fwscanf.c", -"musl/src/stdio/getc.c", -"musl/src/stdio/getc_unlocked.c", -"musl/src/stdio/getchar.c", -"musl/src/stdio/getchar_unlocked.c", -"musl/src/stdio/getdelim.c", -"musl/src/stdio/getline.c", -"musl/src/stdio/gets.c", -"musl/src/stdio/getw.c", -"musl/src/stdio/getwc.c", -"musl/src/stdio/getwchar.c", -"musl/src/stdio/ofl.c", -"musl/src/stdio/ofl_add.c", -"musl/src/stdio/open_memstream.c", -"musl/src/stdio/open_wmemstream.c", -"musl/src/stdio/pclose.c", -"musl/src/stdio/perror.c", -"musl/src/stdio/popen.c", -"musl/src/stdio/printf.c", -"musl/src/stdio/putc.c", -"musl/src/stdio/putc_unlocked.c", -"musl/src/stdio/putchar.c", -"musl/src/stdio/putchar_unlocked.c", -"musl/src/stdio/puts.c", -"musl/src/stdio/putw.c", -"musl/src/stdio/putwc.c", -"musl/src/stdio/putwchar.c", -"musl/src/stdio/remove.c", -"musl/src/stdio/rename.c", -"musl/src/stdio/rewind.c", -"musl/src/stdio/scanf.c", -"musl/src/stdio/setbuf.c", -"musl/src/stdio/setbuffer.c", -"musl/src/stdio/setlinebuf.c", -"musl/src/stdio/setvbuf.c", -"musl/src/stdio/snprintf.c", -"musl/src/stdio/sprintf.c", -"musl/src/stdio/sscanf.c", -"musl/src/stdio/stderr.c", -"musl/src/stdio/stdin.c", -"musl/src/stdio/stdout.c", -"musl/src/stdio/swprintf.c", -"musl/src/stdio/swscanf.c", -"musl/src/stdio/tempnam.c", -"musl/src/stdio/tmpfile.c", -"musl/src/stdio/tmpnam.c", -"musl/src/stdio/ungetc.c", -"musl/src/stdio/ungetwc.c", -"musl/src/stdio/vasprintf.c", -"musl/src/stdio/vdprintf.c", -"musl/src/stdio/vfprintf.c", -"musl/src/stdio/vfscanf.c", -"musl/src/stdio/vfwprintf.c", -"musl/src/stdio/vfwscanf.c", -"musl/src/stdio/vprintf.c", -"musl/src/stdio/vscanf.c", -"musl/src/stdio/vsnprintf.c", -"musl/src/stdio/vsprintf.c", -"musl/src/stdio/vsscanf.c", -"musl/src/stdio/vswprintf.c", -"musl/src/stdio/vswscanf.c", -"musl/src/stdio/vwprintf.c", -"musl/src/stdio/vwscanf.c", -"musl/src/stdio/wprintf.c", -"musl/src/stdio/wscanf.c", -"musl/src/stdlib/abs.c", -"musl/src/stdlib/atof.c", -"musl/src/stdlib/atoi.c", -"musl/src/stdlib/atol.c", -"musl/src/stdlib/atoll.c", -"musl/src/stdlib/bsearch.c", -"musl/src/stdlib/div.c", -"musl/src/stdlib/ecvt.c", -"musl/src/stdlib/fcvt.c", -"musl/src/stdlib/gcvt.c", -"musl/src/stdlib/imaxabs.c", -"musl/src/stdlib/imaxdiv.c", -"musl/src/stdlib/labs.c", -"musl/src/stdlib/ldiv.c", -"musl/src/stdlib/llabs.c", -"musl/src/stdlib/lldiv.c", -"musl/src/stdlib/qsort.c", -"musl/src/stdlib/strtod.c", -"musl/src/stdlib/strtol.c", -"musl/src/stdlib/wcstod.c", -"musl/src/stdlib/wcstol.c", -"musl/src/string/arm/__aeabi_memcpy.s", -"musl/src/string/arm/__aeabi_memset.s", -"musl/src/string/arm/memcpy.c", -"musl/src/string/arm/memcpy_le.S", -"musl/src/string/bcmp.c", -"musl/src/string/bcopy.c", -"musl/src/string/bzero.c", -"musl/src/string/explicit_bzero.c", -"musl/src/string/i386/memcpy.s", -"musl/src/string/i386/memmove.s", -"musl/src/string/i386/memset.s", -"musl/src/string/index.c", -"musl/src/string/memccpy.c", -"musl/src/string/memchr.c", -"musl/src/string/memcmp.c", -"musl/src/string/memcpy.c", -"musl/src/string/memmem.c", -"musl/src/string/memmove.c", -"musl/src/string/mempcpy.c", -"musl/src/string/memrchr.c", -"musl/src/string/memset.c", -"musl/src/string/rindex.c", -"musl/src/string/stpcpy.c", -"musl/src/string/stpncpy.c", -"musl/src/string/strcasecmp.c", -"musl/src/string/strcasestr.c", -"musl/src/string/strcat.c", -"musl/src/string/strchr.c", -"musl/src/string/strchrnul.c", -"musl/src/string/strcmp.c", -"musl/src/string/strcpy.c", -"musl/src/string/strcspn.c", -"musl/src/string/strdup.c", -"musl/src/string/strerror_r.c", -"musl/src/string/strlcat.c", -"musl/src/string/strlcpy.c", -"musl/src/string/strlen.c", -"musl/src/string/strncasecmp.c", -"musl/src/string/strncat.c", -"musl/src/string/strncmp.c", -"musl/src/string/strncpy.c", -"musl/src/string/strndup.c", -"musl/src/string/strnlen.c", -"musl/src/string/strpbrk.c", -"musl/src/string/strrchr.c", -"musl/src/string/strsep.c", -"musl/src/string/strsignal.c", -"musl/src/string/strspn.c", -"musl/src/string/strstr.c", -"musl/src/string/strtok.c", -"musl/src/string/strtok_r.c", -"musl/src/string/strverscmp.c", -"musl/src/string/swab.c", -"musl/src/string/wcpcpy.c", -"musl/src/string/wcpncpy.c", -"musl/src/string/wcscasecmp.c", -"musl/src/string/wcscasecmp_l.c", -"musl/src/string/wcscat.c", -"musl/src/string/wcschr.c", -"musl/src/string/wcscmp.c", -"musl/src/string/wcscpy.c", -"musl/src/string/wcscspn.c", -"musl/src/string/wcsdup.c", -"musl/src/string/wcslen.c", -"musl/src/string/wcsncasecmp.c", -"musl/src/string/wcsncasecmp_l.c", -"musl/src/string/wcsncat.c", -"musl/src/string/wcsncmp.c", -"musl/src/string/wcsncpy.c", -"musl/src/string/wcsnlen.c", -"musl/src/string/wcspbrk.c", -"musl/src/string/wcsrchr.c", -"musl/src/string/wcsspn.c", -"musl/src/string/wcsstr.c", -"musl/src/string/wcstok.c", -"musl/src/string/wcswcs.c", -"musl/src/string/wmemchr.c", -"musl/src/string/wmemcmp.c", -"musl/src/string/wmemcpy.c", -"musl/src/string/wmemmove.c", -"musl/src/string/wmemset.c", -"musl/src/string/x86_64/memcpy.s", -"musl/src/string/x86_64/memmove.s", -"musl/src/string/x86_64/memset.s", -"musl/src/temp/__randname.c", -"musl/src/temp/mkdtemp.c", -"musl/src/temp/mkostemp.c", -"musl/src/temp/mkostemps.c", -"musl/src/temp/mkstemp.c", -"musl/src/temp/mkstemps.c", -"musl/src/temp/mktemp.c", -"musl/src/termios/cfgetospeed.c", -"musl/src/termios/cfmakeraw.c", -"musl/src/termios/cfsetospeed.c", -"musl/src/termios/tcdrain.c", -"musl/src/termios/tcflow.c", -"musl/src/termios/tcflush.c", -"musl/src/termios/tcgetattr.c", -"musl/src/termios/tcgetsid.c", -"musl/src/termios/tcsendbreak.c", -"musl/src/termios/tcsetattr.c", -"musl/src/thread/__lock.c", -"musl/src/thread/__set_thread_area.c", -"musl/src/thread/__syscall_cp.c", -"musl/src/thread/__timedwait.c", -"musl/src/thread/__tls_get_addr.c", -"musl/src/thread/__unmapself.c", -"musl/src/thread/__wait.c", -"musl/src/thread/aarch64/__set_thread_area.s", -"musl/src/thread/aarch64/__unmapself.s", -"musl/src/thread/aarch64/clone.s", -"musl/src/thread/aarch64/syscall_cp.s", -"musl/src/thread/arm/__aeabi_read_tp.s", -"musl/src/thread/arm/__set_thread_area.c", -"musl/src/thread/arm/__unmapself.s", -"musl/src/thread/arm/atomics.s", -"musl/src/thread/arm/clone.s", -"musl/src/thread/arm/syscall_cp.s", -"musl/src/thread/call_once.c", -"musl/src/thread/clone.c", -"musl/src/thread/cnd_broadcast.c", -"musl/src/thread/cnd_destroy.c", -"musl/src/thread/cnd_init.c", -"musl/src/thread/cnd_signal.c", -"musl/src/thread/cnd_timedwait.c", -"musl/src/thread/cnd_wait.c", -"musl/src/thread/default_attr.c", -"musl/src/thread/i386/__set_thread_area.s", -"musl/src/thread/i386/__unmapself.s", -"musl/src/thread/i386/clone.s", -"musl/src/thread/i386/syscall_cp.s", -"musl/src/thread/i386/tls.s", -"musl/src/thread/lock_ptc.c", -"musl/src/thread/m68k/__m68k_read_tp.s", -"musl/src/thread/m68k/clone.s", -"musl/src/thread/m68k/syscall_cp.s", -"musl/src/thread/microblaze/__set_thread_area.s", -"musl/src/thread/microblaze/__unmapself.s", -"musl/src/thread/microblaze/clone.s", -"musl/src/thread/microblaze/syscall_cp.s", -"musl/src/thread/mips/__unmapself.s", -"musl/src/thread/mips/clone.s", -"musl/src/thread/mips/syscall_cp.s", -"musl/src/thread/mips64/__unmapself.s", -"musl/src/thread/mips64/clone.s", -"musl/src/thread/mips64/syscall_cp.s", -"musl/src/thread/mipsn32/__unmapself.s", -"musl/src/thread/mipsn32/clone.s", -"musl/src/thread/mipsn32/syscall_cp.s", -"musl/src/thread/mtx_destroy.c", -"musl/src/thread/mtx_init.c", -"musl/src/thread/mtx_lock.c", -"musl/src/thread/mtx_timedlock.c", -"musl/src/thread/mtx_trylock.c", -"musl/src/thread/mtx_unlock.c", -"musl/src/thread/or1k/__set_thread_area.s", -"musl/src/thread/or1k/__unmapself.s", -"musl/src/thread/or1k/clone.s", -"musl/src/thread/or1k/syscall_cp.s", -"musl/src/thread/powerpc/__set_thread_area.s", -"musl/src/thread/powerpc/__unmapself.s", -"musl/src/thread/powerpc/clone.s", -"musl/src/thread/powerpc/syscall_cp.s", -"musl/src/thread/powerpc64/__set_thread_area.s", -"musl/src/thread/powerpc64/__unmapself.s", -"musl/src/thread/powerpc64/clone.s", -"musl/src/thread/powerpc64/syscall_cp.s", -"musl/src/thread/pthread_atfork.c", -"musl/src/thread/pthread_attr_destroy.c", -"musl/src/thread/pthread_attr_get.c", -"musl/src/thread/pthread_attr_init.c", -"musl/src/thread/pthread_attr_setdetachstate.c", -"musl/src/thread/pthread_attr_setguardsize.c", -"musl/src/thread/pthread_attr_setinheritsched.c", -"musl/src/thread/pthread_attr_setschedparam.c", -"musl/src/thread/pthread_attr_setschedpolicy.c", -"musl/src/thread/pthread_attr_setscope.c", -"musl/src/thread/pthread_attr_setstack.c", -"musl/src/thread/pthread_attr_setstacksize.c", -"musl/src/thread/pthread_barrier_destroy.c", -"musl/src/thread/pthread_barrier_init.c", -"musl/src/thread/pthread_barrier_wait.c", -"musl/src/thread/pthread_barrierattr_destroy.c", -"musl/src/thread/pthread_barrierattr_init.c", -"musl/src/thread/pthread_barrierattr_setpshared.c", -"musl/src/thread/pthread_cancel.c", -"musl/src/thread/pthread_cleanup_push.c", -"musl/src/thread/pthread_cond_broadcast.c", -"musl/src/thread/pthread_cond_destroy.c", -"musl/src/thread/pthread_cond_init.c", -"musl/src/thread/pthread_cond_signal.c", -"musl/src/thread/pthread_cond_timedwait.c", -"musl/src/thread/pthread_cond_wait.c", -"musl/src/thread/pthread_condattr_destroy.c", -"musl/src/thread/pthread_condattr_init.c", -"musl/src/thread/pthread_condattr_setclock.c", -"musl/src/thread/pthread_condattr_setpshared.c", -"musl/src/thread/pthread_create.c", -"musl/src/thread/pthread_detach.c", -"musl/src/thread/pthread_equal.c", -"musl/src/thread/pthread_getattr_np.c", -"musl/src/thread/pthread_getconcurrency.c", -"musl/src/thread/pthread_getcpuclockid.c", -"musl/src/thread/pthread_getschedparam.c", -"musl/src/thread/pthread_getspecific.c", -"musl/src/thread/pthread_join.c", -"musl/src/thread/pthread_key_create.c", -"musl/src/thread/pthread_kill.c", -"musl/src/thread/pthread_mutex_consistent.c", -"musl/src/thread/pthread_mutex_destroy.c", -"musl/src/thread/pthread_mutex_getprioceiling.c", -"musl/src/thread/pthread_mutex_init.c", -"musl/src/thread/pthread_mutex_lock.c", -"musl/src/thread/pthread_mutex_setprioceiling.c", -"musl/src/thread/pthread_mutex_timedlock.c", -"musl/src/thread/pthread_mutex_trylock.c", -"musl/src/thread/pthread_mutex_unlock.c", -"musl/src/thread/pthread_mutexattr_destroy.c", -"musl/src/thread/pthread_mutexattr_init.c", -"musl/src/thread/pthread_mutexattr_setprotocol.c", -"musl/src/thread/pthread_mutexattr_setpshared.c", -"musl/src/thread/pthread_mutexattr_setrobust.c", -"musl/src/thread/pthread_mutexattr_settype.c", -"musl/src/thread/pthread_once.c", -"musl/src/thread/pthread_rwlock_destroy.c", -"musl/src/thread/pthread_rwlock_init.c", -"musl/src/thread/pthread_rwlock_rdlock.c", -"musl/src/thread/pthread_rwlock_timedrdlock.c", -"musl/src/thread/pthread_rwlock_timedwrlock.c", -"musl/src/thread/pthread_rwlock_tryrdlock.c", -"musl/src/thread/pthread_rwlock_trywrlock.c", -"musl/src/thread/pthread_rwlock_unlock.c", -"musl/src/thread/pthread_rwlock_wrlock.c", -"musl/src/thread/pthread_rwlockattr_destroy.c", -"musl/src/thread/pthread_rwlockattr_init.c", -"musl/src/thread/pthread_rwlockattr_setpshared.c", -"musl/src/thread/pthread_self.c", -"musl/src/thread/pthread_setattr_default_np.c", -"musl/src/thread/pthread_setcancelstate.c", -"musl/src/thread/pthread_setcanceltype.c", -"musl/src/thread/pthread_setconcurrency.c", -"musl/src/thread/pthread_setname_np.c", -"musl/src/thread/pthread_setschedparam.c", -"musl/src/thread/pthread_setschedprio.c", -"musl/src/thread/pthread_setspecific.c", -"musl/src/thread/pthread_sigmask.c", -"musl/src/thread/pthread_spin_destroy.c", -"musl/src/thread/pthread_spin_init.c", -"musl/src/thread/pthread_spin_lock.c", -"musl/src/thread/pthread_spin_trylock.c", -"musl/src/thread/pthread_spin_unlock.c", -"musl/src/thread/pthread_testcancel.c", -"musl/src/thread/riscv64/__set_thread_area.s", -"musl/src/thread/riscv64/__unmapself.s", -"musl/src/thread/riscv64/clone.s", -"musl/src/thread/riscv64/syscall_cp.s", -"musl/src/thread/s390x/__set_thread_area.s", -"musl/src/thread/s390x/__tls_get_offset.s", -"musl/src/thread/s390x/__unmapself.s", -"musl/src/thread/s390x/clone.s", -"musl/src/thread/s390x/syscall_cp.s", -"musl/src/thread/sem_destroy.c", -"musl/src/thread/sem_getvalue.c", -"musl/src/thread/sem_init.c", -"musl/src/thread/sem_open.c", -"musl/src/thread/sem_post.c", -"musl/src/thread/sem_timedwait.c", -"musl/src/thread/sem_trywait.c", -"musl/src/thread/sem_unlink.c", -"musl/src/thread/sem_wait.c", -"musl/src/thread/sh/__set_thread_area.c", -"musl/src/thread/sh/__unmapself.c", -"musl/src/thread/sh/__unmapself_mmu.s", -"musl/src/thread/sh/atomics.s", -"musl/src/thread/sh/clone.s", -"musl/src/thread/sh/syscall_cp.s", -"musl/src/thread/synccall.c", -"musl/src/thread/syscall_cp.c", -"musl/src/thread/thrd_create.c", -"musl/src/thread/thrd_exit.c", -"musl/src/thread/thrd_join.c", -"musl/src/thread/thrd_sleep.c", -"musl/src/thread/thrd_yield.c", -"musl/src/thread/tls.c", -"musl/src/thread/tss_create.c", -"musl/src/thread/tss_delete.c", -"musl/src/thread/tss_set.c", -"musl/src/thread/vmlock.c", -"musl/src/thread/x32/__set_thread_area.s", -"musl/src/thread/x32/__unmapself.s", -"musl/src/thread/x32/clone.s", -"musl/src/thread/x32/syscall_cp.s", -"musl/src/thread/x86_64/__set_thread_area.s", -"musl/src/thread/x86_64/__unmapself.s", -"musl/src/thread/x86_64/clone.s", -"musl/src/thread/x86_64/syscall_cp.s", -"musl/src/time/__map_file.c", -"musl/src/time/__month_to_secs.c", -"musl/src/time/__secs_to_tm.c", -"musl/src/time/__tm_to_secs.c", -"musl/src/time/__tz.c", -"musl/src/time/__year_to_secs.c", -"musl/src/time/asctime.c", -"musl/src/time/asctime_r.c", -"musl/src/time/clock.c", -"musl/src/time/clock_getcpuclockid.c", -"musl/src/time/clock_getres.c", -"musl/src/time/clock_gettime.c", -"musl/src/time/clock_nanosleep.c", -"musl/src/time/clock_settime.c", -"musl/src/time/ctime.c", -"musl/src/time/ctime_r.c", -"musl/src/time/difftime.c", -"musl/src/time/ftime.c", -"musl/src/time/getdate.c", -"musl/src/time/gettimeofday.c", -"musl/src/time/gmtime.c", -"musl/src/time/gmtime_r.c", -"musl/src/time/localtime.c", -"musl/src/time/localtime_r.c", -"musl/src/time/mktime.c", -"musl/src/time/nanosleep.c", -"musl/src/time/strftime.c", -"musl/src/time/strptime.c", -"musl/src/time/time.c", -"musl/src/time/timegm.c", -"musl/src/time/timer_create.c", -"musl/src/time/timer_delete.c", -"musl/src/time/timer_getoverrun.c", -"musl/src/time/timer_gettime.c", -"musl/src/time/timer_settime.c", -"musl/src/time/times.c", -"musl/src/time/timespec_get.c", -"musl/src/time/utime.c", -"musl/src/time/wcsftime.c", -"musl/src/unistd/_exit.c", -"musl/src/unistd/access.c", -"musl/src/unistd/acct.c", -"musl/src/unistd/alarm.c", -"musl/src/unistd/chdir.c", -"musl/src/unistd/chown.c", -"musl/src/unistd/close.c", -"musl/src/unistd/ctermid.c", -"musl/src/unistd/dup.c", -"musl/src/unistd/dup2.c", -"musl/src/unistd/dup3.c", -"musl/src/unistd/faccessat.c", -"musl/src/unistd/fchdir.c", -"musl/src/unistd/fchown.c", -"musl/src/unistd/fchownat.c", -"musl/src/unistd/fdatasync.c", -"musl/src/unistd/fsync.c", -"musl/src/unistd/ftruncate.c", -"musl/src/unistd/getcwd.c", -"musl/src/unistd/getegid.c", -"musl/src/unistd/geteuid.c", -"musl/src/unistd/getgid.c", -"musl/src/unistd/getgroups.c", -"musl/src/unistd/gethostname.c", -"musl/src/unistd/getlogin.c", -"musl/src/unistd/getlogin_r.c", -"musl/src/unistd/getpgid.c", -"musl/src/unistd/getpgrp.c", -"musl/src/unistd/getpid.c", -"musl/src/unistd/getppid.c", -"musl/src/unistd/getsid.c", -"musl/src/unistd/getuid.c", -"musl/src/unistd/isatty.c", -"musl/src/unistd/lchown.c", -"musl/src/unistd/link.c", -"musl/src/unistd/linkat.c", -"musl/src/unistd/lseek.c", -"musl/src/unistd/mips/pipe.s", -"musl/src/unistd/mips64/pipe.s", -"musl/src/unistd/mipsn32/lseek.c", -"musl/src/unistd/mipsn32/pipe.s", -"musl/src/unistd/nice.c", -"musl/src/unistd/pause.c", -"musl/src/unistd/pipe.c", -"musl/src/unistd/pipe2.c", -"musl/src/unistd/posix_close.c", -"musl/src/unistd/pread.c", -"musl/src/unistd/preadv.c", -"musl/src/unistd/pwrite.c", -"musl/src/unistd/pwritev.c", -"musl/src/unistd/read.c", -"musl/src/unistd/readlink.c", -"musl/src/unistd/readlinkat.c", -"musl/src/unistd/readv.c", -"musl/src/unistd/renameat.c", -"musl/src/unistd/rmdir.c", -"musl/src/unistd/setegid.c", -"musl/src/unistd/seteuid.c", -"musl/src/unistd/setgid.c", -"musl/src/unistd/setpgid.c", -"musl/src/unistd/setpgrp.c", -"musl/src/unistd/setregid.c", -"musl/src/unistd/setresgid.c", -"musl/src/unistd/setresuid.c", -"musl/src/unistd/setreuid.c", -"musl/src/unistd/setsid.c", -"musl/src/unistd/setuid.c", -"musl/src/unistd/setxid.c", -"musl/src/unistd/sh/pipe.s", -"musl/src/unistd/sleep.c", -"musl/src/unistd/symlink.c", -"musl/src/unistd/symlinkat.c", -"musl/src/unistd/sync.c", -"musl/src/unistd/tcgetpgrp.c", -"musl/src/unistd/tcsetpgrp.c", -"musl/src/unistd/truncate.c", -"musl/src/unistd/ttyname.c", -"musl/src/unistd/ttyname_r.c", -"musl/src/unistd/ualarm.c", -"musl/src/unistd/unlink.c", -"musl/src/unistd/unlinkat.c", -"musl/src/unistd/usleep.c", -"musl/src/unistd/write.c", -"musl/src/unistd/writev.c", -"musl/src/unistd/x32/lseek.c", -}; -static const char *ZIG_MUSL_COMPAT_TIME32_FILES[] = { -"musl/compat/time32/__xstat.c", -"musl/compat/time32/adjtime32.c", -"musl/compat/time32/adjtimex_time32.c", -"musl/compat/time32/aio_suspend_time32.c", -"musl/compat/time32/clock_adjtime32.c", -"musl/compat/time32/clock_getres_time32.c", -"musl/compat/time32/clock_gettime32.c", -"musl/compat/time32/clock_nanosleep_time32.c", -"musl/compat/time32/clock_settime32.c", -"musl/compat/time32/cnd_timedwait_time32.c", -"musl/compat/time32/ctime32.c", -"musl/compat/time32/ctime32_r.c", -"musl/compat/time32/difftime32.c", -"musl/compat/time32/fstat_time32.c", -"musl/compat/time32/fstatat_time32.c", -"musl/compat/time32/ftime32.c", -"musl/compat/time32/futimens_time32.c", -"musl/compat/time32/futimes_time32.c", -"musl/compat/time32/futimesat_time32.c", -"musl/compat/time32/getitimer_time32.c", -"musl/compat/time32/getrusage_time32.c", -"musl/compat/time32/gettimeofday_time32.c", -"musl/compat/time32/gmtime32.c", -"musl/compat/time32/gmtime32_r.c", -"musl/compat/time32/localtime32.c", -"musl/compat/time32/localtime32_r.c", -"musl/compat/time32/lstat_time32.c", -"musl/compat/time32/lutimes_time32.c", -"musl/compat/time32/mktime32.c", -"musl/compat/time32/mq_timedreceive_time32.c", -"musl/compat/time32/mq_timedsend_time32.c", -"musl/compat/time32/mtx_timedlock_time32.c", -"musl/compat/time32/nanosleep_time32.c", -"musl/compat/time32/ppoll_time32.c", -"musl/compat/time32/pselect_time32.c", -"musl/compat/time32/pthread_cond_timedwait_time32.c", -"musl/compat/time32/pthread_mutex_timedlock_time32.c", -"musl/compat/time32/pthread_rwlock_timedrdlock_time32.c", -"musl/compat/time32/pthread_rwlock_timedwrlock_time32.c", -"musl/compat/time32/pthread_timedjoin_np_time32.c", -"musl/compat/time32/recvmmsg_time32.c", -"musl/compat/time32/sched_rr_get_interval_time32.c", -"musl/compat/time32/select_time32.c", -"musl/compat/time32/sem_timedwait_time32.c", -"musl/compat/time32/semtimedop_time32.c", -"musl/compat/time32/setitimer_time32.c", -"musl/compat/time32/settimeofday_time32.c", -"musl/compat/time32/sigtimedwait_time32.c", -"musl/compat/time32/stat_time32.c", -"musl/compat/time32/stime32.c", -"musl/compat/time32/thrd_sleep_time32.c", -"musl/compat/time32/time32.c", -"musl/compat/time32/time32gm.c", -"musl/compat/time32/timer_gettime32.c", -"musl/compat/time32/timer_settime32.c", -"musl/compat/time32/timerfd_gettime32.c", -"musl/compat/time32/timerfd_settime32.c", -"musl/compat/time32/timespec_get_time32.c", -"musl/compat/time32/utime_time32.c", -"musl/compat/time32/utimensat_time32.c", -"musl/compat/time32/utimes_time32.c", -"musl/compat/time32/wait3_time32.c", -"musl/compat/time32/wait4_time32.c", -}; -static const char *ZIG_LIBCXXABI_FILES[] = { -"src/abort_message.cpp", -"src/cxa_aux_runtime.cpp", -"src/cxa_default_handlers.cpp", -"src/cxa_demangle.cpp", -"src/cxa_exception.cpp", -"src/cxa_exception_storage.cpp", -"src/cxa_guard.cpp", -"src/cxa_handlers.cpp", -"src/cxa_noexception.cpp", -"src/cxa_personality.cpp", -"src/cxa_thread_atexit.cpp", -"src/cxa_unexpected.cpp", -"src/cxa_vector.cpp", -"src/cxa_virtual.cpp", -"src/fallback_malloc.cpp", -"src/private_typeinfo.cpp", -"src/stdlib_exception.cpp", -"src/stdlib_stdexcept.cpp", -"src/stdlib_typeinfo.cpp", -}; -static const char *ZIG_LIBCXX_FILES[] = { -"src/algorithm.cpp", -"src/any.cpp", -"src/bind.cpp", -"src/charconv.cpp", -"src/chrono.cpp", -"src/condition_variable.cpp", -"src/condition_variable_destructor.cpp", -"src/debug.cpp", -"src/exception.cpp", -"src/experimental/memory_resource.cpp", -"src/filesystem/directory_iterator.cpp", -"src/filesystem/operations.cpp", -"src/functional.cpp", -"src/future.cpp", -"src/hash.cpp", -"src/ios.cpp", -"src/iostream.cpp", -"src/locale.cpp", -"src/memory.cpp", -"src/mutex.cpp", -"src/mutex_destructor.cpp", -"src/new.cpp", -"src/optional.cpp", -"src/random.cpp", -"src/regex.cpp", -"src/shared_mutex.cpp", -"src/stdexcept.cpp", -"src/string.cpp", -"src/strstream.cpp", -"src/support/solaris/xlocale.cpp", -"src/support/win32/locale_win32.cpp", -"src/support/win32/support.cpp", -"src/support/win32/thread_win32.cpp", -"src/system_error.cpp", -"src/thread.cpp", -"src/typeinfo.cpp", -"src/utility.cpp", -"src/valarray.cpp", -"src/variant.cpp", -"src/vector.cpp", -}; -#endif diff --git a/src/introspect.zig b/src/introspect.zig new file mode 100644 index 0000000000000000000000000000000000000000..b75bf8f4b87072a50bd4985ebe1708123af05cbe --- /dev/null +++ b/src/introspect.zig @@ -0,0 +1,75 @@ +const std = @import("std"); +const mem = std.mem; +const fs = std.fs; +const Compilation = @import("Compilation.zig"); + +/// Returns the sub_path that worked, or `null` if none did. +/// The path of the returned Directory is relative to `base`. +/// The handle of the returned Directory is open. +fn testZigInstallPrefix(base_dir: fs.Dir) ?Compilation.Directory { + const test_index_file = "std" ++ fs.path.sep_str ++ "std.zig"; + + zig_dir: { + // Try lib/zig/std/std.zig + const lib_zig = "lib" ++ fs.path.sep_str ++ "zig"; + var test_zig_dir = base_dir.openDir(lib_zig, .{}) catch break :zig_dir; + const file = test_zig_dir.openFile(test_index_file, .{}) catch { + test_zig_dir.close(); + break :zig_dir; + }; + file.close(); + return Compilation.Directory{ .handle = test_zig_dir, .path = lib_zig }; + } + + // Try lib/std/std.zig + var test_zig_dir = base_dir.openDir("lib", .{}) catch return null; + const file = test_zig_dir.openFile(test_index_file, .{}) catch { + test_zig_dir.close(); + return null; + }; + file.close(); + return Compilation.Directory{ .handle = test_zig_dir, .path = "lib" }; +} + +/// Both the directory handle and the path are newly allocated resources which the caller now owns. +pub fn findZigLibDir(gpa: *mem.Allocator) !Compilation.Directory { + const self_exe_path = try fs.selfExePathAlloc(gpa); + defer gpa.free(self_exe_path); + + return findZigLibDirFromSelfExe(gpa, self_exe_path); +} + +/// Both the directory handle and the path are newly allocated resources which the caller now owns. +pub fn findZigLibDirFromSelfExe( + allocator: *mem.Allocator, + self_exe_path: []const u8, +) error{ OutOfMemory, FileNotFound }!Compilation.Directory { + const cwd = fs.cwd(); + var cur_path: []const u8 = self_exe_path; + while (fs.path.dirname(cur_path)) |dirname| : (cur_path = dirname) { + var base_dir = cwd.openDir(dirname, .{}) catch continue; + defer base_dir.close(); + + const sub_directory = testZigInstallPrefix(base_dir) orelse continue; + return Compilation.Directory{ + .handle = sub_directory.handle, + .path = try fs.path.join(allocator, &[_][]const u8{ dirname, sub_directory.path.? }), + }; + } + return error.FileNotFound; +} + +/// Caller owns returned memory. +pub fn resolveGlobalCacheDir(allocator: *mem.Allocator) ![]u8 { + const appname = "zig"; + + if (std.Target.current.os.tag != .windows) { + if (std.os.getenv("XDG_CACHE_HOME")) |cache_root| { + return fs.path.join(allocator, &[_][]const u8{ cache_root, appname }); + } else if (std.os.getenv("HOME")) |home| { + return fs.path.join(allocator, &[_][]const u8{ home, ".cache", appname }); + } + } + + return fs.getAppDataDir(allocator, appname); +} diff --git a/src/ir.cpp b/src/ir.cpp deleted file mode 100644 index d8d7289dae675e3333b62d9a1d8ed722762bdfde..0000000000000000000000000000000000000000 --- a/src/ir.cpp +++ /dev/null @@ -1,32727 +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 "error.hpp" -#include "ir.hpp" -#include "ir_print.hpp" -#include "os.hpp" -#include "range_set.hpp" -#include "softfloat.hpp" -#include "softfloat_ext.hpp" -#include "util.hpp" -#include "mem_list.hpp" -#include "all_types.hpp" - -#include - -struct IrBuilderSrc { - CodeGen *codegen; - IrExecutableSrc *exec; - IrBasicBlockSrc *current_basic_block; - AstNode *main_block_node; -}; - -struct IrBuilderGen { - CodeGen *codegen; - IrExecutableGen *exec; - IrBasicBlockGen *current_basic_block; - - // track for immediate post-analysis destruction - mem::List constants; -}; - -struct IrAnalyze { - CodeGen *codegen; - IrBuilderSrc old_irb; - IrBuilderGen new_irb; - size_t old_bb_index; - size_t instruction_index; - ZigType *explicit_return_type; - AstNode *explicit_return_type_source_node; - ZigList src_implicit_return_type_list; - ZigList resume_stack; - IrBasicBlockSrc *const_predecessor_bb; - size_t ref_count; - size_t break_debug_id; // for debugging purposes - IrInstGen *return_ptr; - - // For the purpose of using in a debugger - void dump(); -}; - -enum ConstCastResultId { - ConstCastResultIdOk, - ConstCastResultIdInvalid, - ConstCastResultIdErrSet, - ConstCastResultIdErrSetGlobal, - ConstCastResultIdPointerChild, - ConstCastResultIdSliceChild, - ConstCastResultIdOptionalChild, - ConstCastResultIdOptionalShape, - ConstCastResultIdErrorUnionPayload, - ConstCastResultIdErrorUnionErrorSet, - ConstCastResultIdFnAlign, - ConstCastResultIdFnCC, - ConstCastResultIdFnVarArgs, - ConstCastResultIdFnIsGeneric, - ConstCastResultIdFnReturnType, - ConstCastResultIdFnArgCount, - ConstCastResultIdFnGenericArgCount, - ConstCastResultIdFnArg, - ConstCastResultIdFnArgNoAlias, - ConstCastResultIdType, - ConstCastResultIdUnresolvedInferredErrSet, - ConstCastResultIdAsyncAllocatorType, - ConstCastResultIdBadAllowsZero, - ConstCastResultIdArrayChild, - ConstCastResultIdSentinelArrays, - ConstCastResultIdPtrLens, - ConstCastResultIdCV, - ConstCastResultIdPtrSentinel, - ConstCastResultIdIntShorten, -}; - -struct ConstCastOnly; -struct ConstCastArg { - size_t arg_index; - ZigType *actual_param_type; - ZigType *expected_param_type; - ConstCastOnly *child; -}; - -struct ConstCastArgNoAlias { - size_t arg_index; -}; - -struct ConstCastOptionalMismatch; -struct ConstCastPointerMismatch; -struct ConstCastSliceMismatch; -struct ConstCastErrUnionErrSetMismatch; -struct ConstCastErrUnionPayloadMismatch; -struct ConstCastErrSetMismatch; -struct ConstCastTypeMismatch; -struct ConstCastArrayMismatch; -struct ConstCastBadAllowsZero; -struct ConstCastBadNullTermArrays; -struct ConstCastBadCV; -struct ConstCastPtrSentinel; -struct ConstCastIntShorten; - -struct ConstCastOnly { - ConstCastResultId id; - union { - ConstCastErrSetMismatch *error_set_mismatch; - ConstCastPointerMismatch *pointer_mismatch; - ConstCastSliceMismatch *slice_mismatch; - ConstCastOptionalMismatch *optional; - ConstCastErrUnionPayloadMismatch *error_union_payload; - ConstCastErrUnionErrSetMismatch *error_union_error_set; - ConstCastTypeMismatch *type_mismatch; - ConstCastArrayMismatch *array_mismatch; - ConstCastOnly *return_type; - ConstCastOnly *null_wrap_ptr_child; - ConstCastArg fn_arg; - ConstCastArgNoAlias arg_no_alias; - ConstCastBadAllowsZero *bad_allows_zero; - ConstCastBadNullTermArrays *sentinel_arrays; - ConstCastBadCV *bad_cv; - ConstCastPtrSentinel *bad_ptr_sentinel; - ConstCastIntShorten *int_shorten; - } data; -}; - -struct ConstCastTypeMismatch { - ZigType *wanted_type; - ZigType *actual_type; -}; - -struct ConstCastOptionalMismatch { - ConstCastOnly child; - ZigType *wanted_child; - ZigType *actual_child; -}; - -struct ConstCastPointerMismatch { - ConstCastOnly child; - ZigType *wanted_child; - ZigType *actual_child; -}; - -struct ConstCastSliceMismatch { - ConstCastOnly child; - ZigType *wanted_child; - ZigType *actual_child; -}; - -struct ConstCastArrayMismatch { - ConstCastOnly child; - ZigType *wanted_child; - ZigType *actual_child; -}; - -struct ConstCastErrUnionErrSetMismatch { - ConstCastOnly child; - ZigType *wanted_err_set; - ZigType *actual_err_set; -}; - -struct ConstCastErrUnionPayloadMismatch { - ConstCastOnly child; - ZigType *wanted_payload; - ZigType *actual_payload; -}; - -struct ConstCastErrSetMismatch { - ZigList missing_errors; -}; - -struct ConstCastBadAllowsZero { - ZigType *wanted_type; - ZigType *actual_type; -}; - -struct ConstCastBadNullTermArrays { - ConstCastOnly child; - ZigType *wanted_type; - ZigType *actual_type; -}; - -struct ConstCastBadCV { - ZigType *wanted_type; - ZigType *actual_type; -}; - -struct ConstCastPtrSentinel { - ZigType *wanted_type; - ZigType *actual_type; -}; - -struct ConstCastIntShorten { - ZigType *wanted_type; - ZigType *actual_type; -}; - -// for debugging purposes -struct DbgIrBreakPoint { - const char *src_file; - uint32_t line; -}; -DbgIrBreakPoint dbg_ir_breakpoints_buf[20]; -size_t dbg_ir_breakpoints_count = 0; - -static IrInstSrc *ir_gen_node(IrBuilderSrc *irb, AstNode *node, Scope *scope); -static IrInstSrc *ir_gen_node_extra(IrBuilderSrc *irb, AstNode *node, Scope *scope, LVal lval, - ResultLoc *result_loc); -static IrInstGen *ir_implicit_cast(IrAnalyze *ira, IrInstGen *value, ZigType *expected_type); -static IrInstGen *ir_implicit_cast2(IrAnalyze *ira, IrInst *value_source_instr, - IrInstGen *value, ZigType *expected_type); -static IrInstGen *ir_get_deref(IrAnalyze *ira, IrInst *source_instr, IrInstGen *ptr, - ResultLoc *result_loc); -static ErrorMsg *exec_add_error_node(CodeGen *codegen, IrExecutableSrc *exec, AstNode *source_node, Buf *msg); -static IrInstGen *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_name, - IrInst* source_instr, IrInstGen *container_ptr, IrInst *container_ptr_src, - ZigType *container_type, bool initializing); -static void ir_assert_impl(bool ok, IrInst* source_instruction, const char *file, unsigned int line); -static void ir_assert_gen_impl(bool ok, IrInstGen *source_instruction, const char *file, unsigned int line); -static IrInstGen *ir_get_var_ptr(IrAnalyze *ira, IrInst *source_instr, ZigVar *var); -static ZigType *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstGen *op); -static IrInstSrc *ir_lval_wrap(IrBuilderSrc *irb, Scope *scope, IrInstSrc *value, LVal lval, ResultLoc *result_loc); -static IrInstSrc *ir_expr_wrap(IrBuilderSrc *irb, Scope *scope, IrInstSrc *inst, ResultLoc *result_loc); -static ZigType *adjust_ptr_align(CodeGen *g, ZigType *ptr_type, uint32_t new_align); -static ZigType *adjust_ptr_const(CodeGen *g, ZigType *ptr_type, bool is_const); -static ZigType *adjust_slice_align(CodeGen *g, ZigType *slice_type, uint32_t new_align); -static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, uint8_t *buf, ZigValue *val); -static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ZigValue *val); -static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, - ZigValue *out_val, ZigValue *ptr_val); -static IrInstGen *ir_analyze_ptr_cast(IrAnalyze *ira, IrInst* source_instr, IrInstGen *ptr, - IrInst *ptr_src, ZigType *dest_type, IrInst *dest_type_src, bool safety_check_on, - bool keep_bigger_alignment); -static ZigValue *ir_resolve_const(IrAnalyze *ira, IrInstGen *value, UndefAllowed undef_allowed); -static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align); -static IrInstGen *ir_analyze_int_to_ptr(IrAnalyze *ira, IrInst* source_instr, IrInstGen *target, - ZigType *ptr_type); -static IrInstGen *ir_analyze_bit_cast(IrAnalyze *ira, IrInst* source_instr, IrInstGen *value, - ZigType *dest_type); -static IrInstGen *ir_resolve_result_raw(IrAnalyze *ira, IrInst *suspend_source_instr, - ResultLoc *result_loc, ZigType *value_type, IrInstGen *value, bool force_runtime, bool allow_discard); -static IrInstGen *ir_resolve_result(IrAnalyze *ira, IrInst *suspend_source_instr, - ResultLoc *result_loc, ZigType *value_type, IrInstGen *value, bool force_runtime, bool allow_discard); -static IrInstGen *ir_analyze_unwrap_optional_payload(IrAnalyze *ira, IrInst* source_instr, - IrInstGen *base_ptr, bool safety_check_on, bool initializing); -static IrInstGen *ir_analyze_unwrap_error_payload(IrAnalyze *ira, IrInst* source_instr, - IrInstGen *base_ptr, bool safety_check_on, bool initializing); -static IrInstGen *ir_analyze_unwrap_err_code(IrAnalyze *ira, IrInst* source_instr, - IrInstGen *base_ptr, bool initializing); -static IrInstGen *ir_analyze_store_ptr(IrAnalyze *ira, IrInst* source_instr, - IrInstGen *ptr, IrInstGen *uncasted_value, bool allow_write_through_const); -static IrInstSrc *ir_gen_union_init_expr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *union_type, IrInstSrc *field_name, AstNode *expr_node, - LVal lval, ResultLoc *parent_result_loc); -static void ir_reset_result(ResultLoc *result_loc); -static Buf *get_anon_type_name(CodeGen *codegen, IrExecutableSrc *exec, const char *kind_name, - Scope *scope, AstNode *source_node, Buf *out_bare_name); -static ResultLocCast *ir_build_cast_result_loc(IrBuilderSrc *irb, IrInstSrc *dest_type, - ResultLoc *parent_result_loc); -static IrInstGen *ir_analyze_struct_field_ptr(IrAnalyze *ira, IrInst* source_instr, - TypeStructField *field, IrInstGen *struct_ptr, ZigType *struct_type, bool initializing); -static IrInstGen *ir_analyze_inferred_field_ptr(IrAnalyze *ira, Buf *field_name, - IrInst* source_instr, IrInstGen *container_ptr, ZigType *container_type); -static ResultLoc *no_result_loc(void); -static IrInstGen *ir_analyze_test_non_null(IrAnalyze *ira, IrInst *source_inst, IrInstGen *value); -static IrInstGen *ir_error_dependency_loop(IrAnalyze *ira, IrInst *source_instr); -static IrInstGen *ir_const_undef(IrAnalyze *ira, IrInst *source_instruction, ZigType *ty); -static ZigVar *ir_create_var(IrBuilderSrc *irb, AstNode *node, Scope *scope, Buf *name, - bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstSrc *is_comptime); -static void build_decl_var_and_init(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, ZigVar *var, - IrInstSrc *init, const char *name_hint, IrInstSrc *is_comptime); -static IrInstGen *ir_analyze_union_init(IrAnalyze *ira, IrInst* source_instruction, - AstNode *field_source_node, ZigType *union_type, Buf *field_name, IrInstGen *field_result_loc, - IrInstGen *result_loc); -static IrInstGen *ir_analyze_struct_value_field_value(IrAnalyze *ira, IrInst* source_instr, - IrInstGen *struct_operand, TypeStructField *field); -static bool value_cmp_numeric_val_any(ZigValue *left, Cmp predicate, ZigValue *right); -static bool value_cmp_numeric_val_all(ZigValue *left, Cmp predicate, ZigValue *right); -static void memoize_field_init_val(CodeGen *codegen, ZigType *container_type, TypeStructField *field); -static void value_to_bigfloat(BigFloat *out, ZigValue *val); - -#define ir_assert(OK, SOURCE_INSTRUCTION) ir_assert_impl((OK), (SOURCE_INSTRUCTION), __FILE__, __LINE__) -#define ir_assert_gen(OK, SOURCE_INSTRUCTION) ir_assert_gen_impl((OK), (SOURCE_INSTRUCTION), __FILE__, __LINE__) - -static void destroy_instruction_src(IrInstSrc *inst) { - switch (inst->id) { - case IrInstSrcIdInvalid: - zig_unreachable(); - case IrInstSrcIdReturn: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdConst: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdBinOp: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdMergeErrSets: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdDeclVar: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdCall: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdCallExtra: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdAsyncCallExtra: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdUnOp: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdCondBr: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdBr: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdPhi: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdContainerInitList: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdContainerInitFields: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdUnreachable: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdElemPtr: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdVarPtr: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdLoadPtr: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdStorePtr: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdTypeOf: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdFieldPtr: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdSetCold: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdSetRuntimeSafety: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdSetFloatMode: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdArrayType: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdSliceType: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdAnyFrameType: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdAsm: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdSizeOf: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdTestNonNull: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdOptionalUnwrapPtr: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdPopCount: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdClz: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdCtz: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdBswap: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdBitReverse: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdSwitchBr: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdSwitchVar: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdSwitchElseVar: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdSwitchTarget: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdImport: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdRef: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdCompileErr: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdCompileLog: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdErrName: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdCImport: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdCInclude: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdCDefine: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdCUndef: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdEmbedFile: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdCmpxchg: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdFence: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdTruncate: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdIntCast: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdFloatCast: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdErrSetCast: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdIntToFloat: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdFloatToInt: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdBoolToInt: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdVectorType: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdShuffleVector: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdSplat: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdBoolNot: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdMemset: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdMemcpy: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdSlice: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdBreakpoint: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdReturnAddress: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdFrameAddress: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdFrameHandle: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdFrameType: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdFrameSize: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdAlignOf: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdOverflowOp: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdTestErr: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdUnwrapErrCode: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdUnwrapErrPayload: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdFnProto: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdTestComptime: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdPtrCast: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdBitCast: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdPtrToInt: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdIntToPtr: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdIntToEnum: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdIntToErr: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdErrToInt: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdCheckSwitchProngs: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdCheckStatementIsVoid: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdTypeName: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdTagName: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdPtrType: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdDeclRef: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdPanic: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdFieldParentPtr: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdByteOffsetOf: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdBitOffsetOf: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdTypeInfo: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdType: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdHasField: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdSetEvalBranchQuota: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdAlignCast: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdImplicitCast: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdResolveResult: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdResetResult: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdSetAlignStack: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdArgType: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdTagType: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdExport: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdErrorReturnTrace: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdErrorUnion: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdAtomicRmw: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdSaveErrRetAddr: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdAddImplicitReturnType: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdFloatOp: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdMulAdd: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdAtomicLoad: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdAtomicStore: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdEnumToInt: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdCheckRuntimeScope: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdHasDecl: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdUndeclaredIdent: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdAlloca: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdEndExpr: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdUnionInitNamedField: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdSuspendBegin: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdSuspendFinish: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdResume: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdAwait: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdSpillBegin: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdSpillEnd: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdCallArgs: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdWasmMemorySize: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdWasmMemoryGrow: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstSrcIdSrc: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - } - zig_unreachable(); -} - -void destroy_instruction_gen(IrInstGen *inst) { - switch (inst->id) { - case IrInstGenIdInvalid: - zig_unreachable(); - case IrInstGenIdReturn: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdConst: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdBinOp: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdCast: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdCall: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdCondBr: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdBr: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdPhi: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdUnreachable: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdElemPtr: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdVarPtr: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdReturnPtr: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdLoadPtr: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdStorePtr: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdVectorStoreElem: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdStructFieldPtr: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdUnionFieldPtr: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdAsm: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdTestNonNull: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdOptionalUnwrapPtr: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdPopCount: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdClz: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdCtz: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdBswap: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdBitReverse: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdSwitchBr: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdUnionTag: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdRef: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdErrName: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdCmpxchg: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdFence: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdTruncate: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdShuffleVector: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdSplat: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdBoolNot: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdMemset: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdMemcpy: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdSlice: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdBreakpoint: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdReturnAddress: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdFrameAddress: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdFrameHandle: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdFrameSize: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdOverflowOp: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdTestErr: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdUnwrapErrCode: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdUnwrapErrPayload: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdOptionalWrap: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdErrWrapCode: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdErrWrapPayload: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdPtrCast: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdBitCast: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdWidenOrShorten: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdPtrToInt: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdIntToPtr: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdIntToEnum: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdIntToErr: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdErrToInt: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdTagName: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdPanic: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdFieldParentPtr: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdAlignCast: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdErrorReturnTrace: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdAtomicRmw: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdSaveErrRetAddr: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdFloatOp: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdMulAdd: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdAtomicLoad: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdAtomicStore: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdDeclVar: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdArrayToVector: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdVectorToArray: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdPtrOfArrayToSlice: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdAssertZero: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdAssertNonNull: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdAlloca: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdSuspendBegin: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdSuspendFinish: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdResume: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdAwait: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdSpillBegin: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdSpillEnd: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdVectorExtractElem: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdBinaryNot: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdNegation: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdNegationWrapping: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdWasmMemorySize: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - case IrInstGenIdWasmMemoryGrow: - return heap::c_allocator.destroy(reinterpret_cast(inst)); - } - zig_unreachable(); -} - -static void ira_ref(IrAnalyze *ira) { - ira->ref_count += 1; -} -static void ira_deref(IrAnalyze *ira) { - if (ira->ref_count > 1) { - ira->ref_count -= 1; - - // immediate destruction of dangling IrInstGenConst is not possible - // free tracking memory because it will never be used - ira->new_irb.constants.deinit(&heap::c_allocator); - return; - } - assert(ira->ref_count != 0); - - for (size_t bb_i = 0; bb_i < ira->old_irb.exec->basic_block_list.length; bb_i += 1) { - IrBasicBlockSrc *pass1_bb = ira->old_irb.exec->basic_block_list.items[bb_i]; - for (size_t inst_i = 0; inst_i < pass1_bb->instruction_list.length; inst_i += 1) { - IrInstSrc *pass1_inst = pass1_bb->instruction_list.items[inst_i]; - destroy_instruction_src(pass1_inst); - } - heap::c_allocator.destroy(pass1_bb); - } - ira->old_irb.exec->basic_block_list.deinit(); - ira->old_irb.exec->tld_list.deinit(); - heap::c_allocator.destroy(ira->old_irb.exec); - ira->src_implicit_return_type_list.deinit(); - ira->resume_stack.deinit(); - - // destroy dangling IrInstGenConst - for (size_t i = 0; i < ira->new_irb.constants.length; i += 1) { - auto constant = ira->new_irb.constants.items[i]; - if (constant->base.base.ref_count == 0 && !ir_inst_gen_has_side_effects(&constant->base)) - destroy_instruction_gen(&constant->base); - } - ira->new_irb.constants.deinit(&heap::c_allocator); - - heap::c_allocator.destroy(ira); -} - -static ZigValue *const_ptr_pointee_unchecked_no_isf(CodeGen *g, ZigValue *const_val) { - assert(get_src_ptr_type(const_val->type) != nullptr); - assert(const_val->special == ConstValSpecialStatic); - - switch (type_has_one_possible_value(g, const_val->type->data.pointer.child_type)) { - case OnePossibleValueInvalid: - return nullptr; - case OnePossibleValueYes: - return get_the_one_possible_value(g, const_val->type->data.pointer.child_type); - case OnePossibleValueNo: - break; - } - - ZigValue *result; - switch (const_val->data.x_ptr.special) { - case ConstPtrSpecialInvalid: - zig_unreachable(); - case ConstPtrSpecialRef: - result = const_val->data.x_ptr.data.ref.pointee; - break; - case ConstPtrSpecialBaseArray: { - ZigValue *array_val = const_val->data.x_ptr.data.base_array.array_val; - size_t elem_index = const_val->data.x_ptr.data.base_array.elem_index; - if (elem_index == array_val->type->data.array.len) { - result = array_val->type->data.array.sentinel; - } else { - expand_undef_array(g, array_val); - result = &array_val->data.x_array.data.s_none.elements[elem_index]; - } - break; - } - case ConstPtrSpecialSubArray: { - ZigValue *array_val = const_val->data.x_ptr.data.base_array.array_val; - size_t elem_index = const_val->data.x_ptr.data.base_array.elem_index; - - expand_undef_array(g, array_val); - result = g->pass1_arena->create(); - result->special = array_val->special; - result->type = get_array_type(g, array_val->type->data.array.child_type, - array_val->type->data.array.len - elem_index, array_val->type->data.array.sentinel); - result->data.x_array.special = ConstArraySpecialNone; - result->data.x_array.data.s_none.elements = &array_val->data.x_array.data.s_none.elements[elem_index]; - result->parent.id = ConstParentIdArray; - result->parent.data.p_array.array_val = array_val; - result->parent.data.p_array.elem_index = elem_index; - break; - } - case ConstPtrSpecialBaseStruct: { - ZigValue *struct_val = const_val->data.x_ptr.data.base_struct.struct_val; - expand_undef_struct(g, struct_val); - result = struct_val->data.x_struct.fields[const_val->data.x_ptr.data.base_struct.field_index]; - break; - } - case ConstPtrSpecialBaseErrorUnionCode: - result = const_val->data.x_ptr.data.base_err_union_code.err_union_val->data.x_err_union.error_set; - break; - case ConstPtrSpecialBaseErrorUnionPayload: - result = const_val->data.x_ptr.data.base_err_union_payload.err_union_val->data.x_err_union.payload; - break; - case ConstPtrSpecialBaseOptionalPayload: - result = const_val->data.x_ptr.data.base_optional_payload.optional_val->data.x_optional; - break; - case ConstPtrSpecialNull: - result = const_val; - break; - case ConstPtrSpecialHardCodedAddr: - zig_unreachable(); - case ConstPtrSpecialDiscard: - zig_unreachable(); - case ConstPtrSpecialFunction: - zig_unreachable(); - } - assert(result != nullptr); - return result; -} - -static ZigValue *const_ptr_pointee_unchecked(CodeGen *g, ZigValue *const_val) { - assert(get_src_ptr_type(const_val->type) != nullptr); - assert(const_val->special == ConstValSpecialStatic); - - InferredStructField *isf = const_val->type->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) { - assert(field->init_val != nullptr); - return field->init_val; - } - ZigValue *struct_val = const_ptr_pointee_unchecked_no_isf(g, const_val); - assert(struct_val->type->id == ZigTypeIdStruct); - return struct_val->data.x_struct.fields[field->src_index]; - } - - return const_ptr_pointee_unchecked_no_isf(g, const_val); -} - -static bool is_tuple(ZigType *type) { - return type->id == ZigTypeIdStruct && type->data.structure.special == StructSpecialInferredTuple; -} - -static bool is_slice(ZigType *type) { - return type->id == ZigTypeIdStruct && type->data.structure.special == StructSpecialSlice; -} - -// This function returns true when you can change the type of a ZigValue and the -// value remains meaningful. -static bool types_have_same_zig_comptime_repr(CodeGen *codegen, ZigType *expected, ZigType *actual) { - if (expected == actual) - return true; - - if (get_src_ptr_type(expected) != nullptr && get_src_ptr_type(actual) != nullptr) - return true; - - if (is_opt_err_set(expected) && is_opt_err_set(actual)) - return true; - - if (expected->id != actual->id) - return false; - - switch (expected->id) { - case ZigTypeIdInvalid: - case ZigTypeIdUnreachable: - zig_unreachable(); - case ZigTypeIdMetaType: - case ZigTypeIdVoid: - case ZigTypeIdBool: - case ZigTypeIdComptimeFloat: - case ZigTypeIdComptimeInt: - case ZigTypeIdEnumLiteral: - case ZigTypeIdUndefined: - case ZigTypeIdNull: - case ZigTypeIdBoundFn: - case ZigTypeIdErrorSet: - case ZigTypeIdOpaque: - case ZigTypeIdAnyFrame: - case ZigTypeIdFn: - return true; - case ZigTypeIdPointer: - return expected->data.pointer.inferred_struct_field == actual->data.pointer.inferred_struct_field; - case ZigTypeIdFloat: - return expected->data.floating.bit_count == actual->data.floating.bit_count; - case ZigTypeIdInt: - return expected->data.integral.is_signed == actual->data.integral.is_signed; - case ZigTypeIdStruct: - return is_slice(expected) && is_slice(actual); - case ZigTypeIdOptional: - case ZigTypeIdErrorUnion: - case ZigTypeIdEnum: - case ZigTypeIdUnion: - case ZigTypeIdVector: - case ZigTypeIdFnFrame: - return false; - case ZigTypeIdArray: - return expected->data.array.len == actual->data.array.len && - expected->data.array.child_type == actual->data.array.child_type && - (expected->data.array.sentinel == nullptr || (actual->data.array.sentinel != nullptr && - const_values_equal(codegen, expected->data.array.sentinel, actual->data.array.sentinel))); - } - zig_unreachable(); -} - -static bool ir_should_inline(IrExecutableSrc *exec, Scope *scope) { - if (exec->is_inline) - return true; - - while (scope != nullptr) { - if (scope->id == ScopeIdCompTime) - return true; - if (scope->id == ScopeIdTypeOf) - return false; - if (scope->id == ScopeIdFnDef) - break; - scope = scope->parent; - } - return false; -} - -static void ir_instruction_append(IrBasicBlockSrc *basic_block, IrInstSrc *instruction) { - assert(basic_block); - assert(instruction); - basic_block->instruction_list.append(instruction); -} - -static void ir_inst_gen_append(IrBasicBlockGen *basic_block, IrInstGen *instruction) { - assert(basic_block); - assert(instruction); - basic_block->instruction_list.append(instruction); -} - -static size_t exec_next_debug_id(IrExecutableSrc *exec) { - size_t result = exec->next_debug_id; - exec->next_debug_id += 1; - return result; -} - -static size_t exec_next_debug_id_gen(IrExecutableGen *exec) { - size_t result = exec->next_debug_id; - exec->next_debug_id += 1; - return result; -} - -static ZigFn *exec_fn_entry(IrExecutableSrc *exec) { - return exec->fn_entry; -} - -static Buf *exec_c_import_buf(IrExecutableSrc *exec) { - return exec->c_import_buf; -} - -static bool value_is_comptime(ZigValue *const_val) { - return const_val->special != ConstValSpecialRuntime; -} - -static bool instr_is_comptime(IrInstGen *instruction) { - return value_is_comptime(instruction->value); -} - -static bool instr_is_unreachable(IrInstSrc *instruction) { - return instruction->is_noreturn; -} - -static void ir_ref_bb(IrBasicBlockSrc *bb) { - bb->ref_count += 1; -} - -static void ir_ref_instruction(IrInstSrc *instruction, IrBasicBlockSrc *cur_bb) { - assert(instruction->id != IrInstSrcIdInvalid); - instruction->base.ref_count += 1; - if (instruction->owner_bb != cur_bb && !instr_is_unreachable(instruction) - && instruction->id != IrInstSrcIdConst) - { - ir_ref_bb(instruction->owner_bb); - } -} - -static void ir_ref_inst_gen(IrInstGen *instruction) { - assert(instruction->id != IrInstGenIdInvalid); - instruction->base.ref_count += 1; -} - -static void ir_ref_var(ZigVar *var) { - var->ref_count += 1; -} - -static void create_result_ptr(CodeGen *codegen, ZigType *expected_type, - ZigValue **out_result, ZigValue **out_result_ptr) -{ - ZigValue *result = codegen->pass1_arena->create(); - ZigValue *result_ptr = codegen->pass1_arena->create(); - result->special = ConstValSpecialUndef; - result->type = expected_type; - result_ptr->special = ConstValSpecialStatic; - result_ptr->type = get_pointer_to_type(codegen, 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; - - *out_result = result; - *out_result_ptr = result_ptr; -} - -ZigType *ir_analyze_type_expr(IrAnalyze *ira, Scope *scope, AstNode *node) { - Error err; - - ZigValue *result; - ZigValue *result_ptr; - create_result_ptr(ira->codegen, ira->codegen->builtin_types.entry_type, &result, &result_ptr); - - if ((err = ir_eval_const_value(ira->codegen, scope, node, result_ptr, - ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, - nullptr, nullptr, node, nullptr, ira->new_irb.exec, nullptr, UndefBad))) - { - return ira->codegen->builtin_types.entry_invalid; - } - if (type_is_invalid(result->type)) - return ira->codegen->builtin_types.entry_invalid; - - assert(result->special != ConstValSpecialRuntime); - ZigType *res_type = result->data.x_type; - - return res_type; -} - -static IrBasicBlockSrc *ir_create_basic_block(IrBuilderSrc *irb, Scope *scope, const char *name_hint) { - IrBasicBlockSrc *result = heap::c_allocator.create(); - result->scope = scope; - result->name_hint = name_hint; - result->debug_id = exec_next_debug_id(irb->exec); - result->index = UINT32_MAX; // set later - return result; -} - -static IrBasicBlockGen *ir_create_basic_block_gen(IrAnalyze *ira, Scope *scope, const char *name_hint) { - IrBasicBlockGen *result = heap::c_allocator.create(); - result->scope = scope; - result->name_hint = name_hint; - result->debug_id = exec_next_debug_id_gen(ira->new_irb.exec); - return result; -} - -static IrBasicBlockGen *ir_build_bb_from(IrAnalyze *ira, IrBasicBlockSrc *other_bb) { - IrBasicBlockGen *new_bb = ir_create_basic_block_gen(ira, other_bb->scope, other_bb->name_hint); - other_bb->child = new_bb; - return new_bb; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcDeclVar *) { - return IrInstSrcIdDeclVar; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcBr *) { - return IrInstSrcIdBr; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcCondBr *) { - return IrInstSrcIdCondBr; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcSwitchBr *) { - return IrInstSrcIdSwitchBr; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcSwitchVar *) { - return IrInstSrcIdSwitchVar; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcSwitchElseVar *) { - return IrInstSrcIdSwitchElseVar; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcSwitchTarget *) { - return IrInstSrcIdSwitchTarget; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcPhi *) { - return IrInstSrcIdPhi; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcUnOp *) { - return IrInstSrcIdUnOp; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcBinOp *) { - return IrInstSrcIdBinOp; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcMergeErrSets *) { - return IrInstSrcIdMergeErrSets; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcLoadPtr *) { - return IrInstSrcIdLoadPtr; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcStorePtr *) { - return IrInstSrcIdStorePtr; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcFieldPtr *) { - return IrInstSrcIdFieldPtr; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcElemPtr *) { - return IrInstSrcIdElemPtr; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcVarPtr *) { - return IrInstSrcIdVarPtr; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcCall *) { - return IrInstSrcIdCall; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcCallArgs *) { - return IrInstSrcIdCallArgs; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcCallExtra *) { - return IrInstSrcIdCallExtra; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcAsyncCallExtra *) { - return IrInstSrcIdAsyncCallExtra; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcConst *) { - return IrInstSrcIdConst; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcReturn *) { - return IrInstSrcIdReturn; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcContainerInitList *) { - return IrInstSrcIdContainerInitList; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcContainerInitFields *) { - return IrInstSrcIdContainerInitFields; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcUnreachable *) { - return IrInstSrcIdUnreachable; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcTypeOf *) { - return IrInstSrcIdTypeOf; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcSetCold *) { - return IrInstSrcIdSetCold; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcSetRuntimeSafety *) { - return IrInstSrcIdSetRuntimeSafety; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcSetFloatMode *) { - return IrInstSrcIdSetFloatMode; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcArrayType *) { - return IrInstSrcIdArrayType; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcAnyFrameType *) { - return IrInstSrcIdAnyFrameType; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcSliceType *) { - return IrInstSrcIdSliceType; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcAsm *) { - return IrInstSrcIdAsm; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcSizeOf *) { - return IrInstSrcIdSizeOf; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcTestNonNull *) { - return IrInstSrcIdTestNonNull; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcOptionalUnwrapPtr *) { - return IrInstSrcIdOptionalUnwrapPtr; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcClz *) { - return IrInstSrcIdClz; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcCtz *) { - return IrInstSrcIdCtz; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcPopCount *) { - return IrInstSrcIdPopCount; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcBswap *) { - return IrInstSrcIdBswap; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcBitReverse *) { - return IrInstSrcIdBitReverse; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcImport *) { - return IrInstSrcIdImport; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcCImport *) { - return IrInstSrcIdCImport; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcCInclude *) { - return IrInstSrcIdCInclude; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcCDefine *) { - return IrInstSrcIdCDefine; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcCUndef *) { - return IrInstSrcIdCUndef; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcRef *) { - return IrInstSrcIdRef; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcCompileErr *) { - return IrInstSrcIdCompileErr; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcCompileLog *) { - return IrInstSrcIdCompileLog; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcErrName *) { - return IrInstSrcIdErrName; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcEmbedFile *) { - return IrInstSrcIdEmbedFile; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcCmpxchg *) { - return IrInstSrcIdCmpxchg; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcFence *) { - return IrInstSrcIdFence; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcTruncate *) { - return IrInstSrcIdTruncate; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcIntCast *) { - return IrInstSrcIdIntCast; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcFloatCast *) { - return IrInstSrcIdFloatCast; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcIntToFloat *) { - return IrInstSrcIdIntToFloat; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcFloatToInt *) { - return IrInstSrcIdFloatToInt; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcBoolToInt *) { - return IrInstSrcIdBoolToInt; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcVectorType *) { - return IrInstSrcIdVectorType; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcShuffleVector *) { - return IrInstSrcIdShuffleVector; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcSplat *) { - return IrInstSrcIdSplat; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcBoolNot *) { - return IrInstSrcIdBoolNot; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcMemset *) { - return IrInstSrcIdMemset; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcMemcpy *) { - return IrInstSrcIdMemcpy; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcSlice *) { - return IrInstSrcIdSlice; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcBreakpoint *) { - return IrInstSrcIdBreakpoint; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcReturnAddress *) { - return IrInstSrcIdReturnAddress; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcFrameAddress *) { - return IrInstSrcIdFrameAddress; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcFrameHandle *) { - return IrInstSrcIdFrameHandle; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcFrameType *) { - return IrInstSrcIdFrameType; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcFrameSize *) { - return IrInstSrcIdFrameSize; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcAlignOf *) { - return IrInstSrcIdAlignOf; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcOverflowOp *) { - return IrInstSrcIdOverflowOp; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcTestErr *) { - return IrInstSrcIdTestErr; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcMulAdd *) { - return IrInstSrcIdMulAdd; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcFloatOp *) { - return IrInstSrcIdFloatOp; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcUnwrapErrCode *) { - return IrInstSrcIdUnwrapErrCode; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcUnwrapErrPayload *) { - return IrInstSrcIdUnwrapErrPayload; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcFnProto *) { - return IrInstSrcIdFnProto; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcTestComptime *) { - return IrInstSrcIdTestComptime; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcPtrCast *) { - return IrInstSrcIdPtrCast; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcBitCast *) { - return IrInstSrcIdBitCast; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcIntToPtr *) { - return IrInstSrcIdIntToPtr; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcPtrToInt *) { - return IrInstSrcIdPtrToInt; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcIntToEnum *) { - return IrInstSrcIdIntToEnum; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcEnumToInt *) { - return IrInstSrcIdEnumToInt; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcIntToErr *) { - return IrInstSrcIdIntToErr; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcErrToInt *) { - return IrInstSrcIdErrToInt; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcCheckSwitchProngs *) { - return IrInstSrcIdCheckSwitchProngs; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcCheckStatementIsVoid *) { - return IrInstSrcIdCheckStatementIsVoid; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcTypeName *) { - return IrInstSrcIdTypeName; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcDeclRef *) { - return IrInstSrcIdDeclRef; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcPanic *) { - return IrInstSrcIdPanic; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcTagName *) { - return IrInstSrcIdTagName; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcTagType *) { - return IrInstSrcIdTagType; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcFieldParentPtr *) { - return IrInstSrcIdFieldParentPtr; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcByteOffsetOf *) { - return IrInstSrcIdByteOffsetOf; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcBitOffsetOf *) { - return IrInstSrcIdBitOffsetOf; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcTypeInfo *) { - return IrInstSrcIdTypeInfo; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcType *) { - return IrInstSrcIdType; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcHasField *) { - return IrInstSrcIdHasField; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcSetEvalBranchQuota *) { - return IrInstSrcIdSetEvalBranchQuota; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcPtrType *) { - return IrInstSrcIdPtrType; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcAlignCast *) { - return IrInstSrcIdAlignCast; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcImplicitCast *) { - return IrInstSrcIdImplicitCast; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcResolveResult *) { - return IrInstSrcIdResolveResult; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcResetResult *) { - return IrInstSrcIdResetResult; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcSetAlignStack *) { - return IrInstSrcIdSetAlignStack; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcArgType *) { - return IrInstSrcIdArgType; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcExport *) { - return IrInstSrcIdExport; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcErrorReturnTrace *) { - return IrInstSrcIdErrorReturnTrace; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcErrorUnion *) { - return IrInstSrcIdErrorUnion; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcAtomicRmw *) { - return IrInstSrcIdAtomicRmw; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcAtomicLoad *) { - return IrInstSrcIdAtomicLoad; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcAtomicStore *) { - return IrInstSrcIdAtomicStore; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcSaveErrRetAddr *) { - return IrInstSrcIdSaveErrRetAddr; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcAddImplicitReturnType *) { - return IrInstSrcIdAddImplicitReturnType; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcErrSetCast *) { - return IrInstSrcIdErrSetCast; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcCheckRuntimeScope *) { - return IrInstSrcIdCheckRuntimeScope; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcHasDecl *) { - return IrInstSrcIdHasDecl; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcUndeclaredIdent *) { - return IrInstSrcIdUndeclaredIdent; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcAlloca *) { - return IrInstSrcIdAlloca; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcEndExpr *) { - return IrInstSrcIdEndExpr; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcUnionInitNamedField *) { - return IrInstSrcIdUnionInitNamedField; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcSuspendBegin *) { - return IrInstSrcIdSuspendBegin; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcSuspendFinish *) { - return IrInstSrcIdSuspendFinish; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcAwait *) { - return IrInstSrcIdAwait; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcResume *) { - return IrInstSrcIdResume; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcSpillBegin *) { - return IrInstSrcIdSpillBegin; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcSpillEnd *) { - return IrInstSrcIdSpillEnd; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcWasmMemorySize *) { - return IrInstSrcIdWasmMemorySize; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcWasmMemoryGrow *) { - return IrInstSrcIdWasmMemoryGrow; -} - -static constexpr IrInstSrcId ir_inst_id(IrInstSrcSrc *) { - return IrInstSrcIdSrc; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenDeclVar *) { - return IrInstGenIdDeclVar; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenBr *) { - return IrInstGenIdBr; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenCondBr *) { - return IrInstGenIdCondBr; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenSwitchBr *) { - return IrInstGenIdSwitchBr; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenPhi *) { - return IrInstGenIdPhi; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenBinaryNot *) { - return IrInstGenIdBinaryNot; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenNegation *) { - return IrInstGenIdNegation; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenNegationWrapping *) { - return IrInstGenIdNegationWrapping; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenBinOp *) { - return IrInstGenIdBinOp; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenLoadPtr *) { - return IrInstGenIdLoadPtr; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenStorePtr *) { - return IrInstGenIdStorePtr; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenVectorStoreElem *) { - return IrInstGenIdVectorStoreElem; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenStructFieldPtr *) { - return IrInstGenIdStructFieldPtr; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenUnionFieldPtr *) { - return IrInstGenIdUnionFieldPtr; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenElemPtr *) { - return IrInstGenIdElemPtr; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenVarPtr *) { - return IrInstGenIdVarPtr; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenReturnPtr *) { - return IrInstGenIdReturnPtr; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenCall *) { - return IrInstGenIdCall; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenReturn *) { - return IrInstGenIdReturn; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenCast *) { - return IrInstGenIdCast; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenUnreachable *) { - return IrInstGenIdUnreachable; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenAsm *) { - return IrInstGenIdAsm; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenTestNonNull *) { - return IrInstGenIdTestNonNull; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenOptionalUnwrapPtr *) { - return IrInstGenIdOptionalUnwrapPtr; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenOptionalWrap *) { - return IrInstGenIdOptionalWrap; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenUnionTag *) { - return IrInstGenIdUnionTag; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenClz *) { - return IrInstGenIdClz; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenCtz *) { - return IrInstGenIdCtz; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenPopCount *) { - return IrInstGenIdPopCount; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenBswap *) { - return IrInstGenIdBswap; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenBitReverse *) { - return IrInstGenIdBitReverse; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenRef *) { - return IrInstGenIdRef; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenErrName *) { - return IrInstGenIdErrName; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenCmpxchg *) { - return IrInstGenIdCmpxchg; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenFence *) { - return IrInstGenIdFence; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenTruncate *) { - return IrInstGenIdTruncate; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenShuffleVector *) { - return IrInstGenIdShuffleVector; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenSplat *) { - return IrInstGenIdSplat; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenBoolNot *) { - return IrInstGenIdBoolNot; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenMemset *) { - return IrInstGenIdMemset; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenMemcpy *) { - return IrInstGenIdMemcpy; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenSlice *) { - return IrInstGenIdSlice; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenBreakpoint *) { - return IrInstGenIdBreakpoint; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenReturnAddress *) { - return IrInstGenIdReturnAddress; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenFrameAddress *) { - return IrInstGenIdFrameAddress; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenFrameHandle *) { - return IrInstGenIdFrameHandle; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenFrameSize *) { - return IrInstGenIdFrameSize; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenOverflowOp *) { - return IrInstGenIdOverflowOp; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenTestErr *) { - return IrInstGenIdTestErr; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenMulAdd *) { - return IrInstGenIdMulAdd; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenFloatOp *) { - return IrInstGenIdFloatOp; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenUnwrapErrCode *) { - return IrInstGenIdUnwrapErrCode; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenUnwrapErrPayload *) { - return IrInstGenIdUnwrapErrPayload; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenErrWrapCode *) { - return IrInstGenIdErrWrapCode; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenErrWrapPayload *) { - return IrInstGenIdErrWrapPayload; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenPtrCast *) { - return IrInstGenIdPtrCast; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenBitCast *) { - return IrInstGenIdBitCast; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenWidenOrShorten *) { - return IrInstGenIdWidenOrShorten; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenIntToPtr *) { - return IrInstGenIdIntToPtr; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenPtrToInt *) { - return IrInstGenIdPtrToInt; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenIntToEnum *) { - return IrInstGenIdIntToEnum; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenIntToErr *) { - return IrInstGenIdIntToErr; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenErrToInt *) { - return IrInstGenIdErrToInt; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenPanic *) { - return IrInstGenIdPanic; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenTagName *) { - return IrInstGenIdTagName; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenFieldParentPtr *) { - return IrInstGenIdFieldParentPtr; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenAlignCast *) { - return IrInstGenIdAlignCast; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenErrorReturnTrace *) { - return IrInstGenIdErrorReturnTrace; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenAtomicRmw *) { - return IrInstGenIdAtomicRmw; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenAtomicLoad *) { - return IrInstGenIdAtomicLoad; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenAtomicStore *) { - return IrInstGenIdAtomicStore; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenSaveErrRetAddr *) { - return IrInstGenIdSaveErrRetAddr; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenVectorToArray *) { - return IrInstGenIdVectorToArray; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenArrayToVector *) { - return IrInstGenIdArrayToVector; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenAssertZero *) { - return IrInstGenIdAssertZero; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenAssertNonNull *) { - return IrInstGenIdAssertNonNull; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenPtrOfArrayToSlice *) { - return IrInstGenIdPtrOfArrayToSlice; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenSuspendBegin *) { - return IrInstGenIdSuspendBegin; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenSuspendFinish *) { - return IrInstGenIdSuspendFinish; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenAwait *) { - return IrInstGenIdAwait; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenResume *) { - return IrInstGenIdResume; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenSpillBegin *) { - return IrInstGenIdSpillBegin; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenSpillEnd *) { - return IrInstGenIdSpillEnd; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenVectorExtractElem *) { - return IrInstGenIdVectorExtractElem; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenAlloca *) { - return IrInstGenIdAlloca; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenConst *) { - return IrInstGenIdConst; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenWasmMemorySize *) { - return IrInstGenIdWasmMemorySize; -} - -static constexpr IrInstGenId ir_inst_id(IrInstGenWasmMemoryGrow *) { - return IrInstGenIdWasmMemoryGrow; -} - -template -static T *ir_create_instruction(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { - T *special_instruction = heap::c_allocator.create(); - special_instruction->base.id = ir_inst_id(special_instruction); - special_instruction->base.base.scope = scope; - special_instruction->base.base.source_node = source_node; - special_instruction->base.base.debug_id = exec_next_debug_id(irb->exec); - special_instruction->base.owner_bb = irb->current_basic_block; - return special_instruction; -} - -template -static T *ir_create_inst_gen(IrBuilderGen *irb, Scope *scope, AstNode *source_node) { - T *special_instruction = heap::c_allocator.create(); - special_instruction->base.id = ir_inst_id(special_instruction); - special_instruction->base.base.scope = scope; - special_instruction->base.base.source_node = source_node; - special_instruction->base.base.debug_id = exec_next_debug_id_gen(irb->exec); - special_instruction->base.owner_bb = irb->current_basic_block; - special_instruction->base.value = irb->codegen->pass1_arena->create(); - return special_instruction; -} - -template -static T *ir_create_inst_noval(IrBuilderGen *irb, Scope *scope, AstNode *source_node) { - T *special_instruction = heap::c_allocator.create(); - special_instruction->base.id = ir_inst_id(special_instruction); - special_instruction->base.base.scope = scope; - special_instruction->base.base.source_node = source_node; - special_instruction->base.base.debug_id = exec_next_debug_id_gen(irb->exec); - special_instruction->base.owner_bb = irb->current_basic_block; - return special_instruction; -} - -template -static T *ir_build_instruction(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { - T *special_instruction = ir_create_instruction(irb, scope, source_node); - ir_instruction_append(irb->current_basic_block, &special_instruction->base); - return special_instruction; -} - -template -static T *ir_build_inst_gen(IrBuilderGen *irb, Scope *scope, AstNode *source_node) { - T *special_instruction = ir_create_inst_gen(irb, scope, source_node); - ir_inst_gen_append(irb->current_basic_block, &special_instruction->base); - return special_instruction; -} - -template -static T *ir_build_inst_noreturn(IrBuilderGen *irb, Scope *scope, AstNode *source_node) { - T *special_instruction = ir_create_inst_noval(irb, scope, source_node); - special_instruction->base.value = irb->codegen->intern.for_unreachable(); - ir_inst_gen_append(irb->current_basic_block, &special_instruction->base); - return special_instruction; -} - -template -static T *ir_build_inst_void(IrBuilderGen *irb, Scope *scope, AstNode *source_node) { - T *special_instruction = ir_create_inst_noval(irb, scope, source_node); - special_instruction->base.value = irb->codegen->intern.for_void(); - ir_inst_gen_append(irb->current_basic_block, &special_instruction->base); - return special_instruction; -} - -IrInstGen *ir_create_alloca(CodeGen *g, Scope *scope, AstNode *source_node, ZigFn *fn, - ZigType *var_type, const char *name_hint) -{ - IrInstGenAlloca *alloca_gen = heap::c_allocator.create(); - alloca_gen->base.id = IrInstGenIdAlloca; - alloca_gen->base.base.source_node = source_node; - alloca_gen->base.base.scope = scope; - alloca_gen->base.value = g->pass1_arena->create(); - alloca_gen->base.value->type = get_pointer_to_type(g, var_type, false); - alloca_gen->base.base.ref_count = 1; - alloca_gen->name_hint = name_hint; - fn->alloca_gen_list.append(alloca_gen); - return &alloca_gen->base; -} - -static IrInstGen *ir_build_cast(IrAnalyze *ira, IrInst *source_instr,ZigType *dest_type, - IrInstGen *value, CastOp cast_op) -{ - IrInstGenCast *inst = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); - inst->base.value->type = dest_type; - inst->value = value; - inst->cast_op = cast_op; - - ir_ref_inst_gen(value); - - return &inst->base; -} - -static IrInstSrc *ir_build_cond_br(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *condition, - IrBasicBlockSrc *then_block, IrBasicBlockSrc *else_block, IrInstSrc *is_comptime) -{ - IrInstSrcCondBr *inst = ir_build_instruction(irb, scope, source_node); - inst->base.is_noreturn = true; - inst->condition = condition; - inst->then_block = then_block; - inst->else_block = else_block; - inst->is_comptime = is_comptime; - - ir_ref_instruction(condition, irb->current_basic_block); - ir_ref_bb(then_block); - ir_ref_bb(else_block); - if (is_comptime != nullptr) ir_ref_instruction(is_comptime, irb->current_basic_block); - - return &inst->base; -} - -static IrInstGen *ir_build_cond_br_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *condition, - IrBasicBlockGen *then_block, IrBasicBlockGen *else_block) -{ - IrInstGenCondBr *inst = ir_build_inst_noreturn(&ira->new_irb, source_instr->scope, source_instr->source_node); - inst->condition = condition; - inst->then_block = then_block; - inst->else_block = else_block; - - ir_ref_inst_gen(condition); - - return &inst->base; -} - -static IrInstSrc *ir_build_return_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *operand) { - IrInstSrcReturn *inst = ir_build_instruction(irb, scope, source_node); - inst->base.is_noreturn = true; - inst->operand = operand; - - if (operand != nullptr) ir_ref_instruction(operand, irb->current_basic_block); - - return &inst->base; -} - -static IrInstGen *ir_build_return_gen(IrAnalyze *ira, IrInst *source_inst, IrInstGen *operand) { - IrInstGenReturn *inst = ir_build_inst_noreturn(&ira->new_irb, - source_inst->scope, source_inst->source_node); - inst->operand = operand; - - if (operand != nullptr) ir_ref_inst_gen(operand); - - return &inst->base; -} - -static IrInstSrc *ir_build_const_void(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { - IrInstSrcConst *const_instruction = ir_create_instruction(irb, scope, source_node); - ir_instruction_append(irb->current_basic_block, &const_instruction->base); - const_instruction->value = irb->codegen->intern.for_void(); - return &const_instruction->base; -} - -static IrInstSrc *ir_build_const_undefined(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { - IrInstSrcConst *const_instruction = ir_create_instruction(irb, scope, source_node); - ir_instruction_append(irb->current_basic_block, &const_instruction->base); - const_instruction->value = irb->codegen->intern.for_undefined(); - const_instruction->value->special = ConstValSpecialUndef; - return &const_instruction->base; -} - -static IrInstSrc *ir_build_const_uint(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, uint64_t value) { - IrInstSrcConst *const_instruction = ir_build_instruction(irb, scope, source_node); - const_instruction->value = irb->codegen->pass1_arena->create(); - const_instruction->value->type = irb->codegen->builtin_types.entry_num_lit_int; - const_instruction->value->special = ConstValSpecialStatic; - bigint_init_unsigned(&const_instruction->value->data.x_bigint, value); - return &const_instruction->base; -} - -static IrInstSrc *ir_build_const_bigint(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, BigInt *bigint) { - IrInstSrcConst *const_instruction = ir_build_instruction(irb, scope, source_node); - const_instruction->value = irb->codegen->pass1_arena->create(); - const_instruction->value->type = irb->codegen->builtin_types.entry_num_lit_int; - const_instruction->value->special = ConstValSpecialStatic; - bigint_init_bigint(&const_instruction->value->data.x_bigint, bigint); - return &const_instruction->base; -} - -static IrInstSrc *ir_build_const_bigfloat(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, BigFloat *bigfloat) { - IrInstSrcConst *const_instruction = ir_build_instruction(irb, scope, source_node); - const_instruction->value = irb->codegen->pass1_arena->create(); - const_instruction->value->type = irb->codegen->builtin_types.entry_num_lit_float; - const_instruction->value->special = ConstValSpecialStatic; - bigfloat_init_bigfloat(&const_instruction->value->data.x_bigfloat, bigfloat); - return &const_instruction->base; -} - -static IrInstSrc *ir_build_const_null(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { - IrInstSrcConst *const_instruction = ir_create_instruction(irb, scope, source_node); - ir_instruction_append(irb->current_basic_block, &const_instruction->base); - const_instruction->value = irb->codegen->intern.for_null(); - return &const_instruction->base; -} - -static IrInstSrc *ir_build_const_usize(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, uint64_t value) { - IrInstSrcConst *const_instruction = ir_build_instruction(irb, scope, source_node); - const_instruction->value = irb->codegen->pass1_arena->create(); - const_instruction->value->type = irb->codegen->builtin_types.entry_usize; - const_instruction->value->special = ConstValSpecialStatic; - bigint_init_unsigned(&const_instruction->value->data.x_bigint, value); - return &const_instruction->base; -} - -static IrInstSrc *ir_create_const_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - ZigType *type_entry) -{ - IrInstSrcConst *const_instruction = ir_create_instruction(irb, scope, source_node); - const_instruction->value = irb->codegen->pass1_arena->create(); - const_instruction->value->type = irb->codegen->builtin_types.entry_type; - const_instruction->value->special = ConstValSpecialStatic; - const_instruction->value->data.x_type = type_entry; - return &const_instruction->base; -} - -static IrInstSrc *ir_build_const_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - ZigType *type_entry) -{ - IrInstSrc *instruction = ir_create_const_type(irb, scope, source_node, type_entry); - ir_instruction_append(irb->current_basic_block, instruction); - return instruction; -} - -static IrInstSrc *ir_build_const_import(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, ZigType *import) { - IrInstSrcConst *const_instruction = ir_build_instruction(irb, scope, source_node); - const_instruction->value = irb->codegen->pass1_arena->create(); - const_instruction->value->type = irb->codegen->builtin_types.entry_type; - const_instruction->value->special = ConstValSpecialStatic; - const_instruction->value->data.x_type = import; - return &const_instruction->base; -} - -static IrInstSrc *ir_build_const_bool(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, bool value) { - IrInstSrcConst *const_instruction = ir_build_instruction(irb, scope, source_node); - const_instruction->value = irb->codegen->pass1_arena->create(); - const_instruction->value->type = irb->codegen->builtin_types.entry_bool; - const_instruction->value->special = ConstValSpecialStatic; - const_instruction->value->data.x_bool = value; - return &const_instruction->base; -} - -static IrInstSrc *ir_build_const_enum_literal(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, Buf *name) { - IrInstSrcConst *const_instruction = ir_build_instruction(irb, scope, source_node); - const_instruction->value = irb->codegen->pass1_arena->create(); - const_instruction->value->type = irb->codegen->builtin_types.entry_enum_literal; - const_instruction->value->special = ConstValSpecialStatic; - const_instruction->value->data.x_enum_literal = name; - return &const_instruction->base; -} - -static IrInstSrc *ir_create_const_str_lit(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, Buf *str) { - IrInstSrcConst *const_instruction = ir_create_instruction(irb, scope, source_node); - const_instruction->value = irb->codegen->pass1_arena->create(); - init_const_str_lit(irb->codegen, const_instruction->value, str); - - return &const_instruction->base; -} - -static IrInstSrc *ir_build_const_str_lit(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, Buf *str) { - IrInstSrc *instruction = ir_create_const_str_lit(irb, scope, source_node, str); - ir_instruction_append(irb->current_basic_block, instruction); - return instruction; -} - -static IrInstSrc *ir_build_bin_op(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrBinOp op_id, - IrInstSrc *op1, IrInstSrc *op2, bool safety_check_on) -{ - IrInstSrcBinOp *inst = ir_build_instruction(irb, scope, source_node); - inst->op_id = op_id; - inst->op1 = op1; - inst->op2 = op2; - inst->safety_check_on = safety_check_on; - - ir_ref_instruction(op1, irb->current_basic_block); - ir_ref_instruction(op2, irb->current_basic_block); - - return &inst->base; -} - -static IrInstGen *ir_build_bin_op_gen(IrAnalyze *ira, IrInst *source_instr, ZigType *res_type, - IrBinOp op_id, IrInstGen *op1, IrInstGen *op2, bool safety_check_on) -{ - IrInstGenBinOp *inst = ir_build_inst_gen(&ira->new_irb, - source_instr->scope, source_instr->source_node); - inst->base.value->type = res_type; - inst->op_id = op_id; - inst->op1 = op1; - inst->op2 = op2; - inst->safety_check_on = safety_check_on; - - ir_ref_inst_gen(op1); - ir_ref_inst_gen(op2); - - return &inst->base; -} - - -static IrInstSrc *ir_build_merge_err_sets(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *op1, IrInstSrc *op2, Buf *type_name) -{ - IrInstSrcMergeErrSets *inst = ir_build_instruction(irb, scope, source_node); - inst->op1 = op1; - inst->op2 = op2; - inst->type_name = type_name; - - ir_ref_instruction(op1, irb->current_basic_block); - ir_ref_instruction(op2, irb->current_basic_block); - - return &inst->base; -} - -static IrInstSrc *ir_build_var_ptr_x(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, ZigVar *var, - ScopeFnDef *crossed_fndef_scope) -{ - IrInstSrcVarPtr *instruction = ir_build_instruction(irb, scope, source_node); - instruction->var = var; - instruction->crossed_fndef_scope = crossed_fndef_scope; - - ir_ref_var(var); - - return &instruction->base; -} - -static IrInstSrc *ir_build_var_ptr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, ZigVar *var) { - return ir_build_var_ptr_x(irb, scope, source_node, var, nullptr); -} - -static IrInstGen *ir_build_var_ptr_gen(IrAnalyze *ira, IrInst *source_instr, ZigVar *var) { - IrInstGenVarPtr *instruction = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); - instruction->var = var; - - ir_ref_var(var); - - return &instruction->base; -} - -static IrInstGen *ir_build_return_ptr(IrAnalyze *ira, Scope *scope, AstNode *source_node, ZigType *ty) { - IrInstGenReturnPtr *instruction = ir_build_inst_gen(&ira->new_irb, scope, source_node); - instruction->base.value->type = ty; - return &instruction->base; -} - -static IrInstSrc *ir_build_elem_ptr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *array_ptr, IrInstSrc *elem_index, bool safety_check_on, PtrLen ptr_len, - AstNode *init_array_type_source_node) -{ - IrInstSrcElemPtr *instruction = ir_build_instruction(irb, scope, source_node); - instruction->array_ptr = array_ptr; - instruction->elem_index = elem_index; - instruction->safety_check_on = safety_check_on; - instruction->ptr_len = ptr_len; - instruction->init_array_type_source_node = init_array_type_source_node; - - ir_ref_instruction(array_ptr, irb->current_basic_block); - ir_ref_instruction(elem_index, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstGen *ir_build_elem_ptr_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node, - IrInstGen *array_ptr, IrInstGen *elem_index, bool safety_check_on, ZigType *return_type) -{ - IrInstGenElemPtr *instruction = ir_build_inst_gen(&ira->new_irb, scope, source_node); - instruction->base.value->type = return_type; - instruction->array_ptr = array_ptr; - instruction->elem_index = elem_index; - instruction->safety_check_on = safety_check_on; - - ir_ref_inst_gen(array_ptr); - ir_ref_inst_gen(elem_index); - - return &instruction->base; -} - -static IrInstSrc *ir_build_field_ptr_instruction(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *container_ptr, IrInstSrc *field_name_expr, bool initializing) -{ - IrInstSrcFieldPtr *instruction = ir_build_instruction(irb, scope, source_node); - instruction->container_ptr = container_ptr; - instruction->field_name_buffer = nullptr; - instruction->field_name_expr = field_name_expr; - instruction->initializing = initializing; - - ir_ref_instruction(container_ptr, irb->current_basic_block); - ir_ref_instruction(field_name_expr, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_field_ptr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *container_ptr, Buf *field_name, bool initializing) -{ - IrInstSrcFieldPtr *instruction = ir_build_instruction(irb, scope, source_node); - instruction->container_ptr = container_ptr; - instruction->field_name_buffer = field_name; - instruction->field_name_expr = nullptr; - instruction->initializing = initializing; - - ir_ref_instruction(container_ptr, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_has_field(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *container_type, IrInstSrc *field_name) -{ - IrInstSrcHasField *instruction = ir_build_instruction(irb, scope, source_node); - instruction->container_type = container_type; - instruction->field_name = field_name; - - ir_ref_instruction(container_type, irb->current_basic_block); - ir_ref_instruction(field_name, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstGen *ir_build_struct_field_ptr(IrAnalyze *ira, IrInst *source_instr, - IrInstGen *struct_ptr, TypeStructField *field, ZigType *ptr_type) -{ - IrInstGenStructFieldPtr *inst = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); - inst->base.value->type = ptr_type; - inst->struct_ptr = struct_ptr; - inst->field = field; - - ir_ref_inst_gen(struct_ptr); - - return &inst->base; -} - -static IrInstGen *ir_build_union_field_ptr(IrAnalyze *ira, IrInst *source_instr, - IrInstGen *union_ptr, TypeUnionField *field, bool safety_check_on, bool initializing, ZigType *ptr_type) -{ - IrInstGenUnionFieldPtr *inst = ir_build_inst_gen(&ira->new_irb, - source_instr->scope, source_instr->source_node); - inst->base.value->type = ptr_type; - inst->initializing = initializing; - inst->safety_check_on = safety_check_on; - inst->union_ptr = union_ptr; - inst->field = field; - - ir_ref_inst_gen(union_ptr); - - return &inst->base; -} - -static IrInstSrc *ir_build_call_extra(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *options, IrInstSrc *fn_ref, IrInstSrc *args, ResultLoc *result_loc) -{ - IrInstSrcCallExtra *call_instruction = ir_build_instruction(irb, scope, source_node); - call_instruction->options = options; - call_instruction->fn_ref = fn_ref; - call_instruction->args = args; - call_instruction->result_loc = result_loc; - - ir_ref_instruction(options, irb->current_basic_block); - ir_ref_instruction(fn_ref, irb->current_basic_block); - ir_ref_instruction(args, irb->current_basic_block); - - return &call_instruction->base; -} - -static IrInstSrc *ir_build_async_call_extra(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - CallModifier modifier, IrInstSrc *fn_ref, IrInstSrc *ret_ptr, IrInstSrc *new_stack, IrInstSrc *args, ResultLoc *result_loc) -{ - IrInstSrcAsyncCallExtra *call_instruction = ir_build_instruction(irb, scope, source_node); - call_instruction->modifier = modifier; - call_instruction->fn_ref = fn_ref; - call_instruction->ret_ptr = ret_ptr; - call_instruction->new_stack = new_stack; - call_instruction->args = args; - call_instruction->result_loc = result_loc; - - ir_ref_instruction(fn_ref, irb->current_basic_block); - if (ret_ptr != nullptr) ir_ref_instruction(ret_ptr, irb->current_basic_block); - ir_ref_instruction(new_stack, irb->current_basic_block); - ir_ref_instruction(args, irb->current_basic_block); - - return &call_instruction->base; -} - -static IrInstSrc *ir_build_call_args(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *options, IrInstSrc *fn_ref, IrInstSrc **args_ptr, size_t args_len, - ResultLoc *result_loc) -{ - IrInstSrcCallArgs *call_instruction = ir_build_instruction(irb, scope, source_node); - call_instruction->options = options; - call_instruction->fn_ref = fn_ref; - call_instruction->args_ptr = args_ptr; - call_instruction->args_len = args_len; - call_instruction->result_loc = result_loc; - - ir_ref_instruction(options, irb->current_basic_block); - ir_ref_instruction(fn_ref, irb->current_basic_block); - for (size_t i = 0; i < args_len; i += 1) - ir_ref_instruction(args_ptr[i], irb->current_basic_block); - - return &call_instruction->base; -} - -static IrInstSrc *ir_build_call_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - ZigFn *fn_entry, IrInstSrc *fn_ref, size_t arg_count, IrInstSrc **args, - IrInstSrc *ret_ptr, CallModifier modifier, bool is_async_call_builtin, - IrInstSrc *new_stack, ResultLoc *result_loc) -{ - IrInstSrcCall *call_instruction = ir_build_instruction(irb, scope, source_node); - call_instruction->fn_entry = fn_entry; - call_instruction->fn_ref = fn_ref; - call_instruction->args = args; - call_instruction->arg_count = arg_count; - call_instruction->modifier = modifier; - call_instruction->is_async_call_builtin = is_async_call_builtin; - call_instruction->new_stack = new_stack; - call_instruction->result_loc = result_loc; - call_instruction->ret_ptr = ret_ptr; - - if (fn_ref != nullptr) ir_ref_instruction(fn_ref, irb->current_basic_block); - for (size_t i = 0; i < arg_count; i += 1) - ir_ref_instruction(args[i], irb->current_basic_block); - if (ret_ptr != nullptr) ir_ref_instruction(ret_ptr, irb->current_basic_block); - if (new_stack != nullptr) ir_ref_instruction(new_stack, irb->current_basic_block); - - return &call_instruction->base; -} - -static IrInstGenCall *ir_build_call_gen(IrAnalyze *ira, IrInst *source_instruction, - ZigFn *fn_entry, IrInstGen *fn_ref, size_t arg_count, IrInstGen **args, - CallModifier modifier, IrInstGen *new_stack, bool is_async_call_builtin, - IrInstGen *result_loc, ZigType *return_type) -{ - IrInstGenCall *call_instruction = ir_build_inst_gen(&ira->new_irb, - source_instruction->scope, source_instruction->source_node); - call_instruction->base.value->type = return_type; - call_instruction->fn_entry = fn_entry; - call_instruction->fn_ref = fn_ref; - call_instruction->args = args; - call_instruction->arg_count = arg_count; - call_instruction->modifier = modifier; - call_instruction->is_async_call_builtin = is_async_call_builtin; - call_instruction->new_stack = new_stack; - call_instruction->result_loc = result_loc; - - if (fn_ref != nullptr) ir_ref_inst_gen(fn_ref); - for (size_t i = 0; i < arg_count; i += 1) - ir_ref_inst_gen(args[i]); - if (new_stack != nullptr) ir_ref_inst_gen(new_stack); - if (result_loc != nullptr) ir_ref_inst_gen(result_loc); - - return call_instruction; -} - -static IrInstSrc *ir_build_phi(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - size_t incoming_count, IrBasicBlockSrc **incoming_blocks, IrInstSrc **incoming_values, - ResultLocPeerParent *peer_parent) -{ - assert(incoming_count != 0); - assert(incoming_count != SIZE_MAX); - - IrInstSrcPhi *phi_instruction = ir_build_instruction(irb, scope, source_node); - phi_instruction->incoming_count = incoming_count; - phi_instruction->incoming_blocks = incoming_blocks; - phi_instruction->incoming_values = incoming_values; - phi_instruction->peer_parent = peer_parent; - - for (size_t i = 0; i < incoming_count; i += 1) { - ir_ref_bb(incoming_blocks[i]); - ir_ref_instruction(incoming_values[i], irb->current_basic_block); - } - - return &phi_instruction->base; -} - -static IrInstGen *ir_build_phi_gen(IrAnalyze *ira, IrInst *source_instr, size_t incoming_count, - IrBasicBlockGen **incoming_blocks, IrInstGen **incoming_values, ZigType *result_type) -{ - assert(incoming_count != 0); - assert(incoming_count != SIZE_MAX); - - IrInstGenPhi *phi_instruction = ir_build_inst_gen(&ira->new_irb, - source_instr->scope, source_instr->source_node); - phi_instruction->base.value->type = result_type; - phi_instruction->incoming_count = incoming_count; - phi_instruction->incoming_blocks = incoming_blocks; - phi_instruction->incoming_values = incoming_values; - - for (size_t i = 0; i < incoming_count; i += 1) { - ir_ref_inst_gen(incoming_values[i]); - } - - return &phi_instruction->base; -} - -static IrInstSrc *ir_build_br(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrBasicBlockSrc *dest_block, IrInstSrc *is_comptime) -{ - IrInstSrcBr *inst = ir_build_instruction(irb, scope, source_node); - inst->base.is_noreturn = true; - inst->dest_block = dest_block; - inst->is_comptime = is_comptime; - - ir_ref_bb(dest_block); - if (is_comptime) ir_ref_instruction(is_comptime, irb->current_basic_block); - - return &inst->base; -} - -static IrInstGen *ir_build_br_gen(IrAnalyze *ira, IrInst *source_instr, IrBasicBlockGen *dest_block) { - IrInstGenBr *inst = ir_build_inst_noreturn(&ira->new_irb, source_instr->scope, source_instr->source_node); - inst->dest_block = dest_block; - - return &inst->base; -} - -static IrInstSrc *ir_build_ptr_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *child_type, bool is_const, bool is_volatile, PtrLen ptr_len, - IrInstSrc *sentinel, IrInstSrc *align_value, - uint32_t bit_offset_start, uint32_t host_int_bytes, bool is_allow_zero) -{ - IrInstSrcPtrType *inst = ir_build_instruction(irb, scope, source_node); - inst->sentinel = sentinel; - inst->align_value = align_value; - inst->child_type = child_type; - inst->is_const = is_const; - inst->is_volatile = is_volatile; - inst->ptr_len = ptr_len; - inst->bit_offset_start = bit_offset_start; - inst->host_int_bytes = host_int_bytes; - inst->is_allow_zero = is_allow_zero; - - if (sentinel) ir_ref_instruction(sentinel, irb->current_basic_block); - if (align_value) ir_ref_instruction(align_value, irb->current_basic_block); - ir_ref_instruction(child_type, irb->current_basic_block); - - return &inst->base; -} - -static IrInstSrc *ir_build_un_op_lval(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrUnOp op_id, - IrInstSrc *value, LVal lval, ResultLoc *result_loc) -{ - IrInstSrcUnOp *instruction = ir_build_instruction(irb, scope, source_node); - instruction->op_id = op_id; - instruction->value = value; - instruction->lval = lval; - instruction->result_loc = result_loc; - - ir_ref_instruction(value, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_un_op(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrUnOp op_id, - IrInstSrc *value) -{ - return ir_build_un_op_lval(irb, scope, source_node, op_id, value, LValNone, nullptr); -} - -static IrInstGen *ir_build_negation(IrAnalyze *ira, IrInst *source_instr, IrInstGen *operand, ZigType *expr_type) { - IrInstGenNegation *instruction = ir_build_inst_gen(&ira->new_irb, - source_instr->scope, source_instr->source_node); - instruction->base.value->type = expr_type; - instruction->operand = operand; - - ir_ref_inst_gen(operand); - - return &instruction->base; -} - -static IrInstGen *ir_build_negation_wrapping(IrAnalyze *ira, IrInst *source_instr, IrInstGen *operand, - ZigType *expr_type) -{ - IrInstGenNegationWrapping *instruction = ir_build_inst_gen(&ira->new_irb, - source_instr->scope, source_instr->source_node); - instruction->base.value->type = expr_type; - instruction->operand = operand; - - ir_ref_inst_gen(operand); - - return &instruction->base; -} - -static IrInstGen *ir_build_binary_not(IrAnalyze *ira, IrInst *source_instr, IrInstGen *operand, - ZigType *expr_type) -{ - IrInstGenBinaryNot *instruction = ir_build_inst_gen(&ira->new_irb, - source_instr->scope, source_instr->source_node); - instruction->base.value->type = expr_type; - instruction->operand = operand; - - ir_ref_inst_gen(operand); - - return &instruction->base; -} - -static IrInstSrc *ir_build_container_init_list(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - size_t item_count, IrInstSrc **elem_result_loc_list, IrInstSrc *result_loc, - AstNode *init_array_type_source_node) -{ - IrInstSrcContainerInitList *container_init_list_instruction = - ir_build_instruction(irb, scope, source_node); - container_init_list_instruction->item_count = item_count; - container_init_list_instruction->elem_result_loc_list = elem_result_loc_list; - container_init_list_instruction->result_loc = result_loc; - container_init_list_instruction->init_array_type_source_node = init_array_type_source_node; - - for (size_t i = 0; i < item_count; i += 1) { - ir_ref_instruction(elem_result_loc_list[i], irb->current_basic_block); - } - if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block); - - return &container_init_list_instruction->base; -} - -static IrInstSrc *ir_build_container_init_fields(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - size_t field_count, IrInstSrcContainerInitFieldsField *fields, IrInstSrc *result_loc) -{ - IrInstSrcContainerInitFields *container_init_fields_instruction = - ir_build_instruction(irb, scope, source_node); - container_init_fields_instruction->field_count = field_count; - container_init_fields_instruction->fields = fields; - container_init_fields_instruction->result_loc = result_loc; - - for (size_t i = 0; i < field_count; i += 1) { - ir_ref_instruction(fields[i].result_loc, irb->current_basic_block); - } - if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block); - - return &container_init_fields_instruction->base; -} - -static IrInstSrc *ir_build_unreachable(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { - IrInstSrcUnreachable *inst = ir_build_instruction(irb, scope, source_node); - inst->base.is_noreturn = true; - return &inst->base; -} - -static IrInstGen *ir_build_unreachable_gen(IrAnalyze *ira, IrInst *source_instr) { - IrInstGenUnreachable *inst = ir_build_inst_noreturn(&ira->new_irb, source_instr->scope, source_instr->source_node); - return &inst->base; -} - -static IrInstSrcStorePtr *ir_build_store_ptr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *ptr, IrInstSrc *value) -{ - IrInstSrcStorePtr *instruction = ir_build_instruction(irb, scope, source_node); - instruction->ptr = ptr; - instruction->value = value; - - ir_ref_instruction(ptr, irb->current_basic_block); - ir_ref_instruction(value, irb->current_basic_block); - - return instruction; -} - -static IrInstGen *ir_build_store_ptr_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *ptr, IrInstGen *value) { - IrInstGenStorePtr *instruction = ir_build_inst_void(&ira->new_irb, - source_instr->scope, source_instr->source_node); - instruction->ptr = ptr; - instruction->value = value; - - ir_ref_inst_gen(ptr); - ir_ref_inst_gen(value); - - return &instruction->base; -} - -static IrInstGen *ir_build_vector_store_elem(IrAnalyze *ira, IrInst *src_inst, - IrInstGen *vector_ptr, IrInstGen *index, IrInstGen *value) -{ - IrInstGenVectorStoreElem *inst = ir_build_inst_void( - &ira->new_irb, src_inst->scope, src_inst->source_node); - inst->vector_ptr = vector_ptr; - inst->index = index; - inst->value = value; - - ir_ref_inst_gen(vector_ptr); - ir_ref_inst_gen(index); - ir_ref_inst_gen(value); - - return &inst->base; -} - -static IrInstSrc *ir_build_var_decl_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - ZigVar *var, IrInstSrc *align_value, IrInstSrc *ptr) -{ - IrInstSrcDeclVar *inst = ir_build_instruction(irb, scope, source_node); - inst->var = var; - inst->align_value = align_value; - inst->ptr = ptr; - - if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block); - ir_ref_instruction(ptr, irb->current_basic_block); - - return &inst->base; -} - -static IrInstGen *ir_build_var_decl_gen(IrAnalyze *ira, IrInst *source_instruction, - ZigVar *var, IrInstGen *var_ptr) -{ - IrInstGenDeclVar *inst = ir_build_inst_gen(&ira->new_irb, - source_instruction->scope, source_instruction->source_node); - inst->base.value->special = ConstValSpecialStatic; - inst->base.value->type = ira->codegen->builtin_types.entry_void; - inst->var = var; - inst->var_ptr = var_ptr; - - ir_ref_inst_gen(var_ptr); - - return &inst->base; -} - -static IrInstSrc *ir_build_export(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *target, IrInstSrc *options) -{ - IrInstSrcExport *export_instruction = ir_build_instruction( - irb, scope, source_node); - export_instruction->target = target; - export_instruction->options = options; - - ir_ref_instruction(target, irb->current_basic_block); - ir_ref_instruction(options, irb->current_basic_block); - - return &export_instruction->base; -} - -static IrInstSrc *ir_build_load_ptr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *ptr) { - IrInstSrcLoadPtr *instruction = ir_build_instruction(irb, scope, source_node); - instruction->ptr = ptr; - - ir_ref_instruction(ptr, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstGen *ir_build_load_ptr_gen(IrAnalyze *ira, IrInst *source_instruction, - IrInstGen *ptr, ZigType *ty, IrInstGen *result_loc) -{ - IrInstGenLoadPtr *instruction = ir_build_inst_gen( - &ira->new_irb, source_instruction->scope, source_instruction->source_node); - instruction->base.value->type = ty; - instruction->ptr = ptr; - instruction->result_loc = result_loc; - - ir_ref_inst_gen(ptr); - if (result_loc != nullptr) ir_ref_inst_gen(result_loc); - - return &instruction->base; -} - -static IrInstSrc *ir_build_typeof_n(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc **values, size_t value_count) -{ - assert(value_count >= 2); - - IrInstSrcTypeOf *instruction = ir_build_instruction(irb, scope, source_node); - instruction->value.list = values; - instruction->value_count = value_count; - - for (size_t i = 0; i < value_count; i++) - ir_ref_instruction(values[i], irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_typeof_1(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *value) { - IrInstSrcTypeOf *instruction = ir_build_instruction(irb, scope, source_node); - instruction->value.scalar = value; - - ir_ref_instruction(value, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_set_cold(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *is_cold) { - IrInstSrcSetCold *instruction = ir_build_instruction(irb, scope, source_node); - instruction->is_cold = is_cold; - - ir_ref_instruction(is_cold, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_set_runtime_safety(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *safety_on) -{ - IrInstSrcSetRuntimeSafety *inst = ir_build_instruction(irb, scope, source_node); - inst->safety_on = safety_on; - - ir_ref_instruction(safety_on, irb->current_basic_block); - - return &inst->base; -} - -static IrInstSrc *ir_build_set_float_mode(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *mode_value) -{ - IrInstSrcSetFloatMode *instruction = ir_build_instruction(irb, scope, source_node); - instruction->mode_value = mode_value; - - ir_ref_instruction(mode_value, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_array_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *size, - IrInstSrc *sentinel, IrInstSrc *child_type) -{ - IrInstSrcArrayType *instruction = ir_build_instruction(irb, scope, source_node); - instruction->size = size; - instruction->sentinel = sentinel; - instruction->child_type = child_type; - - ir_ref_instruction(size, irb->current_basic_block); - if (sentinel != nullptr) ir_ref_instruction(sentinel, irb->current_basic_block); - ir_ref_instruction(child_type, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_anyframe_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *payload_type) -{ - IrInstSrcAnyFrameType *instruction = ir_build_instruction(irb, scope, source_node); - instruction->payload_type = payload_type; - - if (payload_type != nullptr) ir_ref_instruction(payload_type, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_slice_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *child_type, bool is_const, bool is_volatile, - IrInstSrc *sentinel, IrInstSrc *align_value, bool is_allow_zero) -{ - IrInstSrcSliceType *instruction = ir_build_instruction(irb, scope, source_node); - instruction->is_const = is_const; - instruction->is_volatile = is_volatile; - instruction->child_type = child_type; - instruction->sentinel = sentinel; - instruction->align_value = align_value; - instruction->is_allow_zero = is_allow_zero; - - if (sentinel != nullptr) ir_ref_instruction(sentinel, irb->current_basic_block); - if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block); - ir_ref_instruction(child_type, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_asm_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *asm_template, IrInstSrc **input_list, IrInstSrc **output_types, - ZigVar **output_vars, size_t return_count, bool has_side_effects, bool is_global) -{ - IrInstSrcAsm *instruction = ir_build_instruction(irb, scope, source_node); - instruction->asm_template = asm_template; - instruction->input_list = input_list; - instruction->output_types = output_types; - instruction->output_vars = output_vars; - instruction->return_count = return_count; - instruction->has_side_effects = has_side_effects; - instruction->is_global = is_global; - - assert(source_node->type == NodeTypeAsmExpr); - for (size_t i = 0; i < source_node->data.asm_expr.output_list.length; i += 1) { - IrInstSrc *output_type = output_types[i]; - if (output_type) ir_ref_instruction(output_type, irb->current_basic_block); - } - - for (size_t i = 0; i < source_node->data.asm_expr.input_list.length; i += 1) { - IrInstSrc *input_value = input_list[i]; - ir_ref_instruction(input_value, irb->current_basic_block); - } - - return &instruction->base; -} - -static IrInstGen *ir_build_asm_gen(IrAnalyze *ira, IrInst *source_instr, - 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, ZigType *return_type) -{ - IrInstGenAsm *instruction = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); - instruction->base.value->type = return_type; - instruction->asm_template = asm_template; - instruction->token_list = token_list; - instruction->token_list_len = token_list_len; - instruction->input_list = input_list; - instruction->output_types = output_types; - instruction->output_vars = output_vars; - instruction->return_count = return_count; - instruction->has_side_effects = has_side_effects; - - assert(source_instr->source_node->type == NodeTypeAsmExpr); - for (size_t i = 0; i < source_instr->source_node->data.asm_expr.output_list.length; i += 1) { - IrInstGen *output_type = output_types[i]; - if (output_type) ir_ref_inst_gen(output_type); - } - - for (size_t i = 0; i < source_instr->source_node->data.asm_expr.input_list.length; i += 1) { - IrInstGen *input_value = input_list[i]; - ir_ref_inst_gen(input_value); - } - - return &instruction->base; -} - -static IrInstSrc *ir_build_size_of(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type_value, - bool bit_size) -{ - IrInstSrcSizeOf *instruction = ir_build_instruction(irb, scope, source_node); - instruction->type_value = type_value; - instruction->bit_size = bit_size; - - ir_ref_instruction(type_value, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_test_non_null_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *value) -{ - IrInstSrcTestNonNull *instruction = ir_build_instruction(irb, scope, source_node); - instruction->value = value; - - ir_ref_instruction(value, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstGen *ir_build_test_non_null_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *value) { - IrInstGenTestNonNull *inst = ir_build_inst_gen(&ira->new_irb, - source_instr->scope, source_instr->source_node); - inst->base.value->type = ira->codegen->builtin_types.entry_bool; - inst->value = value; - - ir_ref_inst_gen(value); - - return &inst->base; -} - -static IrInstSrc *ir_build_optional_unwrap_ptr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *base_ptr, bool safety_check_on) -{ - IrInstSrcOptionalUnwrapPtr *instruction = ir_build_instruction(irb, scope, source_node); - instruction->base_ptr = base_ptr; - instruction->safety_check_on = safety_check_on; - - ir_ref_instruction(base_ptr, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstGen *ir_build_optional_unwrap_ptr_gen(IrAnalyze *ira, IrInst *source_instr, - IrInstGen *base_ptr, bool safety_check_on, bool initializing, ZigType *result_type) -{ - IrInstGenOptionalUnwrapPtr *inst = ir_build_inst_gen(&ira->new_irb, - source_instr->scope, source_instr->source_node); - inst->base.value->type = result_type; - inst->base_ptr = base_ptr; - inst->safety_check_on = safety_check_on; - inst->initializing = initializing; - - ir_ref_inst_gen(base_ptr); - - return &inst->base; -} - -static IrInstGen *ir_build_optional_wrap(IrAnalyze *ira, IrInst *source_instruction, ZigType *result_ty, - IrInstGen *operand, IrInstGen *result_loc) -{ - IrInstGenOptionalWrap *instruction = ir_build_inst_gen( - &ira->new_irb, source_instruction->scope, source_instruction->source_node); - instruction->base.value->type = result_ty; - instruction->operand = operand; - instruction->result_loc = result_loc; - - ir_ref_inst_gen(operand); - if (result_loc != nullptr) ir_ref_inst_gen(result_loc); - - return &instruction->base; -} - -static IrInstGen *ir_build_err_wrap_payload(IrAnalyze *ira, IrInst *source_instruction, - ZigType *result_type, IrInstGen *operand, IrInstGen *result_loc) -{ - IrInstGenErrWrapPayload *instruction = ir_build_inst_gen( - &ira->new_irb, source_instruction->scope, source_instruction->source_node); - instruction->base.value->type = result_type; - instruction->operand = operand; - instruction->result_loc = result_loc; - - ir_ref_inst_gen(operand); - if (result_loc != nullptr) ir_ref_inst_gen(result_loc); - - return &instruction->base; -} - -static IrInstGen *ir_build_err_wrap_code(IrAnalyze *ira, IrInst *source_instruction, - ZigType *result_type, IrInstGen *operand, IrInstGen *result_loc) -{ - IrInstGenErrWrapCode *instruction = ir_build_inst_gen( - &ira->new_irb, source_instruction->scope, source_instruction->source_node); - instruction->base.value->type = result_type; - instruction->operand = operand; - instruction->result_loc = result_loc; - - ir_ref_inst_gen(operand); - if (result_loc != nullptr) ir_ref_inst_gen(result_loc); - - return &instruction->base; -} - -static IrInstSrc *ir_build_clz(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type, - IrInstSrc *op) -{ - IrInstSrcClz *instruction = ir_build_instruction(irb, scope, source_node); - instruction->type = type; - instruction->op = op; - - ir_ref_instruction(type, irb->current_basic_block); - ir_ref_instruction(op, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstGen *ir_build_clz_gen(IrAnalyze *ira, IrInst *source_instr, ZigType *result_type, IrInstGen *op) { - IrInstGenClz *instruction = ir_build_inst_gen(&ira->new_irb, - source_instr->scope, source_instr->source_node); - instruction->base.value->type = result_type; - instruction->op = op; - - ir_ref_inst_gen(op); - - return &instruction->base; -} - -static IrInstSrc *ir_build_ctz(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type, - IrInstSrc *op) -{ - IrInstSrcCtz *instruction = ir_build_instruction(irb, scope, source_node); - instruction->type = type; - instruction->op = op; - - ir_ref_instruction(type, irb->current_basic_block); - ir_ref_instruction(op, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstGen *ir_build_ctz_gen(IrAnalyze *ira, IrInst *source_instr, ZigType *result_type, IrInstGen *op) { - IrInstGenCtz *instruction = ir_build_inst_gen(&ira->new_irb, - source_instr->scope, source_instr->source_node); - instruction->base.value->type = result_type; - instruction->op = op; - - ir_ref_inst_gen(op); - - return &instruction->base; -} - -static IrInstSrc *ir_build_pop_count(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type, - IrInstSrc *op) -{ - IrInstSrcPopCount *instruction = ir_build_instruction(irb, scope, source_node); - instruction->type = type; - instruction->op = op; - - ir_ref_instruction(type, irb->current_basic_block); - ir_ref_instruction(op, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstGen *ir_build_pop_count_gen(IrAnalyze *ira, IrInst *source_instr, ZigType *result_type, - IrInstGen *op) -{ - IrInstGenPopCount *instruction = ir_build_inst_gen(&ira->new_irb, - source_instr->scope, source_instr->source_node); - instruction->base.value->type = result_type; - instruction->op = op; - - ir_ref_inst_gen(op); - - return &instruction->base; -} - -static IrInstSrc *ir_build_bswap(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type, - IrInstSrc *op) -{ - IrInstSrcBswap *instruction = ir_build_instruction(irb, scope, source_node); - instruction->type = type; - instruction->op = op; - - ir_ref_instruction(type, irb->current_basic_block); - ir_ref_instruction(op, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstGen *ir_build_bswap_gen(IrAnalyze *ira, IrInst *source_instr, ZigType *op_type, - IrInstGen *op) -{ - IrInstGenBswap *instruction = ir_build_inst_gen(&ira->new_irb, - source_instr->scope, source_instr->source_node); - instruction->base.value->type = op_type; - instruction->op = op; - - ir_ref_inst_gen(op); - - return &instruction->base; -} - -static IrInstSrc *ir_build_bit_reverse(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type, - IrInstSrc *op) -{ - IrInstSrcBitReverse *instruction = ir_build_instruction(irb, scope, source_node); - instruction->type = type; - instruction->op = op; - - ir_ref_instruction(type, irb->current_basic_block); - ir_ref_instruction(op, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstGen *ir_build_bit_reverse_gen(IrAnalyze *ira, IrInst *source_instr, ZigType *int_type, - IrInstGen *op) -{ - IrInstGenBitReverse *instruction = ir_build_inst_gen(&ira->new_irb, - source_instr->scope, source_instr->source_node); - instruction->base.value->type = int_type; - instruction->op = op; - - ir_ref_inst_gen(op); - - return &instruction->base; -} - -static IrInstSrcSwitchBr *ir_build_switch_br_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *target_value, IrBasicBlockSrc *else_block, size_t case_count, IrInstSrcSwitchBrCase *cases, - IrInstSrc *is_comptime, IrInstSrc *switch_prongs_void) -{ - IrInstSrcSwitchBr *instruction = ir_build_instruction(irb, scope, source_node); - instruction->base.is_noreturn = true; - instruction->target_value = target_value; - instruction->else_block = else_block; - instruction->case_count = case_count; - instruction->cases = cases; - instruction->is_comptime = is_comptime; - instruction->switch_prongs_void = switch_prongs_void; - - ir_ref_instruction(target_value, irb->current_basic_block); - ir_ref_instruction(is_comptime, irb->current_basic_block); - ir_ref_bb(else_block); - ir_ref_instruction(switch_prongs_void, irb->current_basic_block); - - for (size_t i = 0; i < case_count; i += 1) { - ir_ref_instruction(cases[i].value, irb->current_basic_block); - ir_ref_bb(cases[i].block); - } - - return instruction; -} - -static IrInstGenSwitchBr *ir_build_switch_br_gen(IrAnalyze *ira, IrInst *source_instr, - IrInstGen *target_value, IrBasicBlockGen *else_block, size_t case_count, IrInstGenSwitchBrCase *cases) -{ - IrInstGenSwitchBr *instruction = ir_build_inst_noreturn(&ira->new_irb, - source_instr->scope, source_instr->source_node); - instruction->target_value = target_value; - instruction->else_block = else_block; - instruction->case_count = case_count; - instruction->cases = cases; - - ir_ref_inst_gen(target_value); - - for (size_t i = 0; i < case_count; i += 1) { - ir_ref_inst_gen(cases[i].value); - } - - return instruction; -} - -static IrInstSrc *ir_build_switch_target(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *target_value_ptr) -{ - IrInstSrcSwitchTarget *instruction = ir_build_instruction(irb, scope, source_node); - instruction->target_value_ptr = target_value_ptr; - - ir_ref_instruction(target_value_ptr, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_switch_var(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *target_value_ptr, IrInstSrc **prongs_ptr, size_t prongs_len) -{ - IrInstSrcSwitchVar *instruction = ir_build_instruction(irb, scope, source_node); - instruction->target_value_ptr = target_value_ptr; - instruction->prongs_ptr = prongs_ptr; - instruction->prongs_len = prongs_len; - - ir_ref_instruction(target_value_ptr, irb->current_basic_block); - for (size_t i = 0; i < prongs_len; i += 1) { - ir_ref_instruction(prongs_ptr[i], irb->current_basic_block); - } - - return &instruction->base; -} - -// For this instruction the switch_br must be set later. -static IrInstSrcSwitchElseVar *ir_build_switch_else_var(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *target_value_ptr) -{ - IrInstSrcSwitchElseVar *instruction = ir_build_instruction(irb, scope, source_node); - instruction->target_value_ptr = target_value_ptr; - - ir_ref_instruction(target_value_ptr, irb->current_basic_block); - - return instruction; -} - -static IrInstGen *ir_build_union_tag(IrAnalyze *ira, IrInst *source_instr, IrInstGen *value, - ZigType *tag_type) -{ - IrInstGenUnionTag *instruction = ir_build_inst_gen(&ira->new_irb, - source_instr->scope, source_instr->source_node); - instruction->value = value; - instruction->base.value->type = tag_type; - - ir_ref_inst_gen(value); - - return &instruction->base; -} - -static IrInstSrc *ir_build_import(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *name) { - IrInstSrcImport *instruction = ir_build_instruction(irb, scope, source_node); - instruction->name = name; - - ir_ref_instruction(name, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_ref_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *value) { - IrInstSrcRef *instruction = ir_build_instruction(irb, scope, source_node); - instruction->value = value; - - ir_ref_instruction(value, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstGen *ir_build_ref_gen(IrAnalyze *ira, IrInst *source_instruction, ZigType *result_type, - IrInstGen *operand, IrInstGen *result_loc) -{ - IrInstGenRef *instruction = ir_build_inst_gen(&ira->new_irb, - source_instruction->scope, source_instruction->source_node); - instruction->base.value->type = result_type; - instruction->operand = operand; - instruction->result_loc = result_loc; - - ir_ref_inst_gen(operand); - if (result_loc != nullptr) ir_ref_inst_gen(result_loc); - - return &instruction->base; -} - -static IrInstSrc *ir_build_compile_err(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *msg) { - IrInstSrcCompileErr *instruction = ir_build_instruction(irb, scope, source_node); - instruction->msg = msg; - - ir_ref_instruction(msg, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_compile_log(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - size_t msg_count, IrInstSrc **msg_list) -{ - IrInstSrcCompileLog *instruction = ir_build_instruction(irb, scope, source_node); - instruction->msg_count = msg_count; - instruction->msg_list = msg_list; - - for (size_t i = 0; i < msg_count; i += 1) { - ir_ref_instruction(msg_list[i], irb->current_basic_block); - } - - return &instruction->base; -} - -static IrInstSrc *ir_build_err_name(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *value) { - IrInstSrcErrName *instruction = ir_build_instruction(irb, scope, source_node); - instruction->value = value; - - ir_ref_instruction(value, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstGen *ir_build_err_name_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *value, - ZigType *str_type) -{ - IrInstGenErrName *instruction = ir_build_inst_gen(&ira->new_irb, - source_instr->scope, source_instr->source_node); - instruction->base.value->type = str_type; - instruction->value = value; - - ir_ref_inst_gen(value); - - return &instruction->base; -} - -static IrInstSrc *ir_build_c_import(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { - IrInstSrcCImport *instruction = ir_build_instruction(irb, scope, source_node); - return &instruction->base; -} - -static IrInstSrc *ir_build_c_include(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *name) { - IrInstSrcCInclude *instruction = ir_build_instruction(irb, scope, source_node); - instruction->name = name; - - ir_ref_instruction(name, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_c_define(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *name, IrInstSrc *value) { - IrInstSrcCDefine *instruction = ir_build_instruction(irb, scope, source_node); - instruction->name = name; - instruction->value = value; - - ir_ref_instruction(name, irb->current_basic_block); - ir_ref_instruction(value, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_c_undef(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *name) { - IrInstSrcCUndef *instruction = ir_build_instruction(irb, scope, source_node); - instruction->name = name; - - ir_ref_instruction(name, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_embed_file(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *name) { - IrInstSrcEmbedFile *instruction = ir_build_instruction(irb, scope, source_node); - instruction->name = name; - - ir_ref_instruction(name, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_cmpxchg_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *type_value, IrInstSrc *ptr, IrInstSrc *cmp_value, IrInstSrc *new_value, - IrInstSrc *success_order_value, IrInstSrc *failure_order_value, bool is_weak, ResultLoc *result_loc) -{ - IrInstSrcCmpxchg *instruction = ir_build_instruction(irb, scope, source_node); - instruction->type_value = type_value; - instruction->ptr = ptr; - instruction->cmp_value = cmp_value; - instruction->new_value = new_value; - instruction->success_order_value = success_order_value; - instruction->failure_order_value = failure_order_value; - instruction->is_weak = is_weak; - instruction->result_loc = result_loc; - - ir_ref_instruction(type_value, irb->current_basic_block); - ir_ref_instruction(ptr, irb->current_basic_block); - ir_ref_instruction(cmp_value, irb->current_basic_block); - ir_ref_instruction(new_value, irb->current_basic_block); - ir_ref_instruction(success_order_value, irb->current_basic_block); - ir_ref_instruction(failure_order_value, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstGen *ir_build_cmpxchg_gen(IrAnalyze *ira, IrInst *source_instruction, ZigType *result_type, - IrInstGen *ptr, IrInstGen *cmp_value, IrInstGen *new_value, - AtomicOrder success_order, AtomicOrder failure_order, bool is_weak, IrInstGen *result_loc) -{ - IrInstGenCmpxchg *instruction = ir_build_inst_gen(&ira->new_irb, - source_instruction->scope, source_instruction->source_node); - instruction->base.value->type = result_type; - instruction->ptr = ptr; - instruction->cmp_value = cmp_value; - instruction->new_value = new_value; - instruction->success_order = success_order; - instruction->failure_order = failure_order; - instruction->is_weak = is_weak; - instruction->result_loc = result_loc; - - ir_ref_inst_gen(ptr); - ir_ref_inst_gen(cmp_value); - ir_ref_inst_gen(new_value); - if (result_loc != nullptr) ir_ref_inst_gen(result_loc); - - return &instruction->base; -} - -static IrInstSrc *ir_build_fence(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *order) { - IrInstSrcFence *instruction = ir_build_instruction(irb, scope, source_node); - instruction->order = order; - - ir_ref_instruction(order, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstGen *ir_build_fence_gen(IrAnalyze *ira, IrInst *source_instr, AtomicOrder order) { - IrInstGenFence *instruction = ir_build_inst_void(&ira->new_irb, - source_instr->scope, source_instr->source_node); - instruction->order = order; - - return &instruction->base; -} - -static IrInstSrc *ir_build_truncate(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *dest_type, IrInstSrc *target) -{ - IrInstSrcTruncate *instruction = ir_build_instruction(irb, scope, source_node); - instruction->dest_type = dest_type; - instruction->target = target; - - ir_ref_instruction(dest_type, irb->current_basic_block); - ir_ref_instruction(target, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstGen *ir_build_truncate_gen(IrAnalyze *ira, IrInst *source_instr, ZigType *dest_type, - IrInstGen *target) -{ - IrInstGenTruncate *instruction = ir_build_inst_gen(&ira->new_irb, - source_instr->scope, source_instr->source_node); - instruction->base.value->type = dest_type; - instruction->target = target; - - ir_ref_inst_gen(target); - - return &instruction->base; -} - -static IrInstSrc *ir_build_int_cast(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *dest_type, - IrInstSrc *target) -{ - IrInstSrcIntCast *instruction = ir_build_instruction(irb, scope, source_node); - instruction->dest_type = dest_type; - instruction->target = target; - - ir_ref_instruction(dest_type, irb->current_basic_block); - ir_ref_instruction(target, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_float_cast(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *dest_type, - IrInstSrc *target) -{ - IrInstSrcFloatCast *instruction = ir_build_instruction(irb, scope, source_node); - instruction->dest_type = dest_type; - instruction->target = target; - - ir_ref_instruction(dest_type, irb->current_basic_block); - ir_ref_instruction(target, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_err_set_cast(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *dest_type, IrInstSrc *target) -{ - IrInstSrcErrSetCast *instruction = ir_build_instruction(irb, scope, source_node); - instruction->dest_type = dest_type; - instruction->target = target; - - ir_ref_instruction(dest_type, irb->current_basic_block); - ir_ref_instruction(target, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_int_to_float(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *dest_type, IrInstSrc *target) -{ - IrInstSrcIntToFloat *instruction = ir_build_instruction(irb, scope, source_node); - instruction->dest_type = dest_type; - instruction->target = target; - - ir_ref_instruction(dest_type, irb->current_basic_block); - ir_ref_instruction(target, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_float_to_int(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *dest_type, IrInstSrc *target) -{ - IrInstSrcFloatToInt *instruction = ir_build_instruction(irb, scope, source_node); - instruction->dest_type = dest_type; - instruction->target = target; - - ir_ref_instruction(dest_type, irb->current_basic_block); - ir_ref_instruction(target, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_bool_to_int(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *target) { - IrInstSrcBoolToInt *instruction = ir_build_instruction(irb, scope, source_node); - instruction->target = target; - - ir_ref_instruction(target, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_vector_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *len, - IrInstSrc *elem_type) -{ - IrInstSrcVectorType *instruction = ir_build_instruction(irb, scope, source_node); - instruction->len = len; - instruction->elem_type = elem_type; - - ir_ref_instruction(len, irb->current_basic_block); - ir_ref_instruction(elem_type, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_shuffle_vector(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *scalar_type, IrInstSrc *a, IrInstSrc *b, IrInstSrc *mask) -{ - IrInstSrcShuffleVector *instruction = ir_build_instruction(irb, scope, source_node); - instruction->scalar_type = scalar_type; - instruction->a = a; - instruction->b = b; - instruction->mask = mask; - - if (scalar_type != nullptr) ir_ref_instruction(scalar_type, irb->current_basic_block); - ir_ref_instruction(a, irb->current_basic_block); - ir_ref_instruction(b, irb->current_basic_block); - ir_ref_instruction(mask, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstGen *ir_build_shuffle_vector_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node, - ZigType *result_type, IrInstGen *a, IrInstGen *b, IrInstGen *mask) -{ - IrInstGenShuffleVector *inst = ir_build_inst_gen(&ira->new_irb, scope, source_node); - inst->base.value->type = result_type; - inst->a = a; - inst->b = b; - inst->mask = mask; - - ir_ref_inst_gen(a); - ir_ref_inst_gen(b); - ir_ref_inst_gen(mask); - - return &inst->base; -} - -static IrInstSrc *ir_build_splat_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *len, IrInstSrc *scalar) -{ - IrInstSrcSplat *instruction = ir_build_instruction(irb, scope, source_node); - instruction->len = len; - instruction->scalar = scalar; - - ir_ref_instruction(len, irb->current_basic_block); - ir_ref_instruction(scalar, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstGen *ir_build_splat_gen(IrAnalyze *ira, IrInst *source_instruction, ZigType *result_type, - IrInstGen *scalar) -{ - IrInstGenSplat *instruction = ir_build_inst_gen( - &ira->new_irb, source_instruction->scope, source_instruction->source_node); - instruction->base.value->type = result_type; - instruction->scalar = scalar; - - ir_ref_inst_gen(scalar); - - return &instruction->base; -} - -static IrInstSrc *ir_build_bool_not(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *value) { - IrInstSrcBoolNot *instruction = ir_build_instruction(irb, scope, source_node); - instruction->value = value; - - ir_ref_instruction(value, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstGen *ir_build_bool_not_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *value) { - IrInstGenBoolNot *instruction = ir_build_inst_gen(&ira->new_irb, - source_instr->scope, source_instr->source_node); - instruction->base.value->type = ira->codegen->builtin_types.entry_bool; - instruction->value = value; - - ir_ref_inst_gen(value); - - return &instruction->base; -} - -static IrInstSrc *ir_build_memset_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *dest_ptr, IrInstSrc *byte, IrInstSrc *count) -{ - IrInstSrcMemset *instruction = ir_build_instruction(irb, scope, source_node); - instruction->dest_ptr = dest_ptr; - instruction->byte = byte; - instruction->count = count; - - ir_ref_instruction(dest_ptr, irb->current_basic_block); - ir_ref_instruction(byte, irb->current_basic_block); - ir_ref_instruction(count, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstGen *ir_build_memset_gen(IrAnalyze *ira, IrInst *source_instr, - IrInstGen *dest_ptr, IrInstGen *byte, IrInstGen *count) -{ - IrInstGenMemset *instruction = ir_build_inst_void(&ira->new_irb, - source_instr->scope, source_instr->source_node); - instruction->dest_ptr = dest_ptr; - instruction->byte = byte; - instruction->count = count; - - ir_ref_inst_gen(dest_ptr); - ir_ref_inst_gen(byte); - ir_ref_inst_gen(count); - - return &instruction->base; -} - -static IrInstSrc *ir_build_memcpy_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *dest_ptr, IrInstSrc *src_ptr, IrInstSrc *count) -{ - IrInstSrcMemcpy *instruction = ir_build_instruction(irb, scope, source_node); - instruction->dest_ptr = dest_ptr; - instruction->src_ptr = src_ptr; - instruction->count = count; - - ir_ref_instruction(dest_ptr, irb->current_basic_block); - ir_ref_instruction(src_ptr, irb->current_basic_block); - ir_ref_instruction(count, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstGen *ir_build_memcpy_gen(IrAnalyze *ira, IrInst *source_instr, - IrInstGen *dest_ptr, IrInstGen *src_ptr, IrInstGen *count) -{ - IrInstGenMemcpy *instruction = ir_build_inst_void(&ira->new_irb, - source_instr->scope, source_instr->source_node); - instruction->dest_ptr = dest_ptr; - instruction->src_ptr = src_ptr; - instruction->count = count; - - ir_ref_inst_gen(dest_ptr); - ir_ref_inst_gen(src_ptr); - ir_ref_inst_gen(count); - - return &instruction->base; -} - -static IrInstSrc *ir_build_slice_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *ptr, IrInstSrc *start, IrInstSrc *end, IrInstSrc *sentinel, - bool safety_check_on, ResultLoc *result_loc) -{ - IrInstSrcSlice *instruction = ir_build_instruction(irb, scope, source_node); - instruction->ptr = ptr; - instruction->start = start; - instruction->end = end; - instruction->sentinel = sentinel; - instruction->safety_check_on = safety_check_on; - instruction->result_loc = result_loc; - - ir_ref_instruction(ptr, irb->current_basic_block); - ir_ref_instruction(start, irb->current_basic_block); - if (end) ir_ref_instruction(end, irb->current_basic_block); - if (sentinel) ir_ref_instruction(sentinel, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstGen *ir_build_slice_gen(IrAnalyze *ira, IrInst *source_instruction, ZigType *slice_type, - IrInstGen *ptr, IrInstGen *start, IrInstGen *end, bool safety_check_on, IrInstGen *result_loc, - ZigValue *sentinel) -{ - IrInstGenSlice *instruction = ir_build_inst_gen( - &ira->new_irb, source_instruction->scope, source_instruction->source_node); - instruction->base.value->type = slice_type; - instruction->ptr = ptr; - instruction->start = start; - instruction->end = end; - instruction->safety_check_on = safety_check_on; - instruction->result_loc = result_loc; - instruction->sentinel = sentinel; - - ir_ref_inst_gen(ptr); - ir_ref_inst_gen(start); - if (end != nullptr) ir_ref_inst_gen(end); - if (result_loc != nullptr) ir_ref_inst_gen(result_loc); - - return &instruction->base; -} - -static IrInstSrc *ir_build_breakpoint(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { - IrInstSrcBreakpoint *instruction = ir_build_instruction(irb, scope, source_node); - return &instruction->base; -} - -static IrInstGen *ir_build_breakpoint_gen(IrAnalyze *ira, IrInst *source_instr) { - IrInstGenBreakpoint *instruction = ir_build_inst_void(&ira->new_irb, - source_instr->scope, source_instr->source_node); - return &instruction->base; -} - -static IrInstSrc *ir_build_return_address_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { - IrInstSrcReturnAddress *instruction = ir_build_instruction(irb, scope, source_node); - return &instruction->base; -} - -static IrInstGen *ir_build_return_address_gen(IrAnalyze *ira, IrInst *source_instr) { - IrInstGenReturnAddress *inst = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); - inst->base.value->type = ira->codegen->builtin_types.entry_usize; - return &inst->base; -} - -static IrInstSrc *ir_build_frame_address_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { - IrInstSrcFrameAddress *inst = ir_build_instruction(irb, scope, source_node); - return &inst->base; -} - -static IrInstGen *ir_build_frame_address_gen(IrAnalyze *ira, IrInst *source_instr) { - IrInstGenFrameAddress *inst = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); - inst->base.value->type = ira->codegen->builtin_types.entry_usize; - return &inst->base; -} - -static IrInstSrc *ir_build_handle_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { - IrInstSrcFrameHandle *inst = ir_build_instruction(irb, scope, source_node); - return &inst->base; -} - -static IrInstGen *ir_build_handle_gen(IrAnalyze *ira, IrInst *source_instr, ZigType *ty) { - IrInstGenFrameHandle *inst = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); - inst->base.value->type = ty; - return &inst->base; -} - -static IrInstSrc *ir_build_frame_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *fn) { - IrInstSrcFrameType *inst = ir_build_instruction(irb, scope, source_node); - inst->fn = fn; - - ir_ref_instruction(fn, irb->current_basic_block); - - return &inst->base; -} - -static IrInstSrc *ir_build_frame_size_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *fn) { - IrInstSrcFrameSize *inst = ir_build_instruction(irb, scope, source_node); - inst->fn = fn; - - ir_ref_instruction(fn, irb->current_basic_block); - - return &inst->base; -} - -static IrInstGen *ir_build_frame_size_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *fn) -{ - IrInstGenFrameSize *inst = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); - inst->base.value->type = ira->codegen->builtin_types.entry_usize; - inst->fn = fn; - - ir_ref_inst_gen(fn); - - return &inst->base; -} - -static IrInstSrc *ir_build_overflow_op_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrOverflowOp op, IrInstSrc *type_value, IrInstSrc *op1, IrInstSrc *op2, IrInstSrc *result_ptr) -{ - IrInstSrcOverflowOp *instruction = ir_build_instruction(irb, scope, source_node); - instruction->op = op; - instruction->type_value = type_value; - instruction->op1 = op1; - instruction->op2 = op2; - instruction->result_ptr = result_ptr; - - ir_ref_instruction(type_value, irb->current_basic_block); - ir_ref_instruction(op1, irb->current_basic_block); - ir_ref_instruction(op2, irb->current_basic_block); - ir_ref_instruction(result_ptr, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstGen *ir_build_overflow_op_gen(IrAnalyze *ira, IrInst *source_instr, - IrOverflowOp op, IrInstGen *op1, IrInstGen *op2, IrInstGen *result_ptr, - ZigType *result_ptr_type) -{ - IrInstGenOverflowOp *instruction = ir_build_inst_gen(&ira->new_irb, - source_instr->scope, source_instr->source_node); - instruction->base.value->type = ira->codegen->builtin_types.entry_bool; - instruction->op = op; - instruction->op1 = op1; - instruction->op2 = op2; - instruction->result_ptr = result_ptr; - instruction->result_ptr_type = result_ptr_type; - - ir_ref_inst_gen(op1); - ir_ref_inst_gen(op2); - ir_ref_inst_gen(result_ptr); - - return &instruction->base; -} - -static IrInstSrc *ir_build_float_op_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *operand, - BuiltinFnId fn_id) -{ - IrInstSrcFloatOp *instruction = ir_build_instruction(irb, scope, source_node); - instruction->operand = operand; - instruction->fn_id = fn_id; - - ir_ref_instruction(operand, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstGen *ir_build_float_op_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *operand, - BuiltinFnId fn_id, ZigType *operand_type) -{ - IrInstGenFloatOp *instruction = ir_build_inst_gen(&ira->new_irb, - source_instr->scope, source_instr->source_node); - instruction->base.value->type = operand_type; - instruction->operand = operand; - instruction->fn_id = fn_id; - - ir_ref_inst_gen(operand); - - return &instruction->base; -} - -static IrInstSrc *ir_build_mul_add_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *type_value, IrInstSrc *op1, IrInstSrc *op2, IrInstSrc *op3) -{ - IrInstSrcMulAdd *instruction = ir_build_instruction(irb, scope, source_node); - instruction->type_value = type_value; - instruction->op1 = op1; - instruction->op2 = op2; - instruction->op3 = op3; - - ir_ref_instruction(type_value, irb->current_basic_block); - ir_ref_instruction(op1, irb->current_basic_block); - ir_ref_instruction(op2, irb->current_basic_block); - ir_ref_instruction(op3, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstGen *ir_build_mul_add_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *op1, IrInstGen *op2, - IrInstGen *op3, ZigType *expr_type) -{ - IrInstGenMulAdd *instruction = ir_build_inst_gen(&ira->new_irb, - source_instr->scope, source_instr->source_node); - instruction->base.value->type = expr_type; - instruction->op1 = op1; - instruction->op2 = op2; - instruction->op3 = op3; - - ir_ref_inst_gen(op1); - ir_ref_inst_gen(op2); - ir_ref_inst_gen(op3); - - return &instruction->base; -} - -static IrInstSrc *ir_build_align_of(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type_value) { - IrInstSrcAlignOf *instruction = ir_build_instruction(irb, scope, source_node); - instruction->type_value = type_value; - - ir_ref_instruction(type_value, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_test_err_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *base_ptr, bool resolve_err_set, bool base_ptr_is_payload) -{ - IrInstSrcTestErr *instruction = ir_build_instruction(irb, scope, source_node); - instruction->base_ptr = base_ptr; - instruction->resolve_err_set = resolve_err_set; - instruction->base_ptr_is_payload = base_ptr_is_payload; - - ir_ref_instruction(base_ptr, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstGen *ir_build_test_err_gen(IrAnalyze *ira, IrInst *source_instruction, IrInstGen *err_union) { - IrInstGenTestErr *instruction = ir_build_inst_gen( - &ira->new_irb, source_instruction->scope, source_instruction->source_node); - instruction->base.value->type = ira->codegen->builtin_types.entry_bool; - instruction->err_union = err_union; - - ir_ref_inst_gen(err_union); - - return &instruction->base; -} - -static IrInstSrc *ir_build_unwrap_err_code_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *err_union_ptr) -{ - IrInstSrcUnwrapErrCode *inst = ir_build_instruction(irb, scope, source_node); - inst->err_union_ptr = err_union_ptr; - - ir_ref_instruction(err_union_ptr, irb->current_basic_block); - - return &inst->base; -} - -static IrInstGen *ir_build_unwrap_err_code_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node, - IrInstGen *err_union_ptr, ZigType *result_type) -{ - IrInstGenUnwrapErrCode *inst = ir_build_inst_gen(&ira->new_irb, scope, source_node); - inst->base.value->type = result_type; - inst->err_union_ptr = err_union_ptr; - - ir_ref_inst_gen(err_union_ptr); - - return &inst->base; -} - -static IrInstSrc *ir_build_unwrap_err_payload_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *value, bool safety_check_on, bool initializing) -{ - IrInstSrcUnwrapErrPayload *inst = ir_build_instruction(irb, scope, source_node); - inst->value = value; - inst->safety_check_on = safety_check_on; - inst->initializing = initializing; - - ir_ref_instruction(value, irb->current_basic_block); - - return &inst->base; -} - -static IrInstGen *ir_build_unwrap_err_payload_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node, - IrInstGen *value, bool safety_check_on, bool initializing, ZigType *result_type) -{ - IrInstGenUnwrapErrPayload *inst = ir_build_inst_gen(&ira->new_irb, scope, source_node); - inst->base.value->type = result_type; - inst->value = value; - inst->safety_check_on = safety_check_on; - inst->initializing = initializing; - - ir_ref_inst_gen(value); - - return &inst->base; -} - -static IrInstSrc *ir_build_fn_proto(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc **param_types, IrInstSrc *align_value, IrInstSrc *callconv_value, - IrInstSrc *return_type, bool is_var_args) -{ - IrInstSrcFnProto *instruction = ir_build_instruction(irb, scope, source_node); - instruction->param_types = param_types; - instruction->align_value = align_value; - instruction->callconv_value = callconv_value; - instruction->return_type = return_type; - instruction->is_var_args = is_var_args; - - assert(source_node->type == NodeTypeFnProto); - size_t param_count = source_node->data.fn_proto.params.length; - if (is_var_args) param_count -= 1; - for (size_t i = 0; i < param_count; i += 1) { - if (param_types[i] != nullptr) ir_ref_instruction(param_types[i], irb->current_basic_block); - } - if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block); - if (callconv_value != nullptr) ir_ref_instruction(callconv_value, irb->current_basic_block); - ir_ref_instruction(return_type, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_test_comptime(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *value) { - IrInstSrcTestComptime *instruction = ir_build_instruction(irb, scope, source_node); - instruction->value = value; - - ir_ref_instruction(value, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_ptr_cast_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *dest_type, IrInstSrc *ptr, bool safety_check_on) -{ - IrInstSrcPtrCast *instruction = ir_build_instruction( - irb, scope, source_node); - instruction->dest_type = dest_type; - instruction->ptr = ptr; - instruction->safety_check_on = safety_check_on; - - ir_ref_instruction(dest_type, irb->current_basic_block); - ir_ref_instruction(ptr, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstGen *ir_build_ptr_cast_gen(IrAnalyze *ira, IrInst *source_instruction, - ZigType *ptr_type, IrInstGen *ptr, bool safety_check_on) -{ - IrInstGenPtrCast *instruction = ir_build_inst_gen( - &ira->new_irb, source_instruction->scope, source_instruction->source_node); - instruction->base.value->type = ptr_type; - instruction->ptr = ptr; - instruction->safety_check_on = safety_check_on; - - ir_ref_inst_gen(ptr); - - return &instruction->base; -} - -static IrInstSrc *ir_build_implicit_cast(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *operand, ResultLocCast *result_loc_cast) -{ - IrInstSrcImplicitCast *instruction = ir_build_instruction(irb, scope, source_node); - instruction->operand = operand; - instruction->result_loc_cast = result_loc_cast; - - ir_ref_instruction(operand, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_bit_cast_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *operand, ResultLocBitCast *result_loc_bit_cast) -{ - IrInstSrcBitCast *instruction = ir_build_instruction(irb, scope, source_node); - instruction->operand = operand; - instruction->result_loc_bit_cast = result_loc_bit_cast; - - ir_ref_instruction(operand, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstGen *ir_build_bit_cast_gen(IrAnalyze *ira, IrInst *source_instruction, - IrInstGen *operand, ZigType *ty) -{ - IrInstGenBitCast *instruction = ir_build_inst_gen( - &ira->new_irb, source_instruction->scope, source_instruction->source_node); - instruction->base.value->type = ty; - instruction->operand = operand; - - ir_ref_inst_gen(operand); - - return &instruction->base; -} - -static IrInstGen *ir_build_widen_or_shorten(IrAnalyze *ira, Scope *scope, AstNode *source_node, IrInstGen *target, - ZigType *result_type) -{ - IrInstGenWidenOrShorten *inst = ir_build_inst_gen(&ira->new_irb, scope, source_node); - inst->base.value->type = result_type; - inst->target = target; - - ir_ref_inst_gen(target); - - return &inst->base; -} - -static IrInstSrc *ir_build_int_to_ptr_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *dest_type, IrInstSrc *target) -{ - IrInstSrcIntToPtr *instruction = ir_build_instruction(irb, scope, source_node); - instruction->dest_type = dest_type; - instruction->target = target; - - ir_ref_instruction(dest_type, irb->current_basic_block); - ir_ref_instruction(target, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstGen *ir_build_int_to_ptr_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node, - IrInstGen *target, ZigType *ptr_type) -{ - IrInstGenIntToPtr *instruction = ir_build_inst_gen(&ira->new_irb, scope, source_node); - instruction->base.value->type = ptr_type; - instruction->target = target; - - ir_ref_inst_gen(target); - - return &instruction->base; -} - -static IrInstSrc *ir_build_ptr_to_int_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *target) -{ - IrInstSrcPtrToInt *inst = ir_build_instruction(irb, scope, source_node); - inst->target = target; - - ir_ref_instruction(target, irb->current_basic_block); - - return &inst->base; -} - -static IrInstGen *ir_build_ptr_to_int_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *target) { - IrInstGenPtrToInt *inst = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); - inst->base.value->type = ira->codegen->builtin_types.entry_usize; - inst->target = target; - - ir_ref_inst_gen(target); - - return &inst->base; -} - -static IrInstSrc *ir_build_int_to_enum_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *dest_type, IrInstSrc *target) -{ - IrInstSrcIntToEnum *instruction = ir_build_instruction(irb, scope, source_node); - instruction->dest_type = dest_type; - instruction->target = target; - - if (dest_type) ir_ref_instruction(dest_type, irb->current_basic_block); - ir_ref_instruction(target, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstGen *ir_build_int_to_enum_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node, - ZigType *dest_type, IrInstGen *target) -{ - IrInstGenIntToEnum *instruction = ir_build_inst_gen(&ira->new_irb, scope, source_node); - instruction->base.value->type = dest_type; - instruction->target = target; - - ir_ref_inst_gen(target); - - return &instruction->base; -} - -static IrInstSrc *ir_build_enum_to_int(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *target) -{ - IrInstSrcEnumToInt *instruction = ir_build_instruction( - irb, scope, source_node); - instruction->target = target; - - ir_ref_instruction(target, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_int_to_err_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *target) -{ - IrInstSrcIntToErr *instruction = ir_build_instruction(irb, scope, source_node); - instruction->target = target; - - ir_ref_instruction(target, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstGen *ir_build_int_to_err_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node, IrInstGen *target, - ZigType *wanted_type) -{ - IrInstGenIntToErr *instruction = ir_build_inst_gen(&ira->new_irb, scope, source_node); - instruction->base.value->type = wanted_type; - instruction->target = target; - - ir_ref_inst_gen(target); - - return &instruction->base; -} - -static IrInstSrc *ir_build_err_to_int_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *target) -{ - IrInstSrcErrToInt *instruction = ir_build_instruction( - irb, scope, source_node); - instruction->target = target; - - ir_ref_instruction(target, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstGen *ir_build_err_to_int_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node, IrInstGen *target, - ZigType *wanted_type) -{ - IrInstGenErrToInt *instruction = ir_build_inst_gen(&ira->new_irb, scope, source_node); - instruction->base.value->type = wanted_type; - instruction->target = target; - - ir_ref_inst_gen(target); - - return &instruction->base; -} - -static IrInstSrc *ir_build_check_switch_prongs(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *target_value, IrInstSrcCheckSwitchProngsRange *ranges, size_t range_count, - AstNode* else_prong, bool have_underscore_prong) -{ - IrInstSrcCheckSwitchProngs *instruction = ir_build_instruction( - irb, scope, source_node); - instruction->target_value = target_value; - instruction->ranges = ranges; - instruction->range_count = range_count; - instruction->else_prong = else_prong; - instruction->have_underscore_prong = have_underscore_prong; - - ir_ref_instruction(target_value, irb->current_basic_block); - for (size_t i = 0; i < range_count; i += 1) { - ir_ref_instruction(ranges[i].start, irb->current_basic_block); - ir_ref_instruction(ranges[i].end, irb->current_basic_block); - } - - return &instruction->base; -} - -static IrInstSrc *ir_build_check_statement_is_void(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc* statement_value) -{ - IrInstSrcCheckStatementIsVoid *instruction = ir_build_instruction( - irb, scope, source_node); - instruction->statement_value = statement_value; - - ir_ref_instruction(statement_value, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_type_name(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *type_value) -{ - IrInstSrcTypeName *instruction = ir_build_instruction(irb, scope, source_node); - instruction->type_value = type_value; - - ir_ref_instruction(type_value, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_decl_ref(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, Tld *tld, LVal lval) { - IrInstSrcDeclRef *instruction = ir_build_instruction(irb, scope, source_node); - instruction->tld = tld; - instruction->lval = lval; - - return &instruction->base; -} - -static IrInstSrc *ir_build_panic_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *msg) { - IrInstSrcPanic *instruction = ir_build_instruction(irb, scope, source_node); - instruction->base.is_noreturn = true; - instruction->msg = msg; - - ir_ref_instruction(msg, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstGen *ir_build_panic_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *msg) { - IrInstGenPanic *instruction = ir_build_inst_noreturn(&ira->new_irb, - source_instr->scope, source_instr->source_node); - instruction->msg = msg; - - ir_ref_inst_gen(msg); - - return &instruction->base; -} - -static IrInstSrc *ir_build_tag_name_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *target) { - IrInstSrcTagName *instruction = ir_build_instruction(irb, scope, source_node); - instruction->target = target; - - ir_ref_instruction(target, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstGen *ir_build_tag_name_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *target, - ZigType *result_type) -{ - IrInstGenTagName *instruction = ir_build_inst_gen(&ira->new_irb, - source_instr->scope, source_instr->source_node); - instruction->base.value->type = result_type; - instruction->target = target; - - ir_ref_inst_gen(target); - - return &instruction->base; -} - -static IrInstSrc *ir_build_tag_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *target) -{ - IrInstSrcTagType *instruction = ir_build_instruction(irb, scope, source_node); - instruction->target = target; - - ir_ref_instruction(target, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_field_parent_ptr_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *type_value, IrInstSrc *field_name, IrInstSrc *field_ptr) -{ - IrInstSrcFieldParentPtr *inst = ir_build_instruction( - irb, scope, source_node); - inst->type_value = type_value; - inst->field_name = field_name; - inst->field_ptr = field_ptr; - - ir_ref_instruction(type_value, irb->current_basic_block); - ir_ref_instruction(field_name, irb->current_basic_block); - ir_ref_instruction(field_ptr, irb->current_basic_block); - - return &inst->base; -} - -static IrInstGen *ir_build_field_parent_ptr_gen(IrAnalyze *ira, IrInst *source_instr, - IrInstGen *field_ptr, TypeStructField *field, ZigType *result_type) -{ - IrInstGenFieldParentPtr *inst = ir_build_inst_gen(&ira->new_irb, - source_instr->scope, source_instr->source_node); - inst->base.value->type = result_type; - inst->field_ptr = field_ptr; - inst->field = field; - - ir_ref_inst_gen(field_ptr); - - return &inst->base; -} - -static IrInstSrc *ir_build_byte_offset_of(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *type_value, IrInstSrc *field_name) -{ - IrInstSrcByteOffsetOf *instruction = ir_build_instruction(irb, scope, source_node); - instruction->type_value = type_value; - instruction->field_name = field_name; - - ir_ref_instruction(type_value, irb->current_basic_block); - ir_ref_instruction(field_name, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_bit_offset_of(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *type_value, IrInstSrc *field_name) -{ - IrInstSrcBitOffsetOf *instruction = ir_build_instruction(irb, scope, source_node); - instruction->type_value = type_value; - instruction->field_name = field_name; - - ir_ref_instruction(type_value, irb->current_basic_block); - ir_ref_instruction(field_name, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_type_info(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type_value) { - IrInstSrcTypeInfo *instruction = ir_build_instruction(irb, scope, source_node); - instruction->type_value = type_value; - - ir_ref_instruction(type_value, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type_info) { - IrInstSrcType *instruction = ir_build_instruction(irb, scope, source_node); - instruction->type_info = type_info; - - ir_ref_instruction(type_info, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_set_eval_branch_quota(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *new_quota) -{ - IrInstSrcSetEvalBranchQuota *instruction = ir_build_instruction(irb, scope, source_node); - instruction->new_quota = new_quota; - - ir_ref_instruction(new_quota, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_align_cast_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *align_bytes, IrInstSrc *target) -{ - IrInstSrcAlignCast *instruction = ir_build_instruction(irb, scope, source_node); - instruction->align_bytes = align_bytes; - instruction->target = target; - - ir_ref_instruction(align_bytes, irb->current_basic_block); - ir_ref_instruction(target, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstGen *ir_build_align_cast_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node, IrInstGen *target, - ZigType *result_type) -{ - IrInstGenAlignCast *instruction = ir_build_inst_gen(&ira->new_irb, scope, source_node); - instruction->base.value->type = result_type; - instruction->target = target; - - ir_ref_inst_gen(target); - - return &instruction->base; -} - -static IrInstSrc *ir_build_resolve_result(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - ResultLoc *result_loc, IrInstSrc *ty) -{ - IrInstSrcResolveResult *instruction = ir_build_instruction(irb, scope, source_node); - instruction->result_loc = result_loc; - instruction->ty = ty; - - if (ty != nullptr) ir_ref_instruction(ty, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_reset_result(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - ResultLoc *result_loc) -{ - IrInstSrcResetResult *instruction = ir_build_instruction(irb, scope, source_node); - instruction->result_loc = result_loc; - instruction->base.is_gen = true; - - return &instruction->base; -} - -static IrInstSrc *ir_build_set_align_stack(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *align_bytes) -{ - IrInstSrcSetAlignStack *instruction = ir_build_instruction(irb, scope, source_node); - instruction->align_bytes = align_bytes; - - ir_ref_instruction(align_bytes, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_arg_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *fn_type, IrInstSrc *arg_index, bool allow_var) -{ - IrInstSrcArgType *instruction = ir_build_instruction(irb, scope, source_node); - instruction->fn_type = fn_type; - instruction->arg_index = arg_index; - instruction->allow_var = allow_var; - - ir_ref_instruction(fn_type, irb->current_basic_block); - ir_ref_instruction(arg_index, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_error_return_trace_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstErrorReturnTraceOptional optional) -{ - IrInstSrcErrorReturnTrace *inst = ir_build_instruction(irb, scope, source_node); - inst->optional = optional; - - return &inst->base; -} - -static IrInstGen *ir_build_error_return_trace_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node, - IrInstErrorReturnTraceOptional optional, ZigType *result_type) -{ - IrInstGenErrorReturnTrace *inst = ir_build_inst_gen(&ira->new_irb, scope, source_node); - inst->base.value->type = result_type; - inst->optional = optional; - - return &inst->base; -} - -static IrInstSrc *ir_build_error_union(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *err_set, IrInstSrc *payload) -{ - IrInstSrcErrorUnion *instruction = ir_build_instruction(irb, scope, source_node); - instruction->err_set = err_set; - instruction->payload = payload; - - ir_ref_instruction(err_set, irb->current_basic_block); - ir_ref_instruction(payload, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_atomic_rmw_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *operand_type, IrInstSrc *ptr, IrInstSrc *op, IrInstSrc *operand, - IrInstSrc *ordering) -{ - IrInstSrcAtomicRmw *instruction = ir_build_instruction(irb, scope, source_node); - instruction->operand_type = operand_type; - instruction->ptr = ptr; - instruction->op = op; - instruction->operand = operand; - instruction->ordering = ordering; - - ir_ref_instruction(operand_type, irb->current_basic_block); - ir_ref_instruction(ptr, irb->current_basic_block); - ir_ref_instruction(op, irb->current_basic_block); - ir_ref_instruction(operand, irb->current_basic_block); - ir_ref_instruction(ordering, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstGen *ir_build_atomic_rmw_gen(IrAnalyze *ira, IrInst *source_instr, - IrInstGen *ptr, IrInstGen *operand, AtomicRmwOp op, AtomicOrder ordering, ZigType *operand_type) -{ - IrInstGenAtomicRmw *instruction = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); - instruction->base.value->type = operand_type; - instruction->ptr = ptr; - instruction->op = op; - instruction->operand = operand; - instruction->ordering = ordering; - - ir_ref_inst_gen(ptr); - ir_ref_inst_gen(operand); - - return &instruction->base; -} - -static IrInstSrc *ir_build_atomic_load_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *operand_type, IrInstSrc *ptr, IrInstSrc *ordering) -{ - IrInstSrcAtomicLoad *instruction = ir_build_instruction(irb, scope, source_node); - instruction->operand_type = operand_type; - instruction->ptr = ptr; - instruction->ordering = ordering; - - ir_ref_instruction(operand_type, irb->current_basic_block); - ir_ref_instruction(ptr, irb->current_basic_block); - ir_ref_instruction(ordering, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstGen *ir_build_atomic_load_gen(IrAnalyze *ira, IrInst *source_instr, - IrInstGen *ptr, AtomicOrder ordering, ZigType *operand_type) -{ - IrInstGenAtomicLoad *instruction = ir_build_inst_gen(&ira->new_irb, - source_instr->scope, source_instr->source_node); - instruction->base.value->type = operand_type; - instruction->ptr = ptr; - instruction->ordering = ordering; - - ir_ref_inst_gen(ptr); - - return &instruction->base; -} - -static IrInstSrc *ir_build_atomic_store_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *operand_type, IrInstSrc *ptr, IrInstSrc *value, IrInstSrc *ordering) -{ - IrInstSrcAtomicStore *instruction = ir_build_instruction(irb, scope, source_node); - instruction->operand_type = operand_type; - instruction->ptr = ptr; - instruction->value = value; - instruction->ordering = ordering; - - ir_ref_instruction(operand_type, irb->current_basic_block); - ir_ref_instruction(ptr, irb->current_basic_block); - ir_ref_instruction(value, irb->current_basic_block); - ir_ref_instruction(ordering, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstGen *ir_build_atomic_store_gen(IrAnalyze *ira, IrInst *source_instr, - IrInstGen *ptr, IrInstGen *value, AtomicOrder ordering) -{ - IrInstGenAtomicStore *instruction = ir_build_inst_void(&ira->new_irb, - source_instr->scope, source_instr->source_node); - instruction->ptr = ptr; - instruction->value = value; - instruction->ordering = ordering; - - ir_ref_inst_gen(ptr); - ir_ref_inst_gen(value); - - return &instruction->base; -} - -static IrInstSrc *ir_build_save_err_ret_addr_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { - IrInstSrcSaveErrRetAddr *inst = ir_build_instruction(irb, scope, source_node); - return &inst->base; -} - -static IrInstGen *ir_build_save_err_ret_addr_gen(IrAnalyze *ira, IrInst *source_instr) { - IrInstGenSaveErrRetAddr *inst = ir_build_inst_void(&ira->new_irb, - source_instr->scope, source_instr->source_node); - return &inst->base; -} - -static IrInstSrc *ir_build_add_implicit_return_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *value, ResultLocReturn *result_loc_ret) -{ - IrInstSrcAddImplicitReturnType *inst = ir_build_instruction(irb, scope, source_node); - inst->value = value; - inst->result_loc_ret = result_loc_ret; - - ir_ref_instruction(value, irb->current_basic_block); - - return &inst->base; -} - -static IrInstSrc *ir_build_has_decl(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *container, IrInstSrc *name) -{ - IrInstSrcHasDecl *instruction = ir_build_instruction(irb, scope, source_node); - instruction->container = container; - instruction->name = name; - - ir_ref_instruction(container, irb->current_basic_block); - ir_ref_instruction(name, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_undeclared_identifier(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, Buf *name) { - IrInstSrcUndeclaredIdent *instruction = ir_build_instruction(irb, scope, source_node); - instruction->name = name; - - return &instruction->base; -} - -static IrInstSrc *ir_build_check_runtime_scope(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *scope_is_comptime, IrInstSrc *is_comptime) { - IrInstSrcCheckRuntimeScope *instruction = ir_build_instruction(irb, scope, source_node); - instruction->scope_is_comptime = scope_is_comptime; - instruction->is_comptime = is_comptime; - - ir_ref_instruction(scope_is_comptime, irb->current_basic_block); - ir_ref_instruction(is_comptime, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrc *ir_build_union_init_named_field(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *union_type, IrInstSrc *field_name, IrInstSrc *field_result_loc, IrInstSrc *result_loc) -{ - IrInstSrcUnionInitNamedField *instruction = ir_build_instruction(irb, scope, source_node); - instruction->union_type = union_type; - instruction->field_name = field_name; - instruction->field_result_loc = field_result_loc; - instruction->result_loc = result_loc; - - ir_ref_instruction(union_type, irb->current_basic_block); - ir_ref_instruction(field_name, irb->current_basic_block); - ir_ref_instruction(field_result_loc, irb->current_basic_block); - if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block); - - return &instruction->base; -} - - -static IrInstGen *ir_build_vector_to_array(IrAnalyze *ira, IrInst *source_instruction, - ZigType *result_type, IrInstGen *vector, IrInstGen *result_loc) -{ - IrInstGenVectorToArray *instruction = ir_build_inst_gen(&ira->new_irb, - source_instruction->scope, source_instruction->source_node); - instruction->base.value->type = result_type; - instruction->vector = vector; - instruction->result_loc = result_loc; - - ir_ref_inst_gen(vector); - ir_ref_inst_gen(result_loc); - - return &instruction->base; -} - -static IrInstGen *ir_build_ptr_of_array_to_slice(IrAnalyze *ira, IrInst *source_instruction, - ZigType *result_type, IrInstGen *operand, IrInstGen *result_loc) -{ - IrInstGenPtrOfArrayToSlice *instruction = ir_build_inst_gen(&ira->new_irb, - source_instruction->scope, source_instruction->source_node); - instruction->base.value->type = result_type; - instruction->operand = operand; - instruction->result_loc = result_loc; - - ir_ref_inst_gen(operand); - ir_ref_inst_gen(result_loc); - - return &instruction->base; -} - -static IrInstGen *ir_build_array_to_vector(IrAnalyze *ira, IrInst *source_instruction, - IrInstGen *array, ZigType *result_type) -{ - IrInstGenArrayToVector *instruction = ir_build_inst_gen(&ira->new_irb, - source_instruction->scope, source_instruction->source_node); - instruction->base.value->type = result_type; - instruction->array = array; - - ir_ref_inst_gen(array); - - return &instruction->base; -} - -static IrInstGen *ir_build_assert_zero(IrAnalyze *ira, IrInst *source_instruction, - IrInstGen *target) -{ - IrInstGenAssertZero *instruction = ir_build_inst_gen(&ira->new_irb, - source_instruction->scope, source_instruction->source_node); - instruction->base.value->type = ira->codegen->builtin_types.entry_void; - instruction->target = target; - - ir_ref_inst_gen(target); - - return &instruction->base; -} - -static IrInstGen *ir_build_assert_non_null(IrAnalyze *ira, IrInst *source_instruction, - IrInstGen *target) -{ - IrInstGenAssertNonNull *instruction = ir_build_inst_gen(&ira->new_irb, - source_instruction->scope, source_instruction->source_node); - instruction->base.value->type = ira->codegen->builtin_types.entry_void; - instruction->target = target; - - ir_ref_inst_gen(target); - - return &instruction->base; -} - -static IrInstSrc *ir_build_alloca_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *align, const char *name_hint, IrInstSrc *is_comptime) -{ - IrInstSrcAlloca *instruction = ir_build_instruction(irb, scope, source_node); - instruction->base.is_gen = true; - instruction->align = align; - instruction->name_hint = name_hint; - instruction->is_comptime = is_comptime; - - if (align != nullptr) ir_ref_instruction(align, irb->current_basic_block); - if (is_comptime != nullptr) ir_ref_instruction(is_comptime, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstGenAlloca *ir_build_alloca_gen(IrAnalyze *ira, IrInst *source_instruction, - uint32_t align, const char *name_hint) -{ - IrInstGenAlloca *instruction = ir_create_inst_gen(&ira->new_irb, - source_instruction->scope, source_instruction->source_node); - instruction->align = align; - instruction->name_hint = name_hint; - - return instruction; -} - -static IrInstSrc *ir_build_end_expr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *value, ResultLoc *result_loc) -{ - IrInstSrcEndExpr *instruction = ir_build_instruction(irb, scope, source_node); - instruction->base.is_gen = true; - instruction->value = value; - instruction->result_loc = result_loc; - - ir_ref_instruction(value, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstSrcSuspendBegin *ir_build_suspend_begin_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { - return ir_build_instruction(irb, scope, source_node); -} - -static IrInstGen *ir_build_suspend_begin_gen(IrAnalyze *ira, IrInst *source_instr) { - IrInstGenSuspendBegin *inst = ir_build_inst_void(&ira->new_irb, - source_instr->scope, source_instr->source_node); - return &inst->base; -} - -static IrInstSrc *ir_build_suspend_finish_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrcSuspendBegin *begin) -{ - IrInstSrcSuspendFinish *inst = ir_build_instruction(irb, scope, source_node); - inst->begin = begin; - - ir_ref_instruction(&begin->base, irb->current_basic_block); - - return &inst->base; -} - -static IrInstGen *ir_build_suspend_finish_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGenSuspendBegin *begin) { - IrInstGenSuspendFinish *inst = ir_build_inst_void(&ira->new_irb, - source_instr->scope, source_instr->source_node); - inst->begin = begin; - - ir_ref_inst_gen(&begin->base); - - return &inst->base; -} - -static IrInstSrc *ir_build_await_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *frame, ResultLoc *result_loc, bool is_nosuspend) -{ - IrInstSrcAwait *instruction = ir_build_instruction(irb, scope, source_node); - instruction->frame = frame; - instruction->result_loc = result_loc; - instruction->is_nosuspend = is_nosuspend; - - ir_ref_instruction(frame, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstGenAwait *ir_build_await_gen(IrAnalyze *ira, IrInst *source_instruction, - IrInstGen *frame, ZigType *result_type, IrInstGen *result_loc, bool is_nosuspend) -{ - IrInstGenAwait *instruction = ir_build_inst_gen(&ira->new_irb, - source_instruction->scope, source_instruction->source_node); - instruction->base.value->type = result_type; - instruction->frame = frame; - instruction->result_loc = result_loc; - instruction->is_nosuspend = is_nosuspend; - - ir_ref_inst_gen(frame); - if (result_loc != nullptr) ir_ref_inst_gen(result_loc); - - return instruction; -} - -static IrInstSrc *ir_build_resume_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *frame) { - IrInstSrcResume *instruction = ir_build_instruction(irb, scope, source_node); - instruction->frame = frame; - - ir_ref_instruction(frame, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstGen *ir_build_resume_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *frame) { - IrInstGenResume *instruction = ir_build_inst_void(&ira->new_irb, - source_instr->scope, source_instr->source_node); - instruction->frame = frame; - - ir_ref_inst_gen(frame); - - return &instruction->base; -} - -static IrInstSrcSpillBegin *ir_build_spill_begin_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *operand, SpillId spill_id) -{ - IrInstSrcSpillBegin *instruction = ir_build_instruction(irb, scope, source_node); - instruction->operand = operand; - instruction->spill_id = spill_id; - - ir_ref_instruction(operand, irb->current_basic_block); - - return instruction; -} - -static IrInstGen *ir_build_spill_begin_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *operand, - SpillId spill_id) -{ - IrInstGenSpillBegin *instruction = ir_build_inst_void(&ira->new_irb, - source_instr->scope, source_instr->source_node); - instruction->operand = operand; - instruction->spill_id = spill_id; - - ir_ref_inst_gen(operand); - - return &instruction->base; -} - -static IrInstSrc *ir_build_spill_end_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrcSpillBegin *begin) -{ - IrInstSrcSpillEnd *instruction = ir_build_instruction(irb, scope, source_node); - instruction->begin = begin; - - ir_ref_instruction(&begin->base, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstGen *ir_build_spill_end_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGenSpillBegin *begin, - ZigType *result_type) -{ - IrInstGenSpillEnd *instruction = ir_build_inst_gen(&ira->new_irb, - source_instr->scope, source_instr->source_node); - instruction->base.value->type = result_type; - instruction->begin = begin; - - ir_ref_inst_gen(&begin->base); - - return &instruction->base; -} - -static IrInstGen *ir_build_vector_extract_elem(IrAnalyze *ira, IrInst *source_instruction, - IrInstGen *vector, IrInstGen *index) -{ - IrInstGenVectorExtractElem *instruction = ir_build_inst_gen( - &ira->new_irb, source_instruction->scope, source_instruction->source_node); - instruction->base.value->type = vector->value->type->data.vector.elem_type; - instruction->vector = vector; - instruction->index = index; - - ir_ref_inst_gen(vector); - ir_ref_inst_gen(index); - - return &instruction->base; -} - -static IrInstSrc *ir_build_wasm_memory_size_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *index) { - IrInstSrcWasmMemorySize *instruction = ir_build_instruction(irb, scope, source_node); - instruction->index = index; - - ir_ref_instruction(index, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstGen *ir_build_wasm_memory_size_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *index) { - IrInstGenWasmMemorySize *instruction = ir_build_inst_gen(&ira->new_irb, - source_instr->scope, source_instr->source_node); - instruction->base.value->type = ira->codegen->builtin_types.entry_u32; - instruction->index = index; - - ir_ref_inst_gen(index); - - return &instruction->base; -} - -static IrInstSrc *ir_build_wasm_memory_grow_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *index, IrInstSrc *delta) { - IrInstSrcWasmMemoryGrow *instruction = ir_build_instruction(irb, scope, source_node); - instruction->index = index; - instruction->delta = delta; - - ir_ref_instruction(index, irb->current_basic_block); - ir_ref_instruction(delta, irb->current_basic_block); - - return &instruction->base; -} - -static IrInstGen *ir_build_wasm_memory_grow_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *index, IrInstGen *delta) { - IrInstGenWasmMemoryGrow *instruction = ir_build_inst_gen(&ira->new_irb, - source_instr->scope, source_instr->source_node); - instruction->base.value->type = ira->codegen->builtin_types.entry_i32; - instruction->index = index; - instruction->delta = delta; - - ir_ref_inst_gen(index); - ir_ref_inst_gen(delta); - - return &instruction->base; -} - -static IrInstSrc *ir_build_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { - IrInstSrcSrc *instruction = ir_build_instruction(irb, scope, source_node); - - return &instruction->base; -} - -static void ir_count_defers(IrBuilderSrc *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) { - results[ReturnKindUnconditional] = 0; - results[ReturnKindError] = 0; - - Scope *scope = inner_scope; - - while (scope != outer_scope) { - assert(scope); - switch (scope->id) { - case ScopeIdDefer: { - AstNode *defer_node = scope->source_node; - assert(defer_node->type == NodeTypeDefer); - ReturnKind defer_kind = defer_node->data.defer.kind; - results[defer_kind] += 1; - scope = scope->parent; - continue; - } - case ScopeIdDecls: - case ScopeIdFnDef: - return; - case ScopeIdBlock: - case ScopeIdVarDecl: - case ScopeIdLoop: - case ScopeIdSuspend: - case ScopeIdCompTime: - case ScopeIdNoSuspend: - case ScopeIdRuntime: - case ScopeIdTypeOf: - case ScopeIdExpr: - scope = scope->parent; - continue; - case ScopeIdDeferExpr: - case ScopeIdCImport: - zig_unreachable(); - } - } -} - -static IrInstSrc *ir_mark_gen(IrInstSrc *instruction) { - instruction->is_gen = true; - return instruction; -} - -static bool ir_gen_defers_for_block(IrBuilderSrc *irb, Scope *inner_scope, Scope *outer_scope, bool *is_noreturn, IrInstSrc *err_value) { - Scope *scope = inner_scope; - if (is_noreturn != nullptr) *is_noreturn = false; - while (scope != outer_scope) { - if (!scope) - return true; - - switch (scope->id) { - case ScopeIdDefer: { - AstNode *defer_node = scope->source_node; - assert(defer_node->type == NodeTypeDefer); - ReturnKind defer_kind = defer_node->data.defer.kind; - AstNode *defer_expr_node = defer_node->data.defer.expr; - AstNode *defer_var_node = defer_node->data.defer.err_payload; - - if (defer_kind == ReturnKindError && err_value == nullptr) { - // This is an `errdefer` but we're generating code for a - // `return` that doesn't return an error, skip it - scope = scope->parent; - continue; - } - - Scope *defer_expr_scope = defer_node->data.defer.expr_scope; - if (defer_var_node != nullptr) { - assert(defer_kind == ReturnKindError); - assert(defer_var_node->type == NodeTypeSymbol); - Buf *var_name = defer_var_node->data.symbol_expr.symbol; - - if (defer_expr_node->type == NodeTypeUnreachable) { - add_node_error(irb->codegen, defer_var_node, - buf_sprintf("unused variable: '%s'", buf_ptr(var_name))); - return false; - } - - IrInstSrc *is_comptime; - if (ir_should_inline(irb->exec, defer_expr_scope)) { - is_comptime = ir_build_const_bool(irb, defer_expr_scope, - defer_expr_node, true); - } else { - is_comptime = ir_build_test_comptime(irb, defer_expr_scope, - defer_expr_node, err_value); - } - - ZigVar *err_var = ir_create_var(irb, defer_var_node, defer_expr_scope, - var_name, true, true, false, is_comptime); - build_decl_var_and_init(irb, defer_expr_scope, defer_var_node, err_var, err_value, - buf_ptr(var_name), is_comptime); - - defer_expr_scope = err_var->child_scope; - } - - IrInstSrc *defer_expr_value = ir_gen_node(irb, defer_expr_node, defer_expr_scope); - if (defer_expr_value == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - - if (defer_expr_value->is_noreturn) { - if (is_noreturn != nullptr) *is_noreturn = true; - } else { - ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node, - defer_expr_value)); - } - scope = scope->parent; - continue; - } - case ScopeIdDecls: - case ScopeIdFnDef: - return true; - case ScopeIdBlock: - case ScopeIdVarDecl: - case ScopeIdLoop: - case ScopeIdSuspend: - case ScopeIdCompTime: - case ScopeIdNoSuspend: - case ScopeIdRuntime: - case ScopeIdTypeOf: - case ScopeIdExpr: - scope = scope->parent; - continue; - case ScopeIdDeferExpr: - case ScopeIdCImport: - zig_unreachable(); - } - } - return true; -} - -static void ir_set_cursor_at_end_gen(IrBuilderGen *irb, IrBasicBlockGen *basic_block) { - assert(basic_block); - irb->current_basic_block = basic_block; -} - -static void ir_set_cursor_at_end(IrBuilderSrc *irb, IrBasicBlockSrc *basic_block) { - assert(basic_block); - irb->current_basic_block = basic_block; -} - -static void ir_append_basic_block_gen(IrBuilderGen *irb, IrBasicBlockGen *bb) { - assert(!bb->already_appended); - bb->already_appended = true; - irb->exec->basic_block_list.append(bb); -} - -static void ir_set_cursor_at_end_and_append_block_gen(IrBuilderGen *irb, IrBasicBlockGen *basic_block) { - ir_append_basic_block_gen(irb, basic_block); - ir_set_cursor_at_end_gen(irb, basic_block); -} - -static void ir_set_cursor_at_end_and_append_block(IrBuilderSrc *irb, IrBasicBlockSrc *basic_block) { - basic_block->index = irb->exec->basic_block_list.length; - irb->exec->basic_block_list.append(basic_block); - ir_set_cursor_at_end(irb, basic_block); -} - -static ScopeSuspend *get_scope_suspend(Scope *scope) { - while (scope) { - if (scope->id == ScopeIdSuspend) - return (ScopeSuspend *)scope; - if (scope->id == ScopeIdFnDef) - return nullptr; - - scope = scope->parent; - } - return nullptr; -} - -static ScopeDeferExpr *get_scope_defer_expr(Scope *scope) { - while (scope) { - if (scope->id == ScopeIdDeferExpr) - return (ScopeDeferExpr *)scope; - if (scope->id == ScopeIdFnDef) - return nullptr; - - scope = scope->parent; - } - return nullptr; -} - -static IrInstSrc *ir_gen_return(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *result_loc) { - assert(node->type == NodeTypeReturnExpr); - - ScopeDeferExpr *scope_defer_expr = get_scope_defer_expr(scope); - if (scope_defer_expr) { - if (!scope_defer_expr->reported_err) { - add_node_error(irb->codegen, node, buf_sprintf("cannot return from defer expression")); - scope_defer_expr->reported_err = true; - } - return irb->codegen->invalid_inst_src; - } - - Scope *outer_scope = irb->exec->begin_scope; - - AstNode *expr_node = node->data.return_expr.expr; - switch (node->data.return_expr.kind) { - case ReturnKindUnconditional: - { - ResultLocReturn *result_loc_ret = heap::c_allocator.create(); - result_loc_ret->base.id = ResultLocIdReturn; - ir_build_reset_result(irb, scope, node, &result_loc_ret->base); - - IrInstSrc *return_value; - if (expr_node) { - // Temporarily set this so that if we return a type it gets the name of the function - ZigFn *prev_name_fn = irb->exec->name_fn; - irb->exec->name_fn = exec_fn_entry(irb->exec); - return_value = ir_gen_node_extra(irb, expr_node, scope, LValNone, &result_loc_ret->base); - irb->exec->name_fn = prev_name_fn; - if (return_value == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - } else { - return_value = ir_build_const_void(irb, scope, node); - ir_build_end_expr(irb, scope, node, return_value, &result_loc_ret->base); - } - - ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, node, return_value, result_loc_ret)); - - size_t defer_counts[2]; - ir_count_defers(irb, scope, outer_scope, defer_counts); - bool have_err_defers = defer_counts[ReturnKindError] > 0; - if (!have_err_defers && !irb->codegen->have_err_ret_tracing) { - // only generate unconditional defers - if (!ir_gen_defers_for_block(irb, scope, outer_scope, nullptr, nullptr)) - return irb->codegen->invalid_inst_src; - IrInstSrc *result = ir_build_return_src(irb, scope, node, nullptr); - result_loc_ret->base.source_instruction = result; - return result; - } - bool should_inline = ir_should_inline(irb->exec, scope); - - IrBasicBlockSrc *err_block = ir_create_basic_block(irb, scope, "ErrRetErr"); - IrBasicBlockSrc *ok_block = ir_create_basic_block(irb, scope, "ErrRetOk"); - - IrInstSrc *is_err = ir_build_test_err_src(irb, scope, node, return_value, false, true); - - IrInstSrc *is_comptime; - if (should_inline) { - is_comptime = ir_build_const_bool(irb, scope, node, should_inline); - } else { - is_comptime = ir_build_test_comptime(irb, scope, node, is_err); - } - - ir_mark_gen(ir_build_cond_br(irb, scope, node, is_err, err_block, ok_block, is_comptime)); - IrBasicBlockSrc *ret_stmt_block = ir_create_basic_block(irb, scope, "RetStmt"); - - ir_set_cursor_at_end_and_append_block(irb, err_block); - if (!ir_gen_defers_for_block(irb, scope, outer_scope, nullptr, return_value)) - return irb->codegen->invalid_inst_src; - if (irb->codegen->have_err_ret_tracing && !should_inline) { - ir_build_save_err_ret_addr_src(irb, scope, node); - } - ir_build_br(irb, scope, node, ret_stmt_block, is_comptime); - - ir_set_cursor_at_end_and_append_block(irb, ok_block); - if (!ir_gen_defers_for_block(irb, scope, outer_scope, nullptr, nullptr)) - return irb->codegen->invalid_inst_src; - ir_build_br(irb, scope, node, ret_stmt_block, is_comptime); - - ir_set_cursor_at_end_and_append_block(irb, ret_stmt_block); - IrInstSrc *result = ir_build_return_src(irb, scope, node, nullptr); - result_loc_ret->base.source_instruction = result; - return result; - } - case ReturnKindError: - { - assert(expr_node); - IrInstSrc *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr, nullptr); - if (err_union_ptr == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - IrInstSrc *is_err_val = ir_build_test_err_src(irb, scope, node, err_union_ptr, true, false); - - IrBasicBlockSrc *return_block = ir_create_basic_block(irb, scope, "ErrRetReturn"); - IrBasicBlockSrc *continue_block = ir_create_basic_block(irb, scope, "ErrRetContinue"); - IrInstSrc *is_comptime; - bool should_inline = ir_should_inline(irb->exec, scope); - if (should_inline) { - is_comptime = ir_build_const_bool(irb, scope, node, true); - } else { - is_comptime = ir_build_test_comptime(irb, scope, node, is_err_val); - } - ir_mark_gen(ir_build_cond_br(irb, scope, node, is_err_val, return_block, continue_block, is_comptime)); - - ir_set_cursor_at_end_and_append_block(irb, return_block); - IrInstSrc *err_val_ptr = ir_build_unwrap_err_code_src(irb, scope, node, err_union_ptr); - IrInstSrc *err_val = ir_build_load_ptr(irb, scope, node, err_val_ptr); - ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, node, err_val, nullptr)); - IrInstSrcSpillBegin *spill_begin = ir_build_spill_begin_src(irb, scope, node, err_val, - SpillIdRetErrCode); - ResultLocReturn *result_loc_ret = heap::c_allocator.create(); - result_loc_ret->base.id = ResultLocIdReturn; - ir_build_reset_result(irb, scope, node, &result_loc_ret->base); - ir_build_end_expr(irb, scope, node, err_val, &result_loc_ret->base); - - bool is_noreturn = false; - if (!ir_gen_defers_for_block(irb, scope, outer_scope, &is_noreturn, err_val)) { - return irb->codegen->invalid_inst_src; - } - if (!is_noreturn) { - if (irb->codegen->have_err_ret_tracing && !should_inline) { - ir_build_save_err_ret_addr_src(irb, scope, node); - } - err_val = ir_build_spill_end_src(irb, scope, node, spill_begin); - IrInstSrc *ret_inst = ir_build_return_src(irb, scope, node, err_val); - result_loc_ret->base.source_instruction = ret_inst; - } - - ir_set_cursor_at_end_and_append_block(irb, continue_block); - IrInstSrc *unwrapped_ptr = ir_build_unwrap_err_payload_src(irb, scope, node, err_union_ptr, false, false); - if (lval == LValPtr) - return unwrapped_ptr; - else - return ir_expr_wrap(irb, scope, ir_build_load_ptr(irb, scope, node, unwrapped_ptr), result_loc); - } - } - zig_unreachable(); -} - -static ZigVar *create_local_var(CodeGen *codegen, AstNode *node, Scope *parent_scope, - Buf *name, bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstSrc *is_comptime, - bool skip_name_check) -{ - ZigVar *variable_entry = heap::c_allocator.create(); - variable_entry->parent_scope = parent_scope; - variable_entry->shadowable = is_shadowable; - variable_entry->is_comptime = is_comptime; - variable_entry->src_arg_index = SIZE_MAX; - variable_entry->const_value = codegen->pass1_arena->create(); - - if (is_comptime != nullptr) { - is_comptime->base.ref_count += 1; - } - - if (name) { - variable_entry->name = strdup(buf_ptr(name)); - - if (!skip_name_check) { - ZigVar *existing_var = find_variable(codegen, 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(codegen, node, - buf_sprintf("redeclaration of variable '%s'", buf_ptr(name))); - add_error_note(codegen, msg, existing_var->decl_node, buf_sprintf("previous declaration is here")); - } - variable_entry->var_type = codegen->builtin_types.entry_invalid; - } else { - ZigType *type; - if (get_primitive_type(codegen, name, &type) != ErrorPrimitiveTypeNotFound) { - add_node_error(codegen, node, - buf_sprintf("variable shadows primitive type '%s'", buf_ptr(name))); - variable_entry->var_type = codegen->builtin_types.entry_invalid; - } else { - Tld *tld = find_decl(codegen, parent_scope, name); - if (tld != nullptr) { - 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(codegen, node, - buf_sprintf("redefinition of '%s'", buf_ptr(name))); - add_error_note(codegen, msg, tld->source_node, buf_sprintf("previous definition is here")); - } - variable_entry->var_type = codegen->builtin_types.entry_invalid; - } - } - } - } - } else { - assert(is_shadowable); - // TODO make this name not actually be in scope. user should be able to make a variable called "_anon" - // might already be solved, let's just make sure it has test coverage - // maybe we put a prefix on this so the debug info doesn't clobber user debug info for same named variables - variable_entry->name = "_anon"; - } - - variable_entry->src_is_const = src_is_const; - variable_entry->gen_is_const = gen_is_const; - variable_entry->decl_node = node; - variable_entry->child_scope = create_var_scope(codegen, node, parent_scope, variable_entry); - - return variable_entry; -} - -// Set name to nullptr to make the variable anonymous (not visible to programmer). -// After you call this function var->child_scope has the variable in scope -static ZigVar *ir_create_var(IrBuilderSrc *irb, AstNode *node, Scope *scope, Buf *name, - bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstSrc *is_comptime) -{ - bool is_underscored = name ? buf_eql_str(name, "_") : false; - ZigVar *var = create_local_var(irb->codegen, node, scope, - (is_underscored ? nullptr : name), src_is_const, gen_is_const, - (is_underscored ? true : is_shadowable), is_comptime, false); - assert(var->child_scope); - return var; -} - -static ResultLocPeer *create_peer_result(ResultLocPeerParent *peer_parent) { - ResultLocPeer *result = heap::c_allocator.create(); - result->base.id = ResultLocIdPeer; - result->base.source_instruction = peer_parent->base.source_instruction; - result->parent = peer_parent; - result->base.allow_write_through_const = peer_parent->parent->allow_write_through_const; - return result; -} - -static bool is_duplicate_label(CodeGen *g, Scope *scope, AstNode *node, Buf *name) { - if (name == nullptr) return false; - - for (;;) { - if (scope == nullptr || scope->id == ScopeIdFnDef) { - break; - } else if (scope->id == ScopeIdBlock || scope->id == ScopeIdLoop) { - Buf *this_block_name = scope->id == ScopeIdBlock ? ((ScopeBlock *)scope)->name : ((ScopeLoop *)scope)->name; - if (this_block_name != nullptr && buf_eql_buf(name, this_block_name)) { - ErrorMsg *msg = add_node_error(g, node, buf_sprintf("redeclaration of label '%s'", buf_ptr(name))); - add_error_note(g, msg, scope->source_node, buf_sprintf("previous declaration is here")); - return true; - } - } - scope = scope->parent; - } - return false; -} - -static IrInstSrc *ir_gen_block(IrBuilderSrc *irb, Scope *parent_scope, AstNode *block_node, LVal lval, - ResultLoc *result_loc) -{ - assert(block_node->type == NodeTypeBlock); - - ZigList incoming_values = {0}; - ZigList incoming_blocks = {0}; - - if (is_duplicate_label(irb->codegen, parent_scope, block_node, block_node->data.block.name)) - return irb->codegen->invalid_inst_src; - - ScopeBlock *scope_block = create_block_scope(irb->codegen, block_node, parent_scope); - - Scope *outer_block_scope = &scope_block->base; - Scope *child_scope = outer_block_scope; - - ZigFn *fn_entry = scope_fn_entry(parent_scope); - if (fn_entry && fn_entry->child_scope == parent_scope) { - fn_entry->def_scope = scope_block; - } - - if (block_node->data.block.statements.length == 0) { - if (scope_block->name != nullptr) { - add_node_error(irb->codegen, block_node, buf_sprintf("unused block label")); - } - // {} - return ir_lval_wrap(irb, parent_scope, ir_build_const_void(irb, child_scope, block_node), lval, result_loc); - } - - if (block_node->data.block.name != nullptr) { - scope_block->lval = lval; - scope_block->incoming_blocks = &incoming_blocks; - scope_block->incoming_values = &incoming_values; - scope_block->end_block = ir_create_basic_block(irb, parent_scope, "BlockEnd"); - scope_block->is_comptime = ir_build_const_bool(irb, parent_scope, block_node, - ir_should_inline(irb->exec, parent_scope)); - - scope_block->peer_parent = heap::c_allocator.create(); - scope_block->peer_parent->base.id = ResultLocIdPeerParent; - scope_block->peer_parent->base.source_instruction = scope_block->is_comptime; - scope_block->peer_parent->base.allow_write_through_const = result_loc->allow_write_through_const; - scope_block->peer_parent->end_bb = scope_block->end_block; - scope_block->peer_parent->is_comptime = scope_block->is_comptime; - scope_block->peer_parent->parent = result_loc; - ir_build_reset_result(irb, parent_scope, block_node, &scope_block->peer_parent->base); - } - - bool is_continuation_unreachable = false; - bool found_invalid_inst = false; - IrInstSrc *noreturn_return_value = nullptr; - for (size_t i = 0; i < block_node->data.block.statements.length; i += 1) { - AstNode *statement_node = block_node->data.block.statements.at(i); - - IrInstSrc *statement_value = ir_gen_node(irb, statement_node, child_scope); - if (statement_value == irb->codegen->invalid_inst_src) { - // keep generating all the elements of the block in case of error, - // we want to collect other compile errors - found_invalid_inst = true; - continue; - } - - is_continuation_unreachable = instr_is_unreachable(statement_value); - if (is_continuation_unreachable) { - // keep the last noreturn statement value around in case we need to return it - noreturn_return_value = statement_value; - } - // This logic must be kept in sync with - // [STMT_EXPR_TEST_THING] <--- (search this token) - if (statement_node->type == NodeTypeDefer) { - // defer starts a new scope - child_scope = statement_node->data.defer.child_scope; - assert(child_scope); - } else if (statement_value->id == IrInstSrcIdDeclVar) { - // variable declarations start a new scope - IrInstSrcDeclVar *decl_var_instruction = (IrInstSrcDeclVar *)statement_value; - child_scope = decl_var_instruction->var->child_scope; - } else if (!is_continuation_unreachable) { - // this statement's value must be void - ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, statement_node, statement_value)); - } - } - - if (scope_block->name != nullptr && scope_block->name_used == false) { - add_node_error(irb->codegen, block_node, buf_sprintf("unused block label")); - } - - if (found_invalid_inst) - return irb->codegen->invalid_inst_src; - - if (is_continuation_unreachable) { - assert(noreturn_return_value != nullptr); - if (block_node->data.block.name == nullptr || incoming_blocks.length == 0) { - return noreturn_return_value; - } - - if (scope_block->peer_parent != nullptr && scope_block->peer_parent->peers.length != 0) { - scope_block->peer_parent->peers.last()->next_bb = scope_block->end_block; - } - ir_set_cursor_at_end_and_append_block(irb, scope_block->end_block); - IrInstSrc *phi = ir_build_phi(irb, parent_scope, block_node, incoming_blocks.length, - incoming_blocks.items, incoming_values.items, scope_block->peer_parent); - return ir_expr_wrap(irb, parent_scope, phi, result_loc); - } else { - incoming_blocks.append(irb->current_basic_block); - IrInstSrc *else_expr_result = ir_mark_gen(ir_build_const_void(irb, parent_scope, block_node)); - - if (scope_block->peer_parent != nullptr) { - ResultLocPeer *peer_result = create_peer_result(scope_block->peer_parent); - scope_block->peer_parent->peers.append(peer_result); - ir_build_end_expr(irb, parent_scope, block_node, else_expr_result, &peer_result->base); - - if (scope_block->peer_parent->peers.length != 0) { - scope_block->peer_parent->peers.last()->next_bb = scope_block->end_block; - } - } - - incoming_values.append(else_expr_result); - } - - bool is_return_from_fn = block_node == irb->main_block_node; - if (!is_return_from_fn) { - if (!ir_gen_defers_for_block(irb, child_scope, outer_block_scope, nullptr, nullptr)) - return irb->codegen->invalid_inst_src; - } - - IrInstSrc *result; - if (block_node->data.block.name != nullptr) { - ir_mark_gen(ir_build_br(irb, parent_scope, block_node, scope_block->end_block, scope_block->is_comptime)); - ir_set_cursor_at_end_and_append_block(irb, scope_block->end_block); - IrInstSrc *phi = ir_build_phi(irb, parent_scope, block_node, incoming_blocks.length, - incoming_blocks.items, incoming_values.items, scope_block->peer_parent); - result = ir_expr_wrap(irb, parent_scope, phi, result_loc); - } else { - IrInstSrc *void_inst = ir_mark_gen(ir_build_const_void(irb, child_scope, block_node)); - result = ir_lval_wrap(irb, parent_scope, void_inst, lval, result_loc); - } - if (!is_return_from_fn) - return result; - - // no need for save_err_ret_addr because this cannot return error - // only generate unconditional defers - - ir_mark_gen(ir_build_add_implicit_return_type(irb, child_scope, block_node, result, nullptr)); - ResultLocReturn *result_loc_ret = heap::c_allocator.create(); - result_loc_ret->base.id = ResultLocIdReturn; - ir_build_reset_result(irb, parent_scope, block_node, &result_loc_ret->base); - ir_mark_gen(ir_build_end_expr(irb, parent_scope, block_node, result, &result_loc_ret->base)); - if (!ir_gen_defers_for_block(irb, child_scope, outer_block_scope, nullptr, nullptr)) - return irb->codegen->invalid_inst_src; - return ir_mark_gen(ir_build_return_src(irb, child_scope, result->base.source_node, result)); -} - -static IrInstSrc *ir_gen_bin_op_id(IrBuilderSrc *irb, Scope *scope, AstNode *node, IrBinOp op_id) { - Scope *inner_scope = scope; - if (op_id == IrBinOpArrayCat || op_id == IrBinOpArrayMult) { - inner_scope = create_comptime_scope(irb->codegen, node, scope); - } - - IrInstSrc *op1 = ir_gen_node(irb, node->data.bin_op_expr.op1, inner_scope); - IrInstSrc *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, inner_scope); - - if (op1 == irb->codegen->invalid_inst_src || op2 == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - - return ir_build_bin_op(irb, scope, node, op_id, op1, op2, true); -} - -static IrInstSrc *ir_gen_merge_err_sets(IrBuilderSrc *irb, Scope *scope, AstNode *node) { - IrInstSrc *op1 = ir_gen_node(irb, node->data.bin_op_expr.op1, scope); - IrInstSrc *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope); - - if (op1 == irb->codegen->invalid_inst_src || op2 == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - - // TODO only pass type_name when the || operator is the top level AST node in the var decl expr - Buf bare_name = BUF_INIT; - Buf *type_name = get_anon_type_name(irb->codegen, irb->exec, "error", scope, node, &bare_name); - - return ir_build_merge_err_sets(irb, scope, node, op1, op2, type_name); -} - -static IrInstSrc *ir_gen_assign(IrBuilderSrc *irb, Scope *scope, AstNode *node) { - IrInstSrc *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LValAssign, nullptr); - if (lvalue == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - - ResultLocInstruction *result_loc_inst = heap::c_allocator.create(); - result_loc_inst->base.id = ResultLocIdInstruction; - result_loc_inst->base.source_instruction = lvalue; - ir_ref_instruction(lvalue, irb->current_basic_block); - ir_build_reset_result(irb, scope, node, &result_loc_inst->base); - - IrInstSrc *rvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op2, scope, LValNone, - &result_loc_inst->base); - if (rvalue == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - - return ir_build_const_void(irb, scope, node); -} - -static IrInstSrc *ir_gen_assign_merge_err_sets(IrBuilderSrc *irb, Scope *scope, AstNode *node) { - IrInstSrc *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LValAssign, nullptr); - if (lvalue == irb->codegen->invalid_inst_src) - return lvalue; - IrInstSrc *op1 = ir_build_load_ptr(irb, scope, node->data.bin_op_expr.op1, lvalue); - IrInstSrc *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope); - if (op2 == irb->codegen->invalid_inst_src) - return op2; - IrInstSrc *result = ir_build_merge_err_sets(irb, scope, node, op1, op2, nullptr); - ir_build_store_ptr(irb, scope, node, lvalue, result); - return ir_build_const_void(irb, scope, node); -} - -static IrInstSrc *ir_gen_assign_op(IrBuilderSrc *irb, Scope *scope, AstNode *node, IrBinOp op_id) { - IrInstSrc *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LValAssign, nullptr); - if (lvalue == irb->codegen->invalid_inst_src) - return lvalue; - IrInstSrc *op1 = ir_build_load_ptr(irb, scope, node->data.bin_op_expr.op1, lvalue); - IrInstSrc *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope); - if (op2 == irb->codegen->invalid_inst_src) - return op2; - IrInstSrc *result = ir_build_bin_op(irb, scope, node, op_id, op1, op2, true); - ir_build_store_ptr(irb, scope, node, lvalue, result); - return ir_build_const_void(irb, scope, node); -} - -static IrInstSrc *ir_gen_bool_or(IrBuilderSrc *irb, Scope *scope, AstNode *node) { - assert(node->type == NodeTypeBinOpExpr); - - IrInstSrc *val1 = ir_gen_node(irb, node->data.bin_op_expr.op1, scope); - if (val1 == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - IrBasicBlockSrc *post_val1_block = irb->current_basic_block; - - IrInstSrc *is_comptime; - if (ir_should_inline(irb->exec, scope)) { - is_comptime = ir_build_const_bool(irb, scope, node, true); - } else { - is_comptime = ir_build_test_comptime(irb, scope, node, val1); - } - - // block for when val1 == false - IrBasicBlockSrc *false_block = ir_create_basic_block(irb, scope, "BoolOrFalse"); - // block for when val1 == true (don't even evaluate the second part) - IrBasicBlockSrc *true_block = ir_create_basic_block(irb, scope, "BoolOrTrue"); - - ir_build_cond_br(irb, scope, node, val1, true_block, false_block, is_comptime); - - ir_set_cursor_at_end_and_append_block(irb, false_block); - IrInstSrc *val2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope); - if (val2 == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - IrBasicBlockSrc *post_val2_block = irb->current_basic_block; - - ir_build_br(irb, scope, node, true_block, is_comptime); - - ir_set_cursor_at_end_and_append_block(irb, true_block); - - IrInstSrc **incoming_values = heap::c_allocator.allocate(2); - incoming_values[0] = val1; - incoming_values[1] = val2; - IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate(2); - incoming_blocks[0] = post_val1_block; - incoming_blocks[1] = post_val2_block; - - return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values, nullptr); -} - -static IrInstSrc *ir_gen_bool_and(IrBuilderSrc *irb, Scope *scope, AstNode *node) { - assert(node->type == NodeTypeBinOpExpr); - - IrInstSrc *val1 = ir_gen_node(irb, node->data.bin_op_expr.op1, scope); - if (val1 == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - IrBasicBlockSrc *post_val1_block = irb->current_basic_block; - - IrInstSrc *is_comptime; - if (ir_should_inline(irb->exec, scope)) { - is_comptime = ir_build_const_bool(irb, scope, node, true); - } else { - is_comptime = ir_build_test_comptime(irb, scope, node, val1); - } - - // block for when val1 == true - IrBasicBlockSrc *true_block = ir_create_basic_block(irb, scope, "BoolAndTrue"); - // block for when val1 == false (don't even evaluate the second part) - IrBasicBlockSrc *false_block = ir_create_basic_block(irb, scope, "BoolAndFalse"); - - ir_build_cond_br(irb, scope, node, val1, true_block, false_block, is_comptime); - - ir_set_cursor_at_end_and_append_block(irb, true_block); - IrInstSrc *val2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope); - if (val2 == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - IrBasicBlockSrc *post_val2_block = irb->current_basic_block; - - ir_build_br(irb, scope, node, false_block, is_comptime); - - ir_set_cursor_at_end_and_append_block(irb, false_block); - - IrInstSrc **incoming_values = heap::c_allocator.allocate(2); - incoming_values[0] = val1; - incoming_values[1] = val2; - IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate(2); - incoming_blocks[0] = post_val1_block; - incoming_blocks[1] = post_val2_block; - - return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values, nullptr); -} - -static ResultLocPeerParent *ir_build_result_peers(IrBuilderSrc *irb, IrInstSrc *cond_br_inst, - IrBasicBlockSrc *end_block, ResultLoc *parent, IrInstSrc *is_comptime) -{ - ResultLocPeerParent *peer_parent = heap::c_allocator.create(); - peer_parent->base.id = ResultLocIdPeerParent; - peer_parent->base.source_instruction = cond_br_inst; - peer_parent->base.allow_write_through_const = parent->allow_write_through_const; - peer_parent->end_bb = end_block; - peer_parent->is_comptime = is_comptime; - peer_parent->parent = parent; - - IrInstSrc *popped_inst = irb->current_basic_block->instruction_list.pop(); - ir_assert(popped_inst == cond_br_inst, &cond_br_inst->base); - - ir_build_reset_result(irb, cond_br_inst->base.scope, cond_br_inst->base.source_node, &peer_parent->base); - irb->current_basic_block->instruction_list.append(popped_inst); - - return peer_parent; -} - -static ResultLocPeerParent *ir_build_binary_result_peers(IrBuilderSrc *irb, IrInstSrc *cond_br_inst, - IrBasicBlockSrc *else_block, IrBasicBlockSrc *end_block, ResultLoc *parent, IrInstSrc *is_comptime) -{ - ResultLocPeerParent *peer_parent = ir_build_result_peers(irb, cond_br_inst, end_block, parent, is_comptime); - - peer_parent->peers.append(create_peer_result(peer_parent)); - peer_parent->peers.last()->next_bb = else_block; - - peer_parent->peers.append(create_peer_result(peer_parent)); - peer_parent->peers.last()->next_bb = end_block; - - return peer_parent; -} - -static IrInstSrc *ir_gen_orelse(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node, LVal lval, - ResultLoc *result_loc) -{ - assert(node->type == NodeTypeBinOpExpr); - - AstNode *op1_node = node->data.bin_op_expr.op1; - AstNode *op2_node = node->data.bin_op_expr.op2; - - IrInstSrc *maybe_ptr = ir_gen_node_extra(irb, op1_node, parent_scope, LValPtr, nullptr); - if (maybe_ptr == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - - IrInstSrc *maybe_val = ir_build_load_ptr(irb, parent_scope, node, maybe_ptr); - IrInstSrc *is_non_null = ir_build_test_non_null_src(irb, parent_scope, node, maybe_val); - - IrInstSrc *is_comptime; - if (ir_should_inline(irb->exec, parent_scope)) { - is_comptime = ir_build_const_bool(irb, parent_scope, node, true); - } else { - is_comptime = ir_build_test_comptime(irb, parent_scope, node, is_non_null); - } - - IrBasicBlockSrc *ok_block = ir_create_basic_block(irb, parent_scope, "OptionalNonNull"); - IrBasicBlockSrc *null_block = ir_create_basic_block(irb, parent_scope, "OptionalNull"); - IrBasicBlockSrc *end_block = ir_create_basic_block(irb, parent_scope, "OptionalEnd"); - IrInstSrc *cond_br_inst = ir_build_cond_br(irb, parent_scope, node, is_non_null, ok_block, null_block, is_comptime); - - ResultLocPeerParent *peer_parent = ir_build_binary_result_peers(irb, cond_br_inst, ok_block, end_block, - result_loc, is_comptime); - - ir_set_cursor_at_end_and_append_block(irb, null_block); - IrInstSrc *null_result = ir_gen_node_extra(irb, op2_node, parent_scope, LValNone, - &peer_parent->peers.at(0)->base); - if (null_result == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - IrBasicBlockSrc *after_null_block = irb->current_basic_block; - if (!instr_is_unreachable(null_result)) - ir_mark_gen(ir_build_br(irb, parent_scope, node, end_block, is_comptime)); - - ir_set_cursor_at_end_and_append_block(irb, ok_block); - IrInstSrc *unwrapped_ptr = ir_build_optional_unwrap_ptr(irb, parent_scope, node, maybe_ptr, false); - IrInstSrc *unwrapped_payload = ir_build_load_ptr(irb, parent_scope, node, unwrapped_ptr); - ir_build_end_expr(irb, parent_scope, node, unwrapped_payload, &peer_parent->peers.at(1)->base); - IrBasicBlockSrc *after_ok_block = irb->current_basic_block; - ir_build_br(irb, parent_scope, node, end_block, is_comptime); - - ir_set_cursor_at_end_and_append_block(irb, end_block); - IrInstSrc **incoming_values = heap::c_allocator.allocate(2); - incoming_values[0] = null_result; - incoming_values[1] = unwrapped_payload; - IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate(2); - incoming_blocks[0] = after_null_block; - incoming_blocks[1] = after_ok_block; - IrInstSrc *phi = ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values, peer_parent); - return ir_lval_wrap(irb, parent_scope, phi, lval, result_loc); -} - -static IrInstSrc *ir_gen_error_union(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node) { - assert(node->type == NodeTypeBinOpExpr); - - AstNode *op1_node = node->data.bin_op_expr.op1; - AstNode *op2_node = node->data.bin_op_expr.op2; - - IrInstSrc *err_set = ir_gen_node(irb, op1_node, parent_scope); - if (err_set == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - - IrInstSrc *payload = ir_gen_node(irb, op2_node, parent_scope); - if (payload == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - - return ir_build_error_union(irb, parent_scope, node, err_set, payload); -} - -static IrInstSrc *ir_gen_bin_op(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *result_loc) { - assert(node->type == NodeTypeBinOpExpr); - - BinOpType bin_op_type = node->data.bin_op_expr.bin_op; - switch (bin_op_type) { - case BinOpTypeInvalid: - zig_unreachable(); - case BinOpTypeAssign: - return ir_lval_wrap(irb, scope, ir_gen_assign(irb, scope, node), lval, result_loc); - case BinOpTypeAssignTimes: - return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpMult), lval, result_loc); - case BinOpTypeAssignTimesWrap: - return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpMultWrap), lval, result_loc); - case BinOpTypeAssignDiv: - return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpDivUnspecified), lval, result_loc); - case BinOpTypeAssignMod: - return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpRemUnspecified), lval, result_loc); - case BinOpTypeAssignPlus: - return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpAdd), lval, result_loc); - case BinOpTypeAssignPlusWrap: - return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpAddWrap), lval, result_loc); - case BinOpTypeAssignMinus: - return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpSub), lval, result_loc); - case BinOpTypeAssignMinusWrap: - return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpSubWrap), lval, result_loc); - case BinOpTypeAssignBitShiftLeft: - return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftLeftLossy), lval, result_loc); - case BinOpTypeAssignBitShiftRight: - return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftRightLossy), lval, result_loc); - case BinOpTypeAssignBitAnd: - return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBinAnd), lval, result_loc); - case BinOpTypeAssignBitXor: - return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBinXor), lval, result_loc); - case BinOpTypeAssignBitOr: - return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBinOr), lval, result_loc); - case BinOpTypeAssignMergeErrorSets: - return ir_lval_wrap(irb, scope, ir_gen_assign_merge_err_sets(irb, scope, node), lval, result_loc); - case BinOpTypeBoolOr: - return ir_lval_wrap(irb, scope, ir_gen_bool_or(irb, scope, node), lval, result_loc); - case BinOpTypeBoolAnd: - return ir_lval_wrap(irb, scope, ir_gen_bool_and(irb, scope, node), lval, result_loc); - case BinOpTypeCmpEq: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpEq), lval, result_loc); - case BinOpTypeCmpNotEq: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpNotEq), lval, result_loc); - case BinOpTypeCmpLessThan: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpLessThan), lval, result_loc); - case BinOpTypeCmpGreaterThan: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpGreaterThan), lval, result_loc); - case BinOpTypeCmpLessOrEq: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpLessOrEq), lval, result_loc); - case BinOpTypeCmpGreaterOrEq: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpGreaterOrEq), lval, result_loc); - case BinOpTypeBinOr: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBinOr), lval, result_loc); - case BinOpTypeBinXor: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBinXor), lval, result_loc); - case BinOpTypeBinAnd: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBinAnd), lval, result_loc); - case BinOpTypeBitShiftLeft: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftLeftLossy), lval, result_loc); - case BinOpTypeBitShiftRight: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftRightLossy), lval, result_loc); - case BinOpTypeAdd: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpAdd), lval, result_loc); - case BinOpTypeAddWrap: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpAddWrap), lval, result_loc); - case BinOpTypeSub: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpSub), lval, result_loc); - case BinOpTypeSubWrap: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpSubWrap), lval, result_loc); - case BinOpTypeMult: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpMult), lval, result_loc); - case BinOpTypeMultWrap: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpMultWrap), lval, result_loc); - case BinOpTypeDiv: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpDivUnspecified), lval, result_loc); - case BinOpTypeMod: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpRemUnspecified), lval, result_loc); - case BinOpTypeArrayCat: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayCat), lval, result_loc); - case BinOpTypeArrayMult: - return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayMult), lval, result_loc); - case BinOpTypeMergeErrorSets: - return ir_lval_wrap(irb, scope, ir_gen_merge_err_sets(irb, scope, node), lval, result_loc); - case BinOpTypeUnwrapOptional: - return ir_gen_orelse(irb, scope, node, lval, result_loc); - case BinOpTypeErrorUnion: - return ir_lval_wrap(irb, scope, ir_gen_error_union(irb, scope, node), lval, result_loc); - } - zig_unreachable(); -} - -static IrInstSrc *ir_gen_int_lit(IrBuilderSrc *irb, Scope *scope, AstNode *node) { - assert(node->type == NodeTypeIntLiteral); - - return ir_build_const_bigint(irb, scope, node, node->data.int_literal.bigint); -} - -static IrInstSrc *ir_gen_float_lit(IrBuilderSrc *irb, Scope *scope, AstNode *node) { - assert(node->type == NodeTypeFloatLiteral); - - if (node->data.float_literal.overflow) { - add_node_error(irb->codegen, node, buf_sprintf("float literal out of range of any type")); - return irb->codegen->invalid_inst_src; - } - - return ir_build_const_bigfloat(irb, scope, node, node->data.float_literal.bigfloat); -} - -static IrInstSrc *ir_gen_char_lit(IrBuilderSrc *irb, Scope *scope, AstNode *node) { - assert(node->type == NodeTypeCharLiteral); - - return ir_build_const_uint(irb, scope, node, node->data.char_literal.value); -} - -static IrInstSrc *ir_gen_null_literal(IrBuilderSrc *irb, Scope *scope, AstNode *node) { - assert(node->type == NodeTypeNullLiteral); - - return ir_build_const_null(irb, scope, node); -} - -static void populate_invalid_variable_in_scope(CodeGen *g, Scope *scope, AstNode *node, Buf *var_name) { - ScopeDecls *scope_decls = nullptr; - while (scope != nullptr) { - if (scope->id == ScopeIdDecls) { - scope_decls = reinterpret_cast(scope); - } - scope = scope->parent; - } - TldVar *tld_var = heap::c_allocator.create(); - init_tld(&tld_var->base, TldIdVar, var_name, VisibModPub, node, &scope_decls->base); - tld_var->base.resolution = TldResolutionInvalid; - tld_var->var = add_variable(g, node, &scope_decls->base, var_name, false, - g->invalid_inst_gen->value, &tld_var->base, g->builtin_types.entry_invalid); - scope_decls->decl_table.put(var_name, &tld_var->base); -} - -static IrInstSrc *ir_gen_symbol(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *result_loc) { - Error err; - assert(node->type == NodeTypeSymbol); - - Buf *variable_name = node->data.symbol_expr.symbol; - - if (buf_eql_str(variable_name, "_")) { - if (lval == LValAssign) { - IrInstSrcConst *const_instruction = ir_build_instruction(irb, scope, node); - const_instruction->value = irb->codegen->pass1_arena->create(); - const_instruction->value->type = get_pointer_to_type(irb->codegen, - irb->codegen->builtin_types.entry_void, false); - const_instruction->value->special = ConstValSpecialStatic; - const_instruction->value->data.x_ptr.special = ConstPtrSpecialDiscard; - return &const_instruction->base; - } else { - add_node_error(irb->codegen, node, buf_sprintf("`_` may only be used to assign things to")); - return irb->codegen->invalid_inst_src; - } - } - - ZigType *primitive_type; - if ((err = get_primitive_type(irb->codegen, variable_name, &primitive_type))) { - if (err == ErrorOverflow) { - add_node_error(irb->codegen, node, - buf_sprintf("primitive integer type '%s' exceeds maximum bit width of 65535", - buf_ptr(variable_name))); - return irb->codegen->invalid_inst_src; - } - assert(err == ErrorPrimitiveTypeNotFound); - } else { - IrInstSrc *value = ir_build_const_type(irb, scope, node, primitive_type); - if (lval == LValPtr || lval == LValAssign) { - return ir_build_ref_src(irb, scope, node, value); - } else { - return ir_expr_wrap(irb, scope, value, result_loc); - } - } - - ScopeFnDef *crossed_fndef_scope; - ZigVar *var = find_variable(irb->codegen, scope, variable_name, &crossed_fndef_scope); - if (var) { - IrInstSrc *var_ptr = ir_build_var_ptr_x(irb, scope, node, var, crossed_fndef_scope); - if (lval == LValPtr || lval == LValAssign) { - return var_ptr; - } else { - return ir_expr_wrap(irb, scope, ir_build_load_ptr(irb, scope, node, var_ptr), result_loc); - } - } - - Tld *tld = find_decl(irb->codegen, scope, variable_name); - if (tld) { - IrInstSrc *decl_ref = ir_build_decl_ref(irb, scope, node, tld, lval); - if (lval == LValPtr || lval == LValAssign) { - return decl_ref; - } else { - return ir_expr_wrap(irb, scope, decl_ref, result_loc); - } - } - - if (get_container_scope(node->owner)->any_imports_failed) { - // skip the error message since we had a failing import in this file - // if an import breaks we don't need redundant undeclared identifier errors - return irb->codegen->invalid_inst_src; - } - - return ir_build_undeclared_identifier(irb, scope, node, variable_name); -} - -static IrInstSrc *ir_gen_array_access(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, - ResultLoc *result_loc) -{ - assert(node->type == NodeTypeArrayAccessExpr); - - AstNode *array_ref_node = node->data.array_access_expr.array_ref_expr; - IrInstSrc *array_ref_instruction = ir_gen_node_extra(irb, array_ref_node, scope, LValPtr, nullptr); - if (array_ref_instruction == irb->codegen->invalid_inst_src) - return array_ref_instruction; - - // Create an usize-typed result location to hold the subscript value, this - // makes it possible for the compiler to infer the subscript expression type - // if needed - IrInstSrc *usize_type_inst = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize); - ResultLocCast *result_loc_cast = ir_build_cast_result_loc(irb, usize_type_inst, no_result_loc()); - - AstNode *subscript_node = node->data.array_access_expr.subscript; - IrInstSrc *subscript_value = ir_gen_node_extra(irb, subscript_node, scope, LValNone, &result_loc_cast->base); - if (subscript_value == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - - IrInstSrc *subscript_instruction = ir_build_implicit_cast(irb, scope, subscript_node, subscript_value, result_loc_cast); - - IrInstSrc *ptr_instruction = ir_build_elem_ptr(irb, scope, node, array_ref_instruction, - subscript_instruction, true, PtrLenSingle, nullptr); - if (lval == LValPtr || lval == LValAssign) - return ptr_instruction; - - IrInstSrc *load_ptr = ir_build_load_ptr(irb, scope, node, ptr_instruction); - return ir_expr_wrap(irb, scope, load_ptr, result_loc); -} - -static IrInstSrc *ir_gen_field_access(IrBuilderSrc *irb, Scope *scope, AstNode *node) { - assert(node->type == NodeTypeFieldAccessExpr); - - AstNode *container_ref_node = node->data.field_access_expr.struct_expr; - Buf *field_name = node->data.field_access_expr.field_name; - - IrInstSrc *container_ref_instruction = ir_gen_node_extra(irb, container_ref_node, scope, LValPtr, nullptr); - if (container_ref_instruction == irb->codegen->invalid_inst_src) - return container_ref_instruction; - - return ir_build_field_ptr(irb, scope, node, container_ref_instruction, field_name, false); -} - -static IrInstSrc *ir_gen_overflow_op(IrBuilderSrc *irb, Scope *scope, AstNode *node, IrOverflowOp op) { - assert(node->type == NodeTypeFnCallExpr); - - AstNode *type_node = node->data.fn_call_expr.params.at(0); - AstNode *op1_node = node->data.fn_call_expr.params.at(1); - AstNode *op2_node = node->data.fn_call_expr.params.at(2); - AstNode *result_ptr_node = node->data.fn_call_expr.params.at(3); - - - IrInstSrc *type_value = ir_gen_node(irb, type_node, scope); - if (type_value == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - - IrInstSrc *op1 = ir_gen_node(irb, op1_node, scope); - if (op1 == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - - IrInstSrc *op2 = ir_gen_node(irb, op2_node, scope); - if (op2 == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - - IrInstSrc *result_ptr = ir_gen_node(irb, result_ptr_node, scope); - if (result_ptr == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - - return ir_build_overflow_op_src(irb, scope, node, op, type_value, op1, op2, result_ptr); -} - -static IrInstSrc *ir_gen_mul_add(IrBuilderSrc *irb, Scope *scope, AstNode *node) { - assert(node->type == NodeTypeFnCallExpr); - - AstNode *type_node = node->data.fn_call_expr.params.at(0); - AstNode *op1_node = node->data.fn_call_expr.params.at(1); - AstNode *op2_node = node->data.fn_call_expr.params.at(2); - AstNode *op3_node = node->data.fn_call_expr.params.at(3); - - IrInstSrc *type_value = ir_gen_node(irb, type_node, scope); - if (type_value == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - - IrInstSrc *op1 = ir_gen_node(irb, op1_node, scope); - if (op1 == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - - IrInstSrc *op2 = ir_gen_node(irb, op2_node, scope); - if (op2 == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - - IrInstSrc *op3 = ir_gen_node(irb, op3_node, scope); - if (op3 == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - - return ir_build_mul_add_src(irb, scope, node, type_value, op1, op2, op3); -} - -static IrInstSrc *ir_gen_this(IrBuilderSrc *irb, Scope *orig_scope, AstNode *node) { - for (Scope *it_scope = orig_scope; it_scope != nullptr; it_scope = it_scope->parent) { - if (it_scope->id == ScopeIdDecls) { - ScopeDecls *decls_scope = (ScopeDecls *)it_scope; - ZigType *container_type = decls_scope->container_type; - if (container_type != nullptr) { - return ir_build_const_type(irb, orig_scope, node, container_type); - } else { - return ir_build_const_import(irb, orig_scope, node, decls_scope->import); - } - } - } - zig_unreachable(); -} - -static IrInstSrc *ir_gen_async_call(IrBuilderSrc *irb, Scope *scope, AstNode *await_node, AstNode *call_node, - LVal lval, ResultLoc *result_loc) -{ - if (call_node->data.fn_call_expr.params.length != 4) { - add_node_error(irb->codegen, call_node, - buf_sprintf("expected 4 arguments, found %" ZIG_PRI_usize, - call_node->data.fn_call_expr.params.length)); - return irb->codegen->invalid_inst_src; - } - - AstNode *bytes_node = call_node->data.fn_call_expr.params.at(0); - IrInstSrc *bytes = ir_gen_node(irb, bytes_node, scope); - if (bytes == irb->codegen->invalid_inst_src) - return bytes; - - AstNode *ret_ptr_node = call_node->data.fn_call_expr.params.at(1); - IrInstSrc *ret_ptr = ir_gen_node(irb, ret_ptr_node, scope); - if (ret_ptr == irb->codegen->invalid_inst_src) - return ret_ptr; - - AstNode *fn_ref_node = call_node->data.fn_call_expr.params.at(2); - IrInstSrc *fn_ref = ir_gen_node(irb, fn_ref_node, scope); - if (fn_ref == irb->codegen->invalid_inst_src) - return fn_ref; - - CallModifier modifier = (await_node == nullptr) ? CallModifierAsync : CallModifierNone; - bool is_async_call_builtin = true; - AstNode *args_node = call_node->data.fn_call_expr.params.at(3); - if (args_node->type == NodeTypeContainerInitExpr) { - if (args_node->data.container_init_expr.kind == ContainerInitKindArray || - args_node->data.container_init_expr.entries.length == 0) - { - size_t arg_count = args_node->data.container_init_expr.entries.length; - IrInstSrc **args = heap::c_allocator.allocate(arg_count); - for (size_t i = 0; i < arg_count; i += 1) { - AstNode *arg_node = args_node->data.container_init_expr.entries.at(i); - IrInstSrc *arg = ir_gen_node(irb, arg_node, scope); - if (arg == irb->codegen->invalid_inst_src) - return arg; - args[i] = arg; - } - - IrInstSrc *call = ir_build_call_src(irb, scope, call_node, nullptr, fn_ref, arg_count, args, - ret_ptr, modifier, is_async_call_builtin, bytes, result_loc); - return ir_lval_wrap(irb, scope, call, lval, result_loc); - } else { - exec_add_error_node(irb->codegen, irb->exec, args_node, - buf_sprintf("TODO: @asyncCall with anon struct literal")); - return irb->codegen->invalid_inst_src; - } - } - IrInstSrc *args = ir_gen_node(irb, args_node, scope); - if (args == irb->codegen->invalid_inst_src) - return args; - - IrInstSrc *call = ir_build_async_call_extra(irb, scope, call_node, modifier, fn_ref, ret_ptr, bytes, args, result_loc); - return ir_lval_wrap(irb, scope, call, lval, result_loc); -} - -static IrInstSrc *ir_gen_fn_call_with_args(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - AstNode *fn_ref_node, CallModifier modifier, IrInstSrc *options, - AstNode **args_ptr, size_t args_len, LVal lval, ResultLoc *result_loc) -{ - IrInstSrc *fn_ref = ir_gen_node(irb, fn_ref_node, scope); - if (fn_ref == irb->codegen->invalid_inst_src) - return fn_ref; - - IrInstSrc *fn_type = ir_build_typeof_1(irb, scope, source_node, fn_ref); - - IrInstSrc **args = heap::c_allocator.allocate(args_len); - for (size_t i = 0; i < args_len; i += 1) { - AstNode *arg_node = args_ptr[i]; - - IrInstSrc *arg_index = ir_build_const_usize(irb, scope, arg_node, i); - IrInstSrc *arg_type = ir_build_arg_type(irb, scope, source_node, fn_type, arg_index, true); - ResultLoc *no_result = no_result_loc(); - ir_build_reset_result(irb, scope, source_node, no_result); - ResultLocCast *result_loc_cast = ir_build_cast_result_loc(irb, arg_type, no_result); - - IrInstSrc *arg = ir_gen_node_extra(irb, arg_node, scope, LValNone, &result_loc_cast->base); - if (arg == irb->codegen->invalid_inst_src) - return arg; - - args[i] = ir_build_implicit_cast(irb, scope, arg_node, arg, result_loc_cast); - } - - IrInstSrc *fn_call; - if (options != nullptr) { - fn_call = ir_build_call_args(irb, scope, source_node, options, fn_ref, args, args_len, result_loc); - } else { - fn_call = ir_build_call_src(irb, scope, source_node, nullptr, fn_ref, args_len, args, nullptr, - modifier, false, nullptr, result_loc); - } - return ir_lval_wrap(irb, scope, fn_call, lval, result_loc); -} - -static IrInstSrc *ir_gen_builtin_fn_call(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, - ResultLoc *result_loc) -{ - assert(node->type == NodeTypeFnCallExpr); - - AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr; - Buf *name = fn_ref_expr->data.symbol_expr.symbol; - auto entry = irb->codegen->builtin_fn_table.maybe_get(name); - - if (!entry) { - add_node_error(irb->codegen, node, - buf_sprintf("invalid builtin function: '%s'", buf_ptr(name))); - return irb->codegen->invalid_inst_src; - } - - BuiltinFnEntry *builtin_fn = entry->value; - size_t actual_param_count = node->data.fn_call_expr.params.length; - - if (builtin_fn->param_count != SIZE_MAX && builtin_fn->param_count != actual_param_count) { - add_node_error(irb->codegen, node, - buf_sprintf("expected %" ZIG_PRI_usize " argument(s), found %" ZIG_PRI_usize, - builtin_fn->param_count, actual_param_count)); - return irb->codegen->invalid_inst_src; - } - - switch (builtin_fn->id) { - case BuiltinFnIdInvalid: - zig_unreachable(); - case BuiltinFnIdTypeof: - { - Scope *sub_scope = create_typeof_scope(irb->codegen, node, scope); - - size_t arg_count = node->data.fn_call_expr.params.length; - - IrInstSrc *type_of; - - if (arg_count == 0) { - add_node_error(irb->codegen, node, - buf_sprintf("expected at least 1 argument, found 0")); - return irb->codegen->invalid_inst_src; - } else if (arg_count == 1) { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, sub_scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - type_of = ir_build_typeof_1(irb, scope, node, arg0_value); - } else { - IrInstSrc **args = heap::c_allocator.allocate(arg_count); - for (size_t i = 0; i < arg_count; i += 1) { - AstNode *arg_node = node->data.fn_call_expr.params.at(i); - IrInstSrc *arg = ir_gen_node(irb, arg_node, sub_scope); - if (arg == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - args[i] = arg; - } - - type_of = ir_build_typeof_n(irb, scope, node, args, arg_count); - } - return ir_lval_wrap(irb, scope, type_of, lval, result_loc); - } - case BuiltinFnIdSetCold: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - IrInstSrc *set_cold = ir_build_set_cold(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, set_cold, lval, result_loc); - } - case BuiltinFnIdSetRuntimeSafety: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - IrInstSrc *set_safety = ir_build_set_runtime_safety(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, set_safety, lval, result_loc); - } - case BuiltinFnIdSetFloatMode: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - IrInstSrc *set_float_mode = ir_build_set_float_mode(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, set_float_mode, lval, result_loc); - } - case BuiltinFnIdSizeof: - case BuiltinFnIdBitSizeof: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - IrInstSrc *size_of = ir_build_size_of(irb, scope, node, arg0_value, builtin_fn->id == BuiltinFnIdBitSizeof); - return ir_lval_wrap(irb, scope, size_of, lval, result_loc); - } - case BuiltinFnIdImport: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - IrInstSrc *import = ir_build_import(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, import, lval, result_loc); - } - case BuiltinFnIdCImport: - { - IrInstSrc *c_import = ir_build_c_import(irb, scope, node); - return ir_lval_wrap(irb, scope, c_import, lval, result_loc); - } - case BuiltinFnIdCInclude: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - if (!exec_c_import_buf(irb->exec)) { - add_node_error(irb->codegen, node, buf_sprintf("C include valid only inside C import block")); - return irb->codegen->invalid_inst_src; - } - - IrInstSrc *c_include = ir_build_c_include(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, c_include, lval, result_loc); - } - case BuiltinFnIdCDefine: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); - if (arg1_value == irb->codegen->invalid_inst_src) - return arg1_value; - - if (!exec_c_import_buf(irb->exec)) { - add_node_error(irb->codegen, node, buf_sprintf("C define valid only inside C import block")); - return irb->codegen->invalid_inst_src; - } - - IrInstSrc *c_define = ir_build_c_define(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, c_define, lval, result_loc); - } - case BuiltinFnIdCUndef: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - if (!exec_c_import_buf(irb->exec)) { - add_node_error(irb->codegen, node, buf_sprintf("C undef valid only inside C import block")); - return irb->codegen->invalid_inst_src; - } - - IrInstSrc *c_undef = ir_build_c_undef(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, c_undef, lval, result_loc); - } - case BuiltinFnIdCompileErr: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - IrInstSrc *compile_err = ir_build_compile_err(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, compile_err, lval, result_loc); - } - case BuiltinFnIdCompileLog: - { - IrInstSrc **args = heap::c_allocator.allocate(actual_param_count); - - for (size_t i = 0; i < actual_param_count; i += 1) { - AstNode *arg_node = node->data.fn_call_expr.params.at(i); - args[i] = ir_gen_node(irb, arg_node, scope); - if (args[i] == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - } - - IrInstSrc *compile_log = ir_build_compile_log(irb, scope, node, actual_param_count, args); - return ir_lval_wrap(irb, scope, compile_log, lval, result_loc); - } - case BuiltinFnIdErrName: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - IrInstSrc *err_name = ir_build_err_name(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, err_name, lval, result_loc); - } - case BuiltinFnIdEmbedFile: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - IrInstSrc *embed_file = ir_build_embed_file(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, embed_file, lval, result_loc); - } - case BuiltinFnIdCmpxchgWeak: - case BuiltinFnIdCmpxchgStrong: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); - if (arg1_value == irb->codegen->invalid_inst_src) - return arg1_value; - - AstNode *arg2_node = node->data.fn_call_expr.params.at(2); - IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope); - if (arg2_value == irb->codegen->invalid_inst_src) - return arg2_value; - - AstNode *arg3_node = node->data.fn_call_expr.params.at(3); - IrInstSrc *arg3_value = ir_gen_node(irb, arg3_node, scope); - if (arg3_value == irb->codegen->invalid_inst_src) - return arg3_value; - - AstNode *arg4_node = node->data.fn_call_expr.params.at(4); - IrInstSrc *arg4_value = ir_gen_node(irb, arg4_node, scope); - if (arg4_value == irb->codegen->invalid_inst_src) - return arg4_value; - - AstNode *arg5_node = node->data.fn_call_expr.params.at(5); - IrInstSrc *arg5_value = ir_gen_node(irb, arg5_node, scope); - if (arg5_value == irb->codegen->invalid_inst_src) - return arg5_value; - - IrInstSrc *cmpxchg = ir_build_cmpxchg_src(irb, scope, node, arg0_value, arg1_value, - arg2_value, arg3_value, arg4_value, arg5_value, (builtin_fn->id == BuiltinFnIdCmpxchgWeak), - result_loc); - return ir_lval_wrap(irb, scope, cmpxchg, lval, result_loc); - } - case BuiltinFnIdFence: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - IrInstSrc *fence = ir_build_fence(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, fence, lval, result_loc); - } - case BuiltinFnIdDivExact: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); - if (arg1_value == irb->codegen->invalid_inst_src) - return arg1_value; - - IrInstSrc *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivExact, arg0_value, arg1_value, true); - return ir_lval_wrap(irb, scope, bin_op, lval, result_loc); - } - case BuiltinFnIdDivTrunc: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); - if (arg1_value == irb->codegen->invalid_inst_src) - return arg1_value; - - IrInstSrc *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivTrunc, arg0_value, arg1_value, true); - return ir_lval_wrap(irb, scope, bin_op, lval, result_loc); - } - case BuiltinFnIdDivFloor: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); - if (arg1_value == irb->codegen->invalid_inst_src) - return arg1_value; - - IrInstSrc *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivFloor, arg0_value, arg1_value, true); - return ir_lval_wrap(irb, scope, bin_op, lval, result_loc); - } - case BuiltinFnIdRem: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); - if (arg1_value == irb->codegen->invalid_inst_src) - return arg1_value; - - IrInstSrc *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpRemRem, arg0_value, arg1_value, true); - return ir_lval_wrap(irb, scope, bin_op, lval, result_loc); - } - case BuiltinFnIdMod: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); - if (arg1_value == irb->codegen->invalid_inst_src) - return arg1_value; - - IrInstSrc *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpRemMod, arg0_value, arg1_value, true); - return ir_lval_wrap(irb, scope, bin_op, lval, result_loc); - } - case BuiltinFnIdSqrt: - case BuiltinFnIdSin: - case BuiltinFnIdCos: - case BuiltinFnIdExp: - case BuiltinFnIdExp2: - case BuiltinFnIdLog: - case BuiltinFnIdLog2: - case BuiltinFnIdLog10: - case BuiltinFnIdFabs: - case BuiltinFnIdFloor: - case BuiltinFnIdCeil: - case BuiltinFnIdTrunc: - case BuiltinFnIdNearbyInt: - case BuiltinFnIdRound: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - IrInstSrc *inst = ir_build_float_op_src(irb, scope, node, arg0_value, builtin_fn->id); - return ir_lval_wrap(irb, scope, inst, lval, result_loc); - } - case BuiltinFnIdTruncate: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); - if (arg1_value == irb->codegen->invalid_inst_src) - return arg1_value; - - IrInstSrc *truncate = ir_build_truncate(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, truncate, lval, result_loc); - } - case BuiltinFnIdIntCast: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); - if (arg1_value == irb->codegen->invalid_inst_src) - return arg1_value; - - IrInstSrc *result = ir_build_int_cast(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, result, lval, result_loc); - } - case BuiltinFnIdFloatCast: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); - if (arg1_value == irb->codegen->invalid_inst_src) - return arg1_value; - - IrInstSrc *result = ir_build_float_cast(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, result, lval, result_loc); - } - case BuiltinFnIdErrSetCast: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); - if (arg1_value == irb->codegen->invalid_inst_src) - return arg1_value; - - IrInstSrc *result = ir_build_err_set_cast(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, result, lval, result_loc); - } - case BuiltinFnIdIntToFloat: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); - if (arg1_value == irb->codegen->invalid_inst_src) - return arg1_value; - - IrInstSrc *result = ir_build_int_to_float(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, result, lval, result_loc); - } - case BuiltinFnIdFloatToInt: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); - if (arg1_value == irb->codegen->invalid_inst_src) - return arg1_value; - - IrInstSrc *result = ir_build_float_to_int(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, result, lval, result_loc); - } - case BuiltinFnIdErrToInt: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - IrInstSrc *result = ir_build_err_to_int_src(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, result, lval, result_loc); - } - case BuiltinFnIdIntToErr: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - IrInstSrc *result = ir_build_int_to_err_src(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, result, lval, result_loc); - } - case BuiltinFnIdBoolToInt: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - IrInstSrc *result = ir_build_bool_to_int(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, result, lval, result_loc); - } - case BuiltinFnIdVectorType: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); - if (arg1_value == irb->codegen->invalid_inst_src) - return arg1_value; - - IrInstSrc *vector_type = ir_build_vector_type(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, vector_type, lval, result_loc); - } - case BuiltinFnIdShuffle: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); - if (arg1_value == irb->codegen->invalid_inst_src) - return arg1_value; - - AstNode *arg2_node = node->data.fn_call_expr.params.at(2); - IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope); - if (arg2_value == irb->codegen->invalid_inst_src) - return arg2_value; - - AstNode *arg3_node = node->data.fn_call_expr.params.at(3); - IrInstSrc *arg3_value = ir_gen_node(irb, arg3_node, scope); - if (arg3_value == irb->codegen->invalid_inst_src) - return arg3_value; - - IrInstSrc *shuffle_vector = ir_build_shuffle_vector(irb, scope, node, - arg0_value, arg1_value, arg2_value, arg3_value); - return ir_lval_wrap(irb, scope, shuffle_vector, lval, result_loc); - } - case BuiltinFnIdSplat: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); - if (arg1_value == irb->codegen->invalid_inst_src) - return arg1_value; - - IrInstSrc *splat = ir_build_splat_src(irb, scope, node, - arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, splat, lval, result_loc); - } - case BuiltinFnIdMemcpy: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); - if (arg1_value == irb->codegen->invalid_inst_src) - return arg1_value; - - AstNode *arg2_node = node->data.fn_call_expr.params.at(2); - IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope); - if (arg2_value == irb->codegen->invalid_inst_src) - return arg2_value; - - IrInstSrc *ir_memcpy = ir_build_memcpy_src(irb, scope, node, arg0_value, arg1_value, arg2_value); - return ir_lval_wrap(irb, scope, ir_memcpy, lval, result_loc); - } - case BuiltinFnIdMemset: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); - if (arg1_value == irb->codegen->invalid_inst_src) - return arg1_value; - - AstNode *arg2_node = node->data.fn_call_expr.params.at(2); - IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope); - if (arg2_value == irb->codegen->invalid_inst_src) - return arg2_value; - - IrInstSrc *ir_memset = ir_build_memset_src(irb, scope, node, arg0_value, arg1_value, arg2_value); - return ir_lval_wrap(irb, scope, ir_memset, lval, result_loc); - } - case BuiltinFnIdWasmMemorySize: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - IrInstSrc *ir_wasm_memory_size = ir_build_wasm_memory_size_src(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, ir_wasm_memory_size, lval, result_loc); - } - case BuiltinFnIdWasmMemoryGrow: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); - if (arg1_value == irb->codegen->invalid_inst_src) - return arg1_value; - - IrInstSrc *ir_wasm_memory_grow = ir_build_wasm_memory_grow_src(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, ir_wasm_memory_grow, lval, result_loc); - } - case BuiltinFnIdField: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node_extra(irb, arg0_node, scope, LValPtr, nullptr); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); - if (arg1_value == irb->codegen->invalid_inst_src) - return arg1_value; - - IrInstSrc *ptr_instruction = ir_build_field_ptr_instruction(irb, scope, node, - arg0_value, arg1_value, false); - - if (lval == LValPtr || lval == LValAssign) - return ptr_instruction; - - IrInstSrc *load_ptr = ir_build_load_ptr(irb, scope, node, ptr_instruction); - return ir_expr_wrap(irb, scope, load_ptr, result_loc); - } - case BuiltinFnIdHasField: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); - if (arg1_value == irb->codegen->invalid_inst_src) - return arg1_value; - - IrInstSrc *type_info = ir_build_has_field(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, type_info, lval, result_loc); - } - case BuiltinFnIdTypeInfo: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - IrInstSrc *type_info = ir_build_type_info(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, type_info, lval, result_loc); - } - case BuiltinFnIdType: - { - AstNode *arg_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg = ir_gen_node(irb, arg_node, scope); - if (arg == irb->codegen->invalid_inst_src) - return arg; - - IrInstSrc *type = ir_build_type(irb, scope, node, arg); - return ir_lval_wrap(irb, scope, type, lval, result_loc); - } - case BuiltinFnIdBreakpoint: - return ir_lval_wrap(irb, scope, ir_build_breakpoint(irb, scope, node), lval, result_loc); - case BuiltinFnIdReturnAddress: - return ir_lval_wrap(irb, scope, ir_build_return_address_src(irb, scope, node), lval, result_loc); - case BuiltinFnIdFrameAddress: - return ir_lval_wrap(irb, scope, ir_build_frame_address_src(irb, scope, node), lval, result_loc); - case BuiltinFnIdFrameHandle: - if (!irb->exec->fn_entry) { - add_node_error(irb->codegen, node, buf_sprintf("@frame() called outside of function definition")); - return irb->codegen->invalid_inst_src; - } - return ir_lval_wrap(irb, scope, ir_build_handle_src(irb, scope, node), lval, result_loc); - case BuiltinFnIdFrameType: { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - IrInstSrc *frame_type = ir_build_frame_type(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, frame_type, lval, result_loc); - } - case BuiltinFnIdFrameSize: { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - IrInstSrc *frame_size = ir_build_frame_size_src(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, frame_size, lval, result_loc); - } - case BuiltinFnIdAlignOf: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - IrInstSrc *align_of = ir_build_align_of(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, align_of, lval, result_loc); - } - case BuiltinFnIdAddWithOverflow: - return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpAdd), lval, result_loc); - case BuiltinFnIdSubWithOverflow: - return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpSub), lval, result_loc); - case BuiltinFnIdMulWithOverflow: - return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpMul), lval, result_loc); - case BuiltinFnIdShlWithOverflow: - return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpShl), lval, result_loc); - case BuiltinFnIdMulAdd: - return ir_lval_wrap(irb, scope, ir_gen_mul_add(irb, scope, node), lval, result_loc); - case BuiltinFnIdTypeName: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - IrInstSrc *type_name = ir_build_type_name(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, type_name, lval, result_loc); - } - case BuiltinFnIdPanic: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - IrInstSrc *panic = ir_build_panic_src(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, panic, lval, result_loc); - } - case BuiltinFnIdPtrCast: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); - if (arg1_value == irb->codegen->invalid_inst_src) - return arg1_value; - - IrInstSrc *ptr_cast = ir_build_ptr_cast_src(irb, scope, node, arg0_value, arg1_value, true); - return ir_lval_wrap(irb, scope, ptr_cast, lval, result_loc); - } - case BuiltinFnIdBitCast: - { - AstNode *dest_type_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *dest_type = ir_gen_node(irb, dest_type_node, scope); - if (dest_type == irb->codegen->invalid_inst_src) - return dest_type; - - ResultLocBitCast *result_loc_bit_cast = heap::c_allocator.create(); - result_loc_bit_cast->base.id = ResultLocIdBitCast; - result_loc_bit_cast->base.source_instruction = dest_type; - result_loc_bit_cast->base.allow_write_through_const = result_loc->allow_write_through_const; - ir_ref_instruction(dest_type, irb->current_basic_block); - result_loc_bit_cast->parent = result_loc; - - ir_build_reset_result(irb, scope, node, &result_loc_bit_cast->base); - - AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstSrc *arg1_value = ir_gen_node_extra(irb, arg1_node, scope, LValNone, - &result_loc_bit_cast->base); - if (arg1_value == irb->codegen->invalid_inst_src) - return arg1_value; - - IrInstSrc *bitcast = ir_build_bit_cast_src(irb, scope, arg1_node, arg1_value, result_loc_bit_cast); - return ir_lval_wrap(irb, scope, bitcast, lval, result_loc); - } - case BuiltinFnIdAs: - { - AstNode *dest_type_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *dest_type = ir_gen_node(irb, dest_type_node, scope); - if (dest_type == irb->codegen->invalid_inst_src) - return dest_type; - - ResultLocCast *result_loc_cast = ir_build_cast_result_loc(irb, dest_type, result_loc); - - AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstSrc *arg1_value = ir_gen_node_extra(irb, arg1_node, scope, LValNone, - &result_loc_cast->base); - if (arg1_value == irb->codegen->invalid_inst_src) - return arg1_value; - - IrInstSrc *result = ir_build_implicit_cast(irb, scope, node, arg1_value, result_loc_cast); - return ir_lval_wrap(irb, scope, result, lval, result_loc); - } - case BuiltinFnIdIntToPtr: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); - if (arg1_value == irb->codegen->invalid_inst_src) - return arg1_value; - - IrInstSrc *int_to_ptr = ir_build_int_to_ptr_src(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, int_to_ptr, lval, result_loc); - } - case BuiltinFnIdPtrToInt: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - IrInstSrc *ptr_to_int = ir_build_ptr_to_int_src(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, ptr_to_int, lval, result_loc); - } - case BuiltinFnIdTagName: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - IrInstSrc *tag_name = ir_build_tag_name_src(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, tag_name, lval, result_loc); - } - case BuiltinFnIdTagType: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - IrInstSrc *tag_type = ir_build_tag_type(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, tag_type, lval, result_loc); - } - case BuiltinFnIdFieldParentPtr: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); - if (arg1_value == irb->codegen->invalid_inst_src) - return arg1_value; - - AstNode *arg2_node = node->data.fn_call_expr.params.at(2); - IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope); - if (arg2_value == irb->codegen->invalid_inst_src) - return arg2_value; - - IrInstSrc *field_parent_ptr = ir_build_field_parent_ptr_src(irb, scope, node, - arg0_value, arg1_value, arg2_value); - return ir_lval_wrap(irb, scope, field_parent_ptr, lval, result_loc); - } - case BuiltinFnIdByteOffsetOf: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); - if (arg1_value == irb->codegen->invalid_inst_src) - return arg1_value; - - IrInstSrc *offset_of = ir_build_byte_offset_of(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, offset_of, lval, result_loc); - } - case BuiltinFnIdBitOffsetOf: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); - if (arg1_value == irb->codegen->invalid_inst_src) - return arg1_value; - - IrInstSrc *offset_of = ir_build_bit_offset_of(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, offset_of, lval, result_loc); - } - case BuiltinFnIdCall: { - // Cast the options parameter to the options type - ZigType *options_type = get_builtin_type(irb->codegen, "CallOptions"); - IrInstSrc *options_type_inst = ir_build_const_type(irb, scope, node, options_type); - ResultLocCast *result_loc_cast = ir_build_cast_result_loc(irb, options_type_inst, no_result_loc()); - - AstNode *options_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *options_inner = ir_gen_node_extra(irb, options_node, scope, - LValNone, &result_loc_cast->base); - if (options_inner == irb->codegen->invalid_inst_src) - return options_inner; - IrInstSrc *options = ir_build_implicit_cast(irb, scope, options_node, options_inner, result_loc_cast); - - AstNode *fn_ref_node = node->data.fn_call_expr.params.at(1); - AstNode *args_node = node->data.fn_call_expr.params.at(2); - if (args_node->type == NodeTypeContainerInitExpr) { - if (args_node->data.container_init_expr.kind == ContainerInitKindArray || - args_node->data.container_init_expr.entries.length == 0) - { - return ir_gen_fn_call_with_args(irb, scope, node, - fn_ref_node, CallModifierNone, options, - args_node->data.container_init_expr.entries.items, - args_node->data.container_init_expr.entries.length, - lval, result_loc); - } else { - exec_add_error_node(irb->codegen, irb->exec, args_node, - buf_sprintf("TODO: @call with anon struct literal")); - return irb->codegen->invalid_inst_src; - } - } else { - IrInstSrc *fn_ref = ir_gen_node(irb, fn_ref_node, scope); - if (fn_ref == irb->codegen->invalid_inst_src) - return fn_ref; - - IrInstSrc *args = ir_gen_node(irb, args_node, scope); - if (args == irb->codegen->invalid_inst_src) - return args; - - IrInstSrc *call = ir_build_call_extra(irb, scope, node, options, fn_ref, args, result_loc); - return ir_lval_wrap(irb, scope, call, lval, result_loc); - } - } - case BuiltinFnIdAsyncCall: - return ir_gen_async_call(irb, scope, nullptr, node, lval, result_loc); - case BuiltinFnIdShlExact: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); - if (arg1_value == irb->codegen->invalid_inst_src) - return arg1_value; - - IrInstSrc *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpBitShiftLeftExact, arg0_value, arg1_value, true); - return ir_lval_wrap(irb, scope, bin_op, lval, result_loc); - } - case BuiltinFnIdShrExact: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); - if (arg1_value == irb->codegen->invalid_inst_src) - return arg1_value; - - IrInstSrc *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpBitShiftRightExact, arg0_value, arg1_value, true); - return ir_lval_wrap(irb, scope, bin_op, lval, result_loc); - } - case BuiltinFnIdSetEvalBranchQuota: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - IrInstSrc *set_eval_branch_quota = ir_build_set_eval_branch_quota(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, set_eval_branch_quota, lval, result_loc); - } - case BuiltinFnIdAlignCast: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); - if (arg1_value == irb->codegen->invalid_inst_src) - return arg1_value; - - IrInstSrc *align_cast = ir_build_align_cast_src(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, align_cast, lval, result_loc); - } - case BuiltinFnIdThis: - { - IrInstSrc *this_inst = ir_gen_this(irb, scope, node); - return ir_lval_wrap(irb, scope, this_inst, lval, result_loc); - } - case BuiltinFnIdSetAlignStack: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - IrInstSrc *set_align_stack = ir_build_set_align_stack(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, set_align_stack, lval, result_loc); - } - case BuiltinFnIdExport: - { - // Cast the options parameter to the options type - ZigType *options_type = get_builtin_type(irb->codegen, "ExportOptions"); - IrInstSrc *options_type_inst = ir_build_const_type(irb, scope, node, options_type); - ResultLocCast *result_loc_cast = ir_build_cast_result_loc(irb, options_type_inst, no_result_loc()); - - AstNode *target_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *target_value = ir_gen_node(irb, target_node, scope); - if (target_value == irb->codegen->invalid_inst_src) - return target_value; - - AstNode *options_node = node->data.fn_call_expr.params.at(1); - IrInstSrc *options_value = ir_gen_node_extra(irb, options_node, - scope, LValNone, &result_loc_cast->base); - if (options_value == irb->codegen->invalid_inst_src) - return options_value; - - IrInstSrc *casted_options_value = ir_build_implicit_cast( - irb, scope, options_node, options_value, result_loc_cast); - - IrInstSrc *ir_export = ir_build_export(irb, scope, node, target_value, casted_options_value); - return ir_lval_wrap(irb, scope, ir_export, lval, result_loc); - } - case BuiltinFnIdErrorReturnTrace: - { - IrInstSrc *error_return_trace = ir_build_error_return_trace_src(irb, scope, node, - IrInstErrorReturnTraceNull); - return ir_lval_wrap(irb, scope, error_return_trace, lval, result_loc); - } - case BuiltinFnIdAtomicRmw: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); - if (arg1_value == irb->codegen->invalid_inst_src) - return arg1_value; - - AstNode *arg2_node = node->data.fn_call_expr.params.at(2); - IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope); - if (arg2_value == irb->codegen->invalid_inst_src) - return arg2_value; - - AstNode *arg3_node = node->data.fn_call_expr.params.at(3); - IrInstSrc *arg3_value = ir_gen_node(irb, arg3_node, scope); - if (arg3_value == irb->codegen->invalid_inst_src) - return arg3_value; - - AstNode *arg4_node = node->data.fn_call_expr.params.at(4); - IrInstSrc *arg4_value = ir_gen_node(irb, arg4_node, scope); - if (arg4_value == irb->codegen->invalid_inst_src) - return arg4_value; - - IrInstSrc *inst = ir_build_atomic_rmw_src(irb, scope, node, - arg0_value, arg1_value, arg2_value, arg3_value, arg4_value); - return ir_lval_wrap(irb, scope, inst, lval, result_loc); - } - case BuiltinFnIdAtomicLoad: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); - if (arg1_value == irb->codegen->invalid_inst_src) - return arg1_value; - - AstNode *arg2_node = node->data.fn_call_expr.params.at(2); - IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope); - if (arg2_value == irb->codegen->invalid_inst_src) - return arg2_value; - - IrInstSrc *inst = ir_build_atomic_load_src(irb, scope, node, arg0_value, arg1_value, arg2_value); - return ir_lval_wrap(irb, scope, inst, lval, result_loc); - } - case BuiltinFnIdAtomicStore: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); - if (arg1_value == irb->codegen->invalid_inst_src) - return arg1_value; - - AstNode *arg2_node = node->data.fn_call_expr.params.at(2); - IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope); - if (arg2_value == irb->codegen->invalid_inst_src) - return arg2_value; - - AstNode *arg3_node = node->data.fn_call_expr.params.at(3); - IrInstSrc *arg3_value = ir_gen_node(irb, arg3_node, scope); - if (arg3_value == irb->codegen->invalid_inst_src) - return arg3_value; - - IrInstSrc *inst = ir_build_atomic_store_src(irb, scope, node, arg0_value, arg1_value, - arg2_value, arg3_value); - return ir_lval_wrap(irb, scope, inst, lval, result_loc); - } - case BuiltinFnIdIntToEnum: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); - if (arg1_value == irb->codegen->invalid_inst_src) - return arg1_value; - - IrInstSrc *result = ir_build_int_to_enum_src(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, result, lval, result_loc); - } - case BuiltinFnIdEnumToInt: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - IrInstSrc *result = ir_build_enum_to_int(irb, scope, node, arg0_value); - return ir_lval_wrap(irb, scope, result, lval, result_loc); - } - case BuiltinFnIdCtz: - case BuiltinFnIdPopCount: - case BuiltinFnIdClz: - case BuiltinFnIdBswap: - case BuiltinFnIdBitReverse: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); - if (arg1_value == irb->codegen->invalid_inst_src) - return arg1_value; - - IrInstSrc *result; - switch (builtin_fn->id) { - case BuiltinFnIdCtz: - result = ir_build_ctz(irb, scope, node, arg0_value, arg1_value); - break; - case BuiltinFnIdPopCount: - result = ir_build_pop_count(irb, scope, node, arg0_value, arg1_value); - break; - case BuiltinFnIdClz: - result = ir_build_clz(irb, scope, node, arg0_value, arg1_value); - break; - case BuiltinFnIdBswap: - result = ir_build_bswap(irb, scope, node, arg0_value, arg1_value); - break; - case BuiltinFnIdBitReverse: - result = ir_build_bit_reverse(irb, scope, node, arg0_value, arg1_value); - break; - default: - zig_unreachable(); - } - return ir_lval_wrap(irb, scope, result, lval, result_loc); - } - case BuiltinFnIdHasDecl: - { - AstNode *arg0_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); - if (arg0_value == irb->codegen->invalid_inst_src) - return arg0_value; - - AstNode *arg1_node = node->data.fn_call_expr.params.at(1); - IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); - if (arg1_value == irb->codegen->invalid_inst_src) - return arg1_value; - - IrInstSrc *has_decl = ir_build_has_decl(irb, scope, node, arg0_value, arg1_value); - return ir_lval_wrap(irb, scope, has_decl, lval, result_loc); - } - case BuiltinFnIdUnionInit: - { - AstNode *union_type_node = node->data.fn_call_expr.params.at(0); - IrInstSrc *union_type_inst = ir_gen_node(irb, union_type_node, scope); - if (union_type_inst == irb->codegen->invalid_inst_src) - return union_type_inst; - - AstNode *name_node = node->data.fn_call_expr.params.at(1); - IrInstSrc *name_inst = ir_gen_node(irb, name_node, scope); - if (name_inst == irb->codegen->invalid_inst_src) - return name_inst; - - AstNode *init_node = node->data.fn_call_expr.params.at(2); - - return ir_gen_union_init_expr(irb, scope, node, union_type_inst, name_inst, init_node, - lval, result_loc); - } - case BuiltinFnIdSrc: - { - IrInstSrc *src_inst = ir_build_src(irb, scope, node); - return ir_lval_wrap(irb, scope, src_inst, lval, result_loc); - } - } - zig_unreachable(); -} - -static ScopeNoSuspend *get_scope_nosuspend(Scope *scope) { - while (scope) { - if (scope->id == ScopeIdNoSuspend) - return (ScopeNoSuspend *)scope; - if (scope->id == ScopeIdFnDef) - return nullptr; - - scope = scope->parent; - } - return nullptr; -} - -static IrInstSrc *ir_gen_fn_call(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, - ResultLoc *result_loc) -{ - assert(node->type == NodeTypeFnCallExpr); - - if (node->data.fn_call_expr.modifier == CallModifierBuiltin) - return ir_gen_builtin_fn_call(irb, scope, node, lval, result_loc); - - bool is_nosuspend = get_scope_nosuspend(scope) != nullptr; - CallModifier modifier = node->data.fn_call_expr.modifier; - if (is_nosuspend) { - if (modifier == CallModifierAsync) { - add_node_error(irb->codegen, node, - buf_sprintf("async call in nosuspend scope")); - return irb->codegen->invalid_inst_src; - } - modifier = CallModifierNoSuspend; - } - - AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr; - return ir_gen_fn_call_with_args(irb, scope, node, fn_ref_node, modifier, - nullptr, node->data.fn_call_expr.params.items, node->data.fn_call_expr.params.length, lval, result_loc); -} - -static IrInstSrc *ir_gen_if_bool_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, - ResultLoc *result_loc) -{ - assert(node->type == NodeTypeIfBoolExpr); - - IrInstSrc *condition = ir_gen_node(irb, node->data.if_bool_expr.condition, scope); - if (condition == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - - IrInstSrc *is_comptime; - if (ir_should_inline(irb->exec, scope)) { - is_comptime = ir_build_const_bool(irb, scope, node, true); - } else { - is_comptime = ir_build_test_comptime(irb, scope, node, condition); - } - - AstNode *then_node = node->data.if_bool_expr.then_block; - AstNode *else_node = node->data.if_bool_expr.else_node; - - IrBasicBlockSrc *then_block = ir_create_basic_block(irb, scope, "Then"); - IrBasicBlockSrc *else_block = ir_create_basic_block(irb, scope, "Else"); - IrBasicBlockSrc *endif_block = ir_create_basic_block(irb, scope, "EndIf"); - - IrInstSrc *cond_br_inst = ir_build_cond_br(irb, scope, node, condition, - then_block, else_block, is_comptime); - ResultLocPeerParent *peer_parent = ir_build_binary_result_peers(irb, cond_br_inst, else_block, endif_block, - result_loc, is_comptime); - - ir_set_cursor_at_end_and_append_block(irb, then_block); - - Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime); - IrInstSrc *then_expr_result = ir_gen_node_extra(irb, then_node, subexpr_scope, lval, - &peer_parent->peers.at(0)->base); - if (then_expr_result == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - IrBasicBlockSrc *after_then_block = irb->current_basic_block; - if (!instr_is_unreachable(then_expr_result)) - ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); - - ir_set_cursor_at_end_and_append_block(irb, else_block); - IrInstSrc *else_expr_result; - if (else_node) { - else_expr_result = ir_gen_node_extra(irb, else_node, subexpr_scope, lval, &peer_parent->peers.at(1)->base); - if (else_expr_result == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - } else { - else_expr_result = ir_build_const_void(irb, scope, node); - ir_build_end_expr(irb, scope, node, else_expr_result, &peer_parent->peers.at(1)->base); - } - IrBasicBlockSrc *after_else_block = irb->current_basic_block; - if (!instr_is_unreachable(else_expr_result)) - ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); - - ir_set_cursor_at_end_and_append_block(irb, endif_block); - IrInstSrc **incoming_values = heap::c_allocator.allocate(2); - incoming_values[0] = then_expr_result; - incoming_values[1] = else_expr_result; - IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate(2); - incoming_blocks[0] = after_then_block; - incoming_blocks[1] = after_else_block; - - IrInstSrc *phi = ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values, peer_parent); - return ir_expr_wrap(irb, scope, phi, result_loc); -} - -static IrInstSrc *ir_gen_prefix_op_id_lval(IrBuilderSrc *irb, Scope *scope, AstNode *node, IrUnOp op_id, LVal lval) { - assert(node->type == NodeTypePrefixOpExpr); - AstNode *expr_node = node->data.prefix_op_expr.primary_expr; - - IrInstSrc *value = ir_gen_node_extra(irb, expr_node, scope, lval, nullptr); - if (value == irb->codegen->invalid_inst_src) - return value; - - return ir_build_un_op(irb, scope, node, op_id, value); -} - -static IrInstSrc *ir_gen_prefix_op_id(IrBuilderSrc *irb, Scope *scope, AstNode *node, IrUnOp op_id) { - return ir_gen_prefix_op_id_lval(irb, scope, node, op_id, LValNone); -} - -static IrInstSrc *ir_expr_wrap(IrBuilderSrc *irb, Scope *scope, IrInstSrc *inst, ResultLoc *result_loc) { - if (inst == irb->codegen->invalid_inst_src) return inst; - ir_build_end_expr(irb, scope, inst->base.source_node, inst, result_loc); - return inst; -} - -static IrInstSrc *ir_lval_wrap(IrBuilderSrc *irb, Scope *scope, IrInstSrc *value, LVal lval, - ResultLoc *result_loc) -{ - // This logic must be kept in sync with - // [STMT_EXPR_TEST_THING] <--- (search this token) - if (value == irb->codegen->invalid_inst_src || - instr_is_unreachable(value) || - value->base.source_node->type == NodeTypeDefer || - value->id == IrInstSrcIdDeclVar) - { - return value; - } - - assert(lval != LValAssign); - if (lval == LValPtr) { - // We needed a pointer to a value, but we got a value. So we create - // an instruction which just makes a pointer of it. - return ir_build_ref_src(irb, scope, value->base.source_node, value); - } else if (result_loc != nullptr) { - return ir_expr_wrap(irb, scope, value, result_loc); - } else { - return value; - } - -} - -static PtrLen star_token_to_ptr_len(TokenId token_id) { - switch (token_id) { - case TokenIdStar: - case TokenIdStarStar: - return PtrLenSingle; - case TokenIdLBracket: - return PtrLenUnknown; - case TokenIdSymbol: - return PtrLenC; - default: - zig_unreachable(); - } -} - -static IrInstSrc *ir_gen_pointer_type(IrBuilderSrc *irb, Scope *scope, AstNode *node) { - assert(node->type == NodeTypePointerType); - - PtrLen ptr_len = star_token_to_ptr_len(node->data.pointer_type.star_token->id); - - bool is_const = node->data.pointer_type.is_const; - bool is_volatile = node->data.pointer_type.is_volatile; - bool is_allow_zero = node->data.pointer_type.allow_zero_token != nullptr; - AstNode *sentinel_expr = node->data.pointer_type.sentinel; - AstNode *expr_node = node->data.pointer_type.op_expr; - AstNode *align_expr = node->data.pointer_type.align_expr; - - IrInstSrc *sentinel; - if (sentinel_expr != nullptr) { - sentinel = ir_gen_node(irb, sentinel_expr, scope); - if (sentinel == irb->codegen->invalid_inst_src) - return sentinel; - } else { - sentinel = nullptr; - } - - IrInstSrc *align_value; - if (align_expr != nullptr) { - align_value = ir_gen_node(irb, align_expr, scope); - if (align_value == irb->codegen->invalid_inst_src) - return align_value; - } else { - align_value = nullptr; - } - - IrInstSrc *child_type = ir_gen_node(irb, expr_node, scope); - if (child_type == irb->codegen->invalid_inst_src) - return child_type; - - uint32_t bit_offset_start = 0; - if (node->data.pointer_type.bit_offset_start != nullptr) { - if (!bigint_fits_in_bits(node->data.pointer_type.bit_offset_start, 32, false)) { - Buf *val_buf = buf_alloc(); - bigint_append_buf(val_buf, node->data.pointer_type.bit_offset_start, 10); - exec_add_error_node(irb->codegen, irb->exec, node, - buf_sprintf("value %s too large for u32 bit offset", buf_ptr(val_buf))); - return irb->codegen->invalid_inst_src; - } - bit_offset_start = bigint_as_u32(node->data.pointer_type.bit_offset_start); - } - - uint32_t host_int_bytes = 0; - if (node->data.pointer_type.host_int_bytes != nullptr) { - if (!bigint_fits_in_bits(node->data.pointer_type.host_int_bytes, 32, false)) { - Buf *val_buf = buf_alloc(); - bigint_append_buf(val_buf, node->data.pointer_type.host_int_bytes, 10); - exec_add_error_node(irb->codegen, irb->exec, node, - buf_sprintf("value %s too large for u32 byte count", buf_ptr(val_buf))); - return irb->codegen->invalid_inst_src; - } - host_int_bytes = bigint_as_u32(node->data.pointer_type.host_int_bytes); - } - - if (host_int_bytes != 0 && bit_offset_start >= host_int_bytes * 8) { - exec_add_error_node(irb->codegen, irb->exec, node, - buf_sprintf("bit offset starts after end of host integer")); - return irb->codegen->invalid_inst_src; - } - - return ir_build_ptr_type(irb, scope, node, child_type, is_const, is_volatile, - ptr_len, sentinel, align_value, bit_offset_start, host_int_bytes, is_allow_zero); -} - -static IrInstSrc *ir_gen_catch_unreachable(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - AstNode *expr_node, LVal lval, ResultLoc *result_loc) -{ - IrInstSrc *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr, nullptr); - if (err_union_ptr == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - - IrInstSrc *payload_ptr = ir_build_unwrap_err_payload_src(irb, scope, source_node, err_union_ptr, true, false); - if (payload_ptr == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - - if (lval == LValPtr) - return payload_ptr; - - IrInstSrc *load_ptr = ir_build_load_ptr(irb, scope, source_node, payload_ptr); - return ir_expr_wrap(irb, scope, load_ptr, result_loc); -} - -static IrInstSrc *ir_gen_bool_not(IrBuilderSrc *irb, Scope *scope, AstNode *node) { - assert(node->type == NodeTypePrefixOpExpr); - AstNode *expr_node = node->data.prefix_op_expr.primary_expr; - - IrInstSrc *value = ir_gen_node(irb, expr_node, scope); - if (value == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - - return ir_build_bool_not(irb, scope, node, value); -} - -static IrInstSrc *ir_gen_prefix_op_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, - ResultLoc *result_loc) -{ - assert(node->type == NodeTypePrefixOpExpr); - - PrefixOp prefix_op = node->data.prefix_op_expr.prefix_op; - - switch (prefix_op) { - case PrefixOpInvalid: - zig_unreachable(); - case PrefixOpBoolNot: - return ir_lval_wrap(irb, scope, ir_gen_bool_not(irb, scope, node), lval, result_loc); - case PrefixOpBinNot: - return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpBinNot), lval, result_loc); - case PrefixOpNegation: - return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegation), lval, result_loc); - case PrefixOpNegationWrap: - return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegationWrap), lval, result_loc); - case PrefixOpOptional: - return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpOptional), lval, result_loc); - case PrefixOpAddrOf: { - AstNode *expr_node = node->data.prefix_op_expr.primary_expr; - return ir_lval_wrap(irb, scope, ir_gen_node_extra(irb, expr_node, scope, LValPtr, nullptr), lval, result_loc); - } - } - zig_unreachable(); -} - -static IrInstSrc *ir_gen_union_init_expr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, - IrInstSrc *union_type, IrInstSrc *field_name, AstNode *expr_node, - LVal lval, ResultLoc *parent_result_loc) -{ - IrInstSrc *container_ptr = ir_build_resolve_result(irb, scope, source_node, parent_result_loc, union_type); - IrInstSrc *field_ptr = ir_build_field_ptr_instruction(irb, scope, source_node, container_ptr, - field_name, true); - - ResultLocInstruction *result_loc_inst = heap::c_allocator.create(); - result_loc_inst->base.id = ResultLocIdInstruction; - result_loc_inst->base.source_instruction = field_ptr; - ir_ref_instruction(field_ptr, irb->current_basic_block); - ir_build_reset_result(irb, scope, expr_node, &result_loc_inst->base); - - IrInstSrc *expr_value = ir_gen_node_extra(irb, expr_node, scope, LValNone, - &result_loc_inst->base); - if (expr_value == irb->codegen->invalid_inst_src) - return expr_value; - - IrInstSrc *init_union = ir_build_union_init_named_field(irb, scope, source_node, union_type, - field_name, field_ptr, container_ptr); - - return ir_lval_wrap(irb, scope, init_union, lval, parent_result_loc); -} - -static IrInstSrc *ir_gen_container_init_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, - ResultLoc *parent_result_loc) -{ - assert(node->type == NodeTypeContainerInitExpr); - - AstNodeContainerInitExpr *container_init_expr = &node->data.container_init_expr; - ContainerInitKind kind = container_init_expr->kind; - - ResultLocCast *result_loc_cast = nullptr; - ResultLoc *child_result_loc; - AstNode *init_array_type_source_node; - if (container_init_expr->type != nullptr) { - IrInstSrc *container_type; - if (container_init_expr->type->type == NodeTypeInferredArrayType) { - if (kind == ContainerInitKindStruct) { - add_node_error(irb->codegen, container_init_expr->type, - buf_sprintf("initializing array with struct syntax")); - return irb->codegen->invalid_inst_src; - } - IrInstSrc *sentinel; - if (container_init_expr->type->data.inferred_array_type.sentinel != nullptr) { - sentinel = ir_gen_node(irb, container_init_expr->type->data.inferred_array_type.sentinel, scope); - if (sentinel == irb->codegen->invalid_inst_src) - return sentinel; - } else { - sentinel = nullptr; - } - - IrInstSrc *elem_type = ir_gen_node(irb, - container_init_expr->type->data.inferred_array_type.child_type, scope); - if (elem_type == irb->codegen->invalid_inst_src) - return elem_type; - size_t item_count = container_init_expr->entries.length; - IrInstSrc *item_count_inst = ir_build_const_usize(irb, scope, node, item_count); - container_type = ir_build_array_type(irb, scope, node, item_count_inst, sentinel, elem_type); - } else { - container_type = ir_gen_node(irb, container_init_expr->type, scope); - if (container_type == irb->codegen->invalid_inst_src) - return container_type; - } - - result_loc_cast = ir_build_cast_result_loc(irb, container_type, parent_result_loc); - child_result_loc = &result_loc_cast->base; - init_array_type_source_node = container_type->base.source_node; - } else { - child_result_loc = parent_result_loc; - if (parent_result_loc->source_instruction != nullptr) { - init_array_type_source_node = parent_result_loc->source_instruction->base.source_node; - } else { - init_array_type_source_node = node; - } - } - - switch (kind) { - case ContainerInitKindStruct: { - IrInstSrc *container_ptr = ir_build_resolve_result(irb, scope, node, child_result_loc, - nullptr); - - size_t field_count = container_init_expr->entries.length; - IrInstSrcContainerInitFieldsField *fields = heap::c_allocator.allocate(field_count); - for (size_t i = 0; i < field_count; i += 1) { - AstNode *entry_node = container_init_expr->entries.at(i); - assert(entry_node->type == NodeTypeStructValueField); - - Buf *name = entry_node->data.struct_val_field.name; - AstNode *expr_node = entry_node->data.struct_val_field.expr; - - IrInstSrc *field_ptr = ir_build_field_ptr(irb, scope, entry_node, container_ptr, name, true); - ResultLocInstruction *result_loc_inst = heap::c_allocator.create(); - result_loc_inst->base.id = ResultLocIdInstruction; - result_loc_inst->base.source_instruction = field_ptr; - result_loc_inst->base.allow_write_through_const = true; - ir_ref_instruction(field_ptr, irb->current_basic_block); - ir_build_reset_result(irb, scope, expr_node, &result_loc_inst->base); - - IrInstSrc *expr_value = ir_gen_node_extra(irb, expr_node, scope, LValNone, - &result_loc_inst->base); - if (expr_value == irb->codegen->invalid_inst_src) - return expr_value; - - fields[i].name = name; - fields[i].source_node = entry_node; - fields[i].result_loc = field_ptr; - } - IrInstSrc *result = ir_build_container_init_fields(irb, scope, node, field_count, - fields, container_ptr); - - if (result_loc_cast != nullptr) { - result = ir_build_implicit_cast(irb, scope, node, result, result_loc_cast); - } - return ir_lval_wrap(irb, scope, result, lval, parent_result_loc); - } - case ContainerInitKindArray: { - size_t item_count = container_init_expr->entries.length; - - IrInstSrc *container_ptr = ir_build_resolve_result(irb, scope, node, child_result_loc, - nullptr); - - IrInstSrc **result_locs = heap::c_allocator.allocate(item_count); - for (size_t i = 0; i < item_count; i += 1) { - AstNode *expr_node = container_init_expr->entries.at(i); - - IrInstSrc *elem_index = ir_build_const_usize(irb, scope, expr_node, i); - IrInstSrc *elem_ptr = ir_build_elem_ptr(irb, scope, expr_node, container_ptr, - elem_index, false, PtrLenSingle, init_array_type_source_node); - ResultLocInstruction *result_loc_inst = heap::c_allocator.create(); - result_loc_inst->base.id = ResultLocIdInstruction; - result_loc_inst->base.source_instruction = elem_ptr; - result_loc_inst->base.allow_write_through_const = true; - ir_ref_instruction(elem_ptr, irb->current_basic_block); - ir_build_reset_result(irb, scope, expr_node, &result_loc_inst->base); - - IrInstSrc *expr_value = ir_gen_node_extra(irb, expr_node, scope, LValNone, - &result_loc_inst->base); - if (expr_value == irb->codegen->invalid_inst_src) - return expr_value; - - result_locs[i] = elem_ptr; - } - IrInstSrc *result = ir_build_container_init_list(irb, scope, node, item_count, - result_locs, container_ptr, init_array_type_source_node); - if (result_loc_cast != nullptr) { - result = ir_build_implicit_cast(irb, scope, node, result, result_loc_cast); - } - return ir_lval_wrap(irb, scope, result, lval, parent_result_loc); - } - } - zig_unreachable(); -} - -static ResultLocVar *ir_build_var_result_loc(IrBuilderSrc *irb, IrInstSrc *alloca, ZigVar *var) { - ResultLocVar *result_loc_var = heap::c_allocator.create(); - result_loc_var->base.id = ResultLocIdVar; - result_loc_var->base.source_instruction = alloca; - result_loc_var->base.allow_write_through_const = true; - result_loc_var->var = var; - - ir_build_reset_result(irb, alloca->base.scope, alloca->base.source_node, &result_loc_var->base); - - return result_loc_var; -} - -static ResultLocCast *ir_build_cast_result_loc(IrBuilderSrc *irb, IrInstSrc *dest_type, - ResultLoc *parent_result_loc) -{ - ResultLocCast *result_loc_cast = heap::c_allocator.create(); - result_loc_cast->base.id = ResultLocIdCast; - result_loc_cast->base.source_instruction = dest_type; - result_loc_cast->base.allow_write_through_const = parent_result_loc->allow_write_through_const; - ir_ref_instruction(dest_type, irb->current_basic_block); - result_loc_cast->parent = parent_result_loc; - - ir_build_reset_result(irb, dest_type->base.scope, dest_type->base.source_node, &result_loc_cast->base); - - return result_loc_cast; -} - -static void build_decl_var_and_init(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, ZigVar *var, - IrInstSrc *init, const char *name_hint, IrInstSrc *is_comptime) -{ - IrInstSrc *alloca = ir_build_alloca_src(irb, scope, source_node, nullptr, name_hint, is_comptime); - ResultLocVar *var_result_loc = ir_build_var_result_loc(irb, alloca, var); - ir_build_end_expr(irb, scope, source_node, init, &var_result_loc->base); - ir_build_var_decl_src(irb, scope, source_node, var, nullptr, alloca); -} - -static IrInstSrc *ir_gen_var_decl(IrBuilderSrc *irb, Scope *scope, AstNode *node) { - assert(node->type == NodeTypeVariableDeclaration); - - AstNodeVariableDeclaration *variable_declaration = &node->data.variable_declaration; - - if (buf_eql_str(variable_declaration->symbol, "_")) { - add_node_error(irb->codegen, node, buf_sprintf("`_` is not a declarable symbol")); - return irb->codegen->invalid_inst_src; - } - - // Used for the type expr and the align expr - Scope *comptime_scope = create_comptime_scope(irb->codegen, node, scope); - - IrInstSrc *type_instruction; - if (variable_declaration->type != nullptr) { - type_instruction = ir_gen_node(irb, variable_declaration->type, comptime_scope); - if (type_instruction == irb->codegen->invalid_inst_src) - return type_instruction; - } else { - type_instruction = nullptr; - } - - bool is_shadowable = false; - bool is_const = variable_declaration->is_const; - bool is_extern = variable_declaration->is_extern; - - bool is_comptime_scalar = ir_should_inline(irb->exec, scope) || variable_declaration->is_comptime; - IrInstSrc *is_comptime = ir_build_const_bool(irb, scope, node, is_comptime_scalar); - ZigVar *var = ir_create_var(irb, node, scope, variable_declaration->symbol, - is_const, is_const, is_shadowable, is_comptime); - // we detect IrInstSrcDeclVar in gen_block to make sure the next node - // is inside var->child_scope - - if (!is_extern && !variable_declaration->expr) { - var->var_type = irb->codegen->builtin_types.entry_invalid; - add_node_error(irb->codegen, node, buf_sprintf("variables must be initialized")); - return irb->codegen->invalid_inst_src; - } - - IrInstSrc *align_value = nullptr; - if (variable_declaration->align_expr != nullptr) { - align_value = ir_gen_node(irb, variable_declaration->align_expr, comptime_scope); - if (align_value == irb->codegen->invalid_inst_src) - return align_value; - } - - if (variable_declaration->section_expr != nullptr) { - add_node_error(irb->codegen, variable_declaration->section_expr, - buf_sprintf("cannot set section of local variable '%s'", buf_ptr(variable_declaration->symbol))); - } - - // Parser should ensure that this never happens - assert(variable_declaration->threadlocal_tok == nullptr); - - IrInstSrc *alloca = ir_build_alloca_src(irb, scope, node, align_value, - buf_ptr(variable_declaration->symbol), is_comptime); - - // Create a result location for the initialization expression. - ResultLocVar *result_loc_var = ir_build_var_result_loc(irb, alloca, var); - ResultLoc *init_result_loc; - ResultLocCast *result_loc_cast; - if (type_instruction != nullptr) { - result_loc_cast = ir_build_cast_result_loc(irb, type_instruction, &result_loc_var->base); - init_result_loc = &result_loc_cast->base; - } else { - result_loc_cast = nullptr; - init_result_loc = &result_loc_var->base; - } - - Scope *init_scope = is_comptime_scalar ? - create_comptime_scope(irb->codegen, variable_declaration->expr, scope) : scope; - - // Temporarily set the name of the IrExecutableSrc to the VariableDeclaration - // so that the struct or enum from the init expression inherits the name. - Buf *old_exec_name = irb->exec->name; - irb->exec->name = variable_declaration->symbol; - IrInstSrc *init_value = ir_gen_node_extra(irb, variable_declaration->expr, init_scope, - LValNone, init_result_loc); - irb->exec->name = old_exec_name; - - if (init_value == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - - if (result_loc_cast != nullptr) { - IrInstSrc *implicit_cast = ir_build_implicit_cast(irb, scope, init_value->base.source_node, - init_value, result_loc_cast); - ir_build_end_expr(irb, scope, node, implicit_cast, &result_loc_var->base); - } - - return ir_build_var_decl_src(irb, scope, node, var, align_value, alloca); -} - -static IrInstSrc *ir_gen_while_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, - ResultLoc *result_loc) -{ - assert(node->type == NodeTypeWhileExpr); - - AstNode *continue_expr_node = node->data.while_expr.continue_expr; - AstNode *else_node = node->data.while_expr.else_node; - - IrBasicBlockSrc *cond_block = ir_create_basic_block(irb, scope, "WhileCond"); - IrBasicBlockSrc *body_block = ir_create_basic_block(irb, scope, "WhileBody"); - IrBasicBlockSrc *continue_block = continue_expr_node ? - ir_create_basic_block(irb, scope, "WhileContinue") : cond_block; - IrBasicBlockSrc *end_block = ir_create_basic_block(irb, scope, "WhileEnd"); - IrBasicBlockSrc *else_block = else_node ? - ir_create_basic_block(irb, scope, "WhileElse") : end_block; - - IrInstSrc *is_comptime = ir_build_const_bool(irb, scope, node, - ir_should_inline(irb->exec, scope) || node->data.while_expr.is_inline); - ir_build_br(irb, scope, node, cond_block, is_comptime); - - Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime); - Buf *var_symbol = node->data.while_expr.var_symbol; - Buf *err_symbol = node->data.while_expr.err_symbol; - if (err_symbol != nullptr) { - ir_set_cursor_at_end_and_append_block(irb, cond_block); - - Scope *payload_scope; - AstNode *symbol_node = node; // TODO make more accurate - ZigVar *payload_var; - if (var_symbol) { - // TODO make it an error to write to payload variable - payload_var = ir_create_var(irb, symbol_node, subexpr_scope, var_symbol, - true, false, false, is_comptime); - payload_scope = payload_var->child_scope; - } else { - payload_scope = subexpr_scope; - } - ScopeExpr *spill_scope = create_expr_scope(irb->codegen, node, payload_scope); - IrInstSrc *err_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, subexpr_scope, - LValPtr, nullptr); - if (err_val_ptr == irb->codegen->invalid_inst_src) - return err_val_ptr; - IrInstSrc *is_err = ir_build_test_err_src(irb, scope, node->data.while_expr.condition, err_val_ptr, - true, false); - IrBasicBlockSrc *after_cond_block = irb->current_basic_block; - IrInstSrc *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node)); - IrInstSrc *cond_br_inst; - if (!instr_is_unreachable(is_err)) { - cond_br_inst = ir_build_cond_br(irb, scope, node->data.while_expr.condition, is_err, - else_block, body_block, is_comptime); - cond_br_inst->is_gen = true; - } else { - // for the purposes of the source instruction to ir_build_result_peers - cond_br_inst = irb->current_basic_block->instruction_list.last(); - } - - ResultLocPeerParent *peer_parent = ir_build_result_peers(irb, cond_br_inst, end_block, result_loc, - is_comptime); - - ir_set_cursor_at_end_and_append_block(irb, body_block); - if (var_symbol) { - IrInstSrc *payload_ptr = ir_build_unwrap_err_payload_src(irb, &spill_scope->base, symbol_node, - err_val_ptr, false, false); - IrInstSrc *var_value = node->data.while_expr.var_is_ptr ? - payload_ptr : ir_build_load_ptr(irb, &spill_scope->base, symbol_node, payload_ptr); - build_decl_var_and_init(irb, payload_scope, symbol_node, payload_var, var_value, buf_ptr(var_symbol), is_comptime); - } - - ZigList incoming_values = {0}; - ZigList incoming_blocks = {0}; - - if (is_duplicate_label(irb->codegen, payload_scope, node, node->data.while_expr.name)) - return irb->codegen->invalid_inst_src; - - ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, payload_scope); - loop_scope->break_block = end_block; - loop_scope->continue_block = continue_block; - loop_scope->is_comptime = is_comptime; - loop_scope->incoming_blocks = &incoming_blocks; - loop_scope->incoming_values = &incoming_values; - loop_scope->lval = lval; - loop_scope->peer_parent = peer_parent; - loop_scope->spill_scope = spill_scope; - - // Note the body block of the loop is not the place that lval and result_loc are used - - // it's actually in break statements, handled similarly to return statements. - // That is why we set those values in loop_scope above and not in this ir_gen_node call. - IrInstSrc *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base); - if (body_result == irb->codegen->invalid_inst_src) - return body_result; - - if (loop_scope->name != nullptr && loop_scope->name_used == false) { - add_node_error(irb->codegen, node, buf_sprintf("unused while label")); - } - - if (!instr_is_unreachable(body_result)) { - ir_mark_gen(ir_build_check_statement_is_void(irb, payload_scope, node->data.while_expr.body, body_result)); - ir_mark_gen(ir_build_br(irb, payload_scope, node, continue_block, is_comptime)); - } - - if (continue_expr_node) { - ir_set_cursor_at_end_and_append_block(irb, continue_block); - IrInstSrc *expr_result = ir_gen_node(irb, continue_expr_node, payload_scope); - if (expr_result == irb->codegen->invalid_inst_src) - return expr_result; - if (!instr_is_unreachable(expr_result)) { - ir_mark_gen(ir_build_check_statement_is_void(irb, payload_scope, continue_expr_node, expr_result)); - ir_mark_gen(ir_build_br(irb, payload_scope, node, cond_block, is_comptime)); - } - } - - ir_set_cursor_at_end_and_append_block(irb, else_block); - assert(else_node != nullptr); - - // TODO make it an error to write to error variable - AstNode *err_symbol_node = else_node; // TODO make more accurate - ZigVar *err_var = ir_create_var(irb, err_symbol_node, scope, err_symbol, - true, false, false, is_comptime); - Scope *err_scope = err_var->child_scope; - IrInstSrc *err_ptr = ir_build_unwrap_err_code_src(irb, err_scope, err_symbol_node, err_val_ptr); - IrInstSrc *err_value = ir_build_load_ptr(irb, err_scope, err_symbol_node, err_ptr); - build_decl_var_and_init(irb, err_scope, err_symbol_node, err_var, err_value, buf_ptr(err_symbol), is_comptime); - - if (peer_parent->peers.length != 0) { - peer_parent->peers.last()->next_bb = else_block; - } - ResultLocPeer *peer_result = create_peer_result(peer_parent); - peer_parent->peers.append(peer_result); - IrInstSrc *else_result = ir_gen_node_extra(irb, else_node, err_scope, lval, &peer_result->base); - if (else_result == irb->codegen->invalid_inst_src) - return else_result; - if (!instr_is_unreachable(else_result)) - ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime)); - IrBasicBlockSrc *after_else_block = irb->current_basic_block; - ir_set_cursor_at_end_and_append_block(irb, end_block); - if (else_result) { - incoming_blocks.append(after_else_block); - incoming_values.append(else_result); - } else { - incoming_blocks.append(after_cond_block); - incoming_values.append(void_else_result); - } - if (peer_parent->peers.length != 0) { - peer_parent->peers.last()->next_bb = end_block; - } - - IrInstSrc *phi = ir_build_phi(irb, scope, node, incoming_blocks.length, - incoming_blocks.items, incoming_values.items, peer_parent); - return ir_expr_wrap(irb, scope, phi, result_loc); - } else if (var_symbol != nullptr) { - ir_set_cursor_at_end_and_append_block(irb, cond_block); - Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime); - // TODO make it an error to write to payload variable - AstNode *symbol_node = node; // TODO make more accurate - - ZigVar *payload_var = ir_create_var(irb, symbol_node, subexpr_scope, var_symbol, - true, false, false, is_comptime); - Scope *child_scope = payload_var->child_scope; - ScopeExpr *spill_scope = create_expr_scope(irb->codegen, node, child_scope); - IrInstSrc *maybe_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, subexpr_scope, - LValPtr, nullptr); - if (maybe_val_ptr == irb->codegen->invalid_inst_src) - return maybe_val_ptr; - IrInstSrc *maybe_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, maybe_val_ptr); - IrInstSrc *is_non_null = ir_build_test_non_null_src(irb, scope, node->data.while_expr.condition, maybe_val); - IrBasicBlockSrc *after_cond_block = irb->current_basic_block; - IrInstSrc *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node)); - IrInstSrc *cond_br_inst; - if (!instr_is_unreachable(is_non_null)) { - cond_br_inst = ir_build_cond_br(irb, scope, node->data.while_expr.condition, is_non_null, - body_block, else_block, is_comptime); - cond_br_inst->is_gen = true; - } else { - // for the purposes of the source instruction to ir_build_result_peers - cond_br_inst = irb->current_basic_block->instruction_list.last(); - } - - ResultLocPeerParent *peer_parent = ir_build_result_peers(irb, cond_br_inst, end_block, result_loc, - is_comptime); - - ir_set_cursor_at_end_and_append_block(irb, body_block); - IrInstSrc *payload_ptr = ir_build_optional_unwrap_ptr(irb, &spill_scope->base, symbol_node, maybe_val_ptr, false); - IrInstSrc *var_value = node->data.while_expr.var_is_ptr ? - payload_ptr : ir_build_load_ptr(irb, &spill_scope->base, symbol_node, payload_ptr); - build_decl_var_and_init(irb, child_scope, symbol_node, payload_var, var_value, buf_ptr(var_symbol), is_comptime); - - ZigList incoming_values = {0}; - ZigList incoming_blocks = {0}; - - if (is_duplicate_label(irb->codegen, child_scope, node, node->data.while_expr.name)) - return irb->codegen->invalid_inst_src; - - ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, child_scope); - loop_scope->break_block = end_block; - loop_scope->continue_block = continue_block; - loop_scope->is_comptime = is_comptime; - loop_scope->incoming_blocks = &incoming_blocks; - loop_scope->incoming_values = &incoming_values; - loop_scope->lval = lval; - loop_scope->peer_parent = peer_parent; - loop_scope->spill_scope = spill_scope; - - // Note the body block of the loop is not the place that lval and result_loc are used - - // it's actually in break statements, handled similarly to return statements. - // That is why we set those values in loop_scope above and not in this ir_gen_node call. - IrInstSrc *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base); - if (body_result == irb->codegen->invalid_inst_src) - return body_result; - - if (loop_scope->name != nullptr && loop_scope->name_used == false) { - add_node_error(irb->codegen, node, buf_sprintf("unused while label")); - } - - if (!instr_is_unreachable(body_result)) { - ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, node->data.while_expr.body, body_result)); - ir_mark_gen(ir_build_br(irb, child_scope, node, continue_block, is_comptime)); - } - - if (continue_expr_node) { - ir_set_cursor_at_end_and_append_block(irb, continue_block); - IrInstSrc *expr_result = ir_gen_node(irb, continue_expr_node, child_scope); - if (expr_result == irb->codegen->invalid_inst_src) - return expr_result; - if (!instr_is_unreachable(expr_result)) { - ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, continue_expr_node, expr_result)); - ir_mark_gen(ir_build_br(irb, child_scope, node, cond_block, is_comptime)); - } - } - - IrInstSrc *else_result = nullptr; - if (else_node) { - ir_set_cursor_at_end_and_append_block(irb, else_block); - - if (peer_parent->peers.length != 0) { - peer_parent->peers.last()->next_bb = else_block; - } - ResultLocPeer *peer_result = create_peer_result(peer_parent); - peer_parent->peers.append(peer_result); - else_result = ir_gen_node_extra(irb, else_node, scope, lval, &peer_result->base); - if (else_result == irb->codegen->invalid_inst_src) - return else_result; - if (!instr_is_unreachable(else_result)) - ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime)); - } - IrBasicBlockSrc *after_else_block = irb->current_basic_block; - ir_set_cursor_at_end_and_append_block(irb, end_block); - if (else_result) { - incoming_blocks.append(after_else_block); - incoming_values.append(else_result); - } else { - incoming_blocks.append(after_cond_block); - incoming_values.append(void_else_result); - } - if (peer_parent->peers.length != 0) { - peer_parent->peers.last()->next_bb = end_block; - } - - IrInstSrc *phi = ir_build_phi(irb, scope, node, incoming_blocks.length, - incoming_blocks.items, incoming_values.items, peer_parent); - return ir_expr_wrap(irb, scope, phi, result_loc); - } else { - ir_set_cursor_at_end_and_append_block(irb, cond_block); - IrInstSrc *cond_val = ir_gen_node(irb, node->data.while_expr.condition, scope); - if (cond_val == irb->codegen->invalid_inst_src) - return cond_val; - IrBasicBlockSrc *after_cond_block = irb->current_basic_block; - IrInstSrc *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node)); - IrInstSrc *cond_br_inst; - if (!instr_is_unreachable(cond_val)) { - cond_br_inst = ir_build_cond_br(irb, scope, node->data.while_expr.condition, cond_val, - body_block, else_block, is_comptime); - cond_br_inst->is_gen = true; - } else { - // for the purposes of the source instruction to ir_build_result_peers - cond_br_inst = irb->current_basic_block->instruction_list.last(); - } - - ResultLocPeerParent *peer_parent = ir_build_result_peers(irb, cond_br_inst, end_block, result_loc, - is_comptime); - ir_set_cursor_at_end_and_append_block(irb, body_block); - - ZigList incoming_values = {0}; - ZigList incoming_blocks = {0}; - - Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime); - - if (is_duplicate_label(irb->codegen, subexpr_scope, node, node->data.while_expr.name)) - return irb->codegen->invalid_inst_src; - - ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, subexpr_scope); - loop_scope->break_block = end_block; - loop_scope->continue_block = continue_block; - loop_scope->is_comptime = is_comptime; - loop_scope->incoming_blocks = &incoming_blocks; - loop_scope->incoming_values = &incoming_values; - loop_scope->lval = lval; - loop_scope->peer_parent = peer_parent; - - // Note the body block of the loop is not the place that lval and result_loc are used - - // it's actually in break statements, handled similarly to return statements. - // That is why we set those values in loop_scope above and not in this ir_gen_node call. - IrInstSrc *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base); - if (body_result == irb->codegen->invalid_inst_src) - return body_result; - - if (loop_scope->name != nullptr && loop_scope->name_used == false) { - add_node_error(irb->codegen, node, buf_sprintf("unused while label")); - } - - if (!instr_is_unreachable(body_result)) { - ir_mark_gen(ir_build_check_statement_is_void(irb, scope, node->data.while_expr.body, body_result)); - ir_mark_gen(ir_build_br(irb, scope, node, continue_block, is_comptime)); - } - - if (continue_expr_node) { - ir_set_cursor_at_end_and_append_block(irb, continue_block); - IrInstSrc *expr_result = ir_gen_node(irb, continue_expr_node, subexpr_scope); - if (expr_result == irb->codegen->invalid_inst_src) - return expr_result; - if (!instr_is_unreachable(expr_result)) { - ir_mark_gen(ir_build_check_statement_is_void(irb, scope, continue_expr_node, expr_result)); - ir_mark_gen(ir_build_br(irb, scope, node, cond_block, is_comptime)); - } - } - - IrInstSrc *else_result = nullptr; - if (else_node) { - ir_set_cursor_at_end_and_append_block(irb, else_block); - - if (peer_parent->peers.length != 0) { - peer_parent->peers.last()->next_bb = else_block; - } - ResultLocPeer *peer_result = create_peer_result(peer_parent); - peer_parent->peers.append(peer_result); - - else_result = ir_gen_node_extra(irb, else_node, subexpr_scope, lval, &peer_result->base); - if (else_result == irb->codegen->invalid_inst_src) - return else_result; - if (!instr_is_unreachable(else_result)) - ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime)); - } - IrBasicBlockSrc *after_else_block = irb->current_basic_block; - ir_set_cursor_at_end_and_append_block(irb, end_block); - if (else_result) { - incoming_blocks.append(after_else_block); - incoming_values.append(else_result); - } else { - incoming_blocks.append(after_cond_block); - incoming_values.append(void_else_result); - } - if (peer_parent->peers.length != 0) { - peer_parent->peers.last()->next_bb = end_block; - } - - IrInstSrc *phi = ir_build_phi(irb, scope, node, incoming_blocks.length, - incoming_blocks.items, incoming_values.items, peer_parent); - return ir_expr_wrap(irb, scope, phi, result_loc); - } -} - -static IrInstSrc *ir_gen_for_expr(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node, LVal lval, - ResultLoc *result_loc) -{ - assert(node->type == NodeTypeForExpr); - - AstNode *array_node = node->data.for_expr.array_expr; - AstNode *elem_node = node->data.for_expr.elem_node; - AstNode *index_node = node->data.for_expr.index_node; - AstNode *body_node = node->data.for_expr.body; - AstNode *else_node = node->data.for_expr.else_node; - - if (!elem_node) { - add_node_error(irb->codegen, node, buf_sprintf("for loop expression missing element parameter")); - return irb->codegen->invalid_inst_src; - } - assert(elem_node->type == NodeTypeSymbol); - - ScopeExpr *spill_scope = create_expr_scope(irb->codegen, node, parent_scope); - - IrInstSrc *array_val_ptr = ir_gen_node_extra(irb, array_node, &spill_scope->base, LValPtr, nullptr); - if (array_val_ptr == irb->codegen->invalid_inst_src) - return array_val_ptr; - - IrInstSrc *is_comptime = ir_build_const_bool(irb, parent_scope, node, - ir_should_inline(irb->exec, parent_scope) || node->data.for_expr.is_inline); - - AstNode *index_var_source_node; - ZigVar *index_var; - const char *index_var_name; - if (index_node) { - index_var_source_node = index_node; - Buf *index_var_name_buf = index_node->data.symbol_expr.symbol; - index_var = ir_create_var(irb, index_node, parent_scope, index_var_name_buf, true, false, false, is_comptime); - index_var_name = buf_ptr(index_var_name_buf); - } else { - index_var_source_node = node; - index_var = ir_create_var(irb, node, parent_scope, nullptr, true, false, true, is_comptime); - index_var_name = "i"; - } - - IrInstSrc *zero = ir_build_const_usize(irb, parent_scope, node, 0); - build_decl_var_and_init(irb, parent_scope, index_var_source_node, index_var, zero, index_var_name, is_comptime); - parent_scope = index_var->child_scope; - - IrInstSrc *one = ir_build_const_usize(irb, parent_scope, node, 1); - IrInstSrc *index_ptr = ir_build_var_ptr(irb, parent_scope, node, index_var); - - - IrBasicBlockSrc *cond_block = ir_create_basic_block(irb, parent_scope, "ForCond"); - IrBasicBlockSrc *body_block = ir_create_basic_block(irb, parent_scope, "ForBody"); - IrBasicBlockSrc *end_block = ir_create_basic_block(irb, parent_scope, "ForEnd"); - IrBasicBlockSrc *else_block = else_node ? ir_create_basic_block(irb, parent_scope, "ForElse") : end_block; - IrBasicBlockSrc *continue_block = ir_create_basic_block(irb, parent_scope, "ForContinue"); - - Buf *len_field_name = buf_create_from_str("len"); - IrInstSrc *len_ref = ir_build_field_ptr(irb, parent_scope, node, array_val_ptr, len_field_name, false); - IrInstSrc *len_val = ir_build_load_ptr(irb, &spill_scope->base, node, len_ref); - ir_build_br(irb, parent_scope, node, cond_block, is_comptime); - - ir_set_cursor_at_end_and_append_block(irb, cond_block); - IrInstSrc *index_val = ir_build_load_ptr(irb, &spill_scope->base, node, index_ptr); - IrInstSrc *cond = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpLessThan, index_val, len_val, false); - IrBasicBlockSrc *after_cond_block = irb->current_basic_block; - IrInstSrc *void_else_value = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, parent_scope, node)); - IrInstSrc *cond_br_inst = ir_mark_gen(ir_build_cond_br(irb, parent_scope, node, cond, - body_block, else_block, is_comptime)); - - ResultLocPeerParent *peer_parent = ir_build_result_peers(irb, cond_br_inst, end_block, result_loc, is_comptime); - - ir_set_cursor_at_end_and_append_block(irb, body_block); - IrInstSrc *elem_ptr = ir_build_elem_ptr(irb, &spill_scope->base, node, array_val_ptr, index_val, - false, PtrLenSingle, nullptr); - // TODO make it an error to write to element variable or i variable. - Buf *elem_var_name = elem_node->data.symbol_expr.symbol; - ZigVar *elem_var = ir_create_var(irb, elem_node, parent_scope, elem_var_name, true, false, false, is_comptime); - Scope *child_scope = elem_var->child_scope; - - IrInstSrc *elem_value = node->data.for_expr.elem_is_ptr ? - elem_ptr : ir_build_load_ptr(irb, &spill_scope->base, elem_node, elem_ptr); - build_decl_var_and_init(irb, parent_scope, elem_node, elem_var, elem_value, buf_ptr(elem_var_name), is_comptime); - - if (is_duplicate_label(irb->codegen, child_scope, node, node->data.for_expr.name)) - return irb->codegen->invalid_inst_src; - - ZigList incoming_values = {0}; - ZigList incoming_blocks = {0}; - ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, child_scope); - loop_scope->break_block = end_block; - loop_scope->continue_block = continue_block; - loop_scope->is_comptime = is_comptime; - loop_scope->incoming_blocks = &incoming_blocks; - loop_scope->incoming_values = &incoming_values; - loop_scope->lval = LValNone; - loop_scope->peer_parent = peer_parent; - loop_scope->spill_scope = spill_scope; - - // Note the body block of the loop is not the place that lval and result_loc are used - - // it's actually in break statements, handled similarly to return statements. - // That is why we set those values in loop_scope above and not in this ir_gen_node call. - IrInstSrc *body_result = ir_gen_node(irb, body_node, &loop_scope->base); - if (body_result == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - - if (loop_scope->name != nullptr && loop_scope->name_used == false) { - add_node_error(irb->codegen, node, buf_sprintf("unused for label")); - } - - if (!instr_is_unreachable(body_result)) { - ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, node->data.for_expr.body, body_result)); - ir_mark_gen(ir_build_br(irb, child_scope, node, continue_block, is_comptime)); - } - - ir_set_cursor_at_end_and_append_block(irb, continue_block); - IrInstSrc *new_index_val = ir_build_bin_op(irb, child_scope, node, IrBinOpAdd, index_val, one, false); - ir_build_store_ptr(irb, child_scope, node, index_ptr, new_index_val)->allow_write_through_const = true; - ir_build_br(irb, child_scope, node, cond_block, is_comptime); - - IrInstSrc *else_result = nullptr; - if (else_node) { - ir_set_cursor_at_end_and_append_block(irb, else_block); - - if (peer_parent->peers.length != 0) { - peer_parent->peers.last()->next_bb = else_block; - } - ResultLocPeer *peer_result = create_peer_result(peer_parent); - peer_parent->peers.append(peer_result); - else_result = ir_gen_node_extra(irb, else_node, parent_scope, LValNone, &peer_result->base); - if (else_result == irb->codegen->invalid_inst_src) - return else_result; - if (!instr_is_unreachable(else_result)) - ir_mark_gen(ir_build_br(irb, parent_scope, node, end_block, is_comptime)); - } - IrBasicBlockSrc *after_else_block = irb->current_basic_block; - ir_set_cursor_at_end_and_append_block(irb, end_block); - - if (else_result) { - incoming_blocks.append(after_else_block); - incoming_values.append(else_result); - } else { - incoming_blocks.append(after_cond_block); - incoming_values.append(void_else_value); - } - if (peer_parent->peers.length != 0) { - peer_parent->peers.last()->next_bb = end_block; - } - - IrInstSrc *phi = ir_build_phi(irb, parent_scope, node, incoming_blocks.length, - incoming_blocks.items, incoming_values.items, peer_parent); - return ir_lval_wrap(irb, parent_scope, phi, lval, result_loc); -} - -static IrInstSrc *ir_gen_bool_literal(IrBuilderSrc *irb, Scope *scope, AstNode *node) { - assert(node->type == NodeTypeBoolLiteral); - return ir_build_const_bool(irb, scope, node, node->data.bool_literal.value); -} - -static IrInstSrc *ir_gen_enum_literal(IrBuilderSrc *irb, Scope *scope, AstNode *node) { - assert(node->type == NodeTypeEnumLiteral); - Buf *name = &node->data.enum_literal.identifier->data.str_lit.str; - return ir_build_const_enum_literal(irb, scope, node, name); -} - -static IrInstSrc *ir_gen_string_literal(IrBuilderSrc *irb, Scope *scope, AstNode *node) { - assert(node->type == NodeTypeStringLiteral); - - return ir_build_const_str_lit(irb, scope, node, node->data.string_literal.buf); -} - -static IrInstSrc *ir_gen_array_type(IrBuilderSrc *irb, Scope *scope, AstNode *node) { - assert(node->type == NodeTypeArrayType); - - AstNode *size_node = node->data.array_type.size; - AstNode *child_type_node = node->data.array_type.child_type; - bool is_const = node->data.array_type.is_const; - bool is_volatile = node->data.array_type.is_volatile; - bool is_allow_zero = node->data.array_type.allow_zero_token != nullptr; - AstNode *sentinel_expr = node->data.array_type.sentinel; - AstNode *align_expr = node->data.array_type.align_expr; - - Scope *comptime_scope = create_comptime_scope(irb->codegen, node, scope); - - IrInstSrc *sentinel; - if (sentinel_expr != nullptr) { - sentinel = ir_gen_node(irb, sentinel_expr, comptime_scope); - if (sentinel == irb->codegen->invalid_inst_src) - return sentinel; - } else { - sentinel = nullptr; - } - - if (size_node) { - if (is_const) { - add_node_error(irb->codegen, node, buf_create_from_str("const qualifier invalid on array type")); - return irb->codegen->invalid_inst_src; - } - if (is_volatile) { - add_node_error(irb->codegen, node, buf_create_from_str("volatile qualifier invalid on array type")); - return irb->codegen->invalid_inst_src; - } - if (is_allow_zero) { - add_node_error(irb->codegen, node, buf_create_from_str("allowzero qualifier invalid on array type")); - return irb->codegen->invalid_inst_src; - } - if (align_expr != nullptr) { - add_node_error(irb->codegen, node, buf_create_from_str("align qualifier invalid on array type")); - return irb->codegen->invalid_inst_src; - } - - IrInstSrc *size_value = ir_gen_node(irb, size_node, comptime_scope); - if (size_value == irb->codegen->invalid_inst_src) - return size_value; - - IrInstSrc *child_type = ir_gen_node(irb, child_type_node, comptime_scope); - if (child_type == irb->codegen->invalid_inst_src) - return child_type; - - return ir_build_array_type(irb, scope, node, size_value, sentinel, child_type); - } else { - IrInstSrc *align_value; - if (align_expr != nullptr) { - align_value = ir_gen_node(irb, align_expr, comptime_scope); - if (align_value == irb->codegen->invalid_inst_src) - return align_value; - } else { - align_value = nullptr; - } - - IrInstSrc *child_type = ir_gen_node(irb, child_type_node, comptime_scope); - if (child_type == irb->codegen->invalid_inst_src) - return child_type; - - return ir_build_slice_type(irb, scope, node, child_type, is_const, is_volatile, sentinel, - align_value, is_allow_zero); - } -} - -static IrInstSrc *ir_gen_anyframe_type(IrBuilderSrc *irb, Scope *scope, AstNode *node) { - assert(node->type == NodeTypeAnyFrameType); - - AstNode *payload_type_node = node->data.anyframe_type.payload_type; - IrInstSrc *payload_type_value = nullptr; - - if (payload_type_node != nullptr) { - payload_type_value = ir_gen_node(irb, payload_type_node, scope); - if (payload_type_value == irb->codegen->invalid_inst_src) - return payload_type_value; - - } - - return ir_build_anyframe_type(irb, scope, node, payload_type_value); -} - -static IrInstSrc *ir_gen_undefined_literal(IrBuilderSrc *irb, Scope *scope, AstNode *node) { - assert(node->type == NodeTypeUndefinedLiteral); - return ir_build_const_undefined(irb, scope, node); -} - -static Error parse_asm_template(IrAnalyze *ira, AstNode *source_node, Buf *asm_template, - ZigList *tok_list) -{ - // TODO Connect the errors in this function back up to the actual source location - // rather than just the token. https://github.com/ziglang/zig/issues/2080 - enum State { - StateStart, - StatePercent, - StateTemplate, - StateVar, - }; - - assert(tok_list->length == 0); - - AsmToken *cur_tok = nullptr; - - enum State state = StateStart; - - for (size_t i = 0; i < buf_len(asm_template); i += 1) { - uint8_t c = *((uint8_t*)buf_ptr(asm_template) + i); - switch (state) { - case StateStart: - if (c == '%') { - tok_list->add_one(); - cur_tok = &tok_list->last(); - cur_tok->id = AsmTokenIdPercent; - cur_tok->start = i; - state = StatePercent; - } else { - tok_list->add_one(); - cur_tok = &tok_list->last(); - cur_tok->id = AsmTokenIdTemplate; - cur_tok->start = i; - state = StateTemplate; - } - break; - case StatePercent: - if (c == '%') { - cur_tok->end = i; - state = StateStart; - } else if (c == '[') { - cur_tok->id = AsmTokenIdVar; - state = StateVar; - } else if (c == '=') { - cur_tok->id = AsmTokenIdUniqueId; - cur_tok->end = i; - state = StateStart; - } else { - add_node_error(ira->codegen, source_node, - buf_create_from_str("expected a '%' or '['")); - return ErrorSemanticAnalyzeFail; - } - break; - case StateTemplate: - if (c == '%') { - cur_tok->end = i; - i -= 1; - cur_tok = nullptr; - state = StateStart; - } - break; - case StateVar: - if (c == ']') { - cur_tok->end = i; - state = StateStart; - } else if ((c >= 'a' && c <= 'z') || - (c >= '0' && c <= '9') || - (c == '_')) - { - // do nothing - } else { - add_node_error(ira->codegen, source_node, - buf_sprintf("invalid substitution character: '%c'", c)); - return ErrorSemanticAnalyzeFail; - } - break; - } - } - - switch (state) { - case StateStart: - break; - case StatePercent: - case StateVar: - add_node_error(ira->codegen, source_node, buf_sprintf("unexpected end of assembly template")); - return ErrorSemanticAnalyzeFail; - case StateTemplate: - cur_tok->end = buf_len(asm_template); - break; - } - return ErrorNone; -} - -static size_t find_asm_index(CodeGen *g, AstNode *node, AsmToken *tok, Buf *src_template) { - const char *ptr = buf_ptr(src_template) + tok->start + 2; - size_t len = tok->end - tok->start - 2; - size_t result = 0; - for (size_t i = 0; i < node->data.asm_expr.output_list.length; i += 1, result += 1) { - AsmOutput *asm_output = node->data.asm_expr.output_list.at(i); - if (buf_eql_mem(asm_output->asm_symbolic_name, ptr, len)) { - return result; - } - } - for (size_t i = 0; i < node->data.asm_expr.input_list.length; i += 1, result += 1) { - AsmInput *asm_input = node->data.asm_expr.input_list.at(i); - if (buf_eql_mem(asm_input->asm_symbolic_name, ptr, len)) { - return result; - } - } - return SIZE_MAX; -} - -static IrInstSrc *ir_gen_asm_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node) { - assert(node->type == NodeTypeAsmExpr); - AstNodeAsmExpr *asm_expr = &node->data.asm_expr; - - IrInstSrc *asm_template = ir_gen_node(irb, asm_expr->asm_template, scope); - if (asm_template == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - - bool is_volatile = asm_expr->volatile_token != nullptr; - bool in_fn_scope = (scope_fn_entry(scope) != nullptr); - - if (!in_fn_scope) { - if (is_volatile) { - add_token_error(irb->codegen, node->owner, asm_expr->volatile_token, - buf_sprintf("volatile is meaningless on global assembly")); - return irb->codegen->invalid_inst_src; - } - - if (asm_expr->output_list.length != 0 || asm_expr->input_list.length != 0 || - asm_expr->clobber_list.length != 0) - { - add_node_error(irb->codegen, node, - buf_sprintf("global assembly cannot have inputs, outputs, or clobbers")); - return irb->codegen->invalid_inst_src; - } - - return ir_build_asm_src(irb, scope, node, asm_template, nullptr, nullptr, - nullptr, 0, is_volatile, true); - } - - IrInstSrc **input_list = heap::c_allocator.allocate(asm_expr->input_list.length); - IrInstSrc **output_types = heap::c_allocator.allocate(asm_expr->output_list.length); - ZigVar **output_vars = heap::c_allocator.allocate(asm_expr->output_list.length); - size_t return_count = 0; - if (!is_volatile && asm_expr->output_list.length == 0) { - add_node_error(irb->codegen, node, - buf_sprintf("assembly expression with no output must be marked volatile")); - return irb->codegen->invalid_inst_src; - } - for (size_t i = 0; i < asm_expr->output_list.length; i += 1) { - AsmOutput *asm_output = asm_expr->output_list.at(i); - if (asm_output->return_type) { - return_count += 1; - - IrInstSrc *return_type = ir_gen_node(irb, asm_output->return_type, scope); - if (return_type == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - if (return_count > 1) { - add_node_error(irb->codegen, node, - buf_sprintf("inline assembly allows up to one output value")); - return irb->codegen->invalid_inst_src; - } - output_types[i] = return_type; - } else { - Buf *variable_name = asm_output->variable_name; - // TODO there is some duplication here with ir_gen_symbol. I need to do a full audit of how - // inline assembly works. https://github.com/ziglang/zig/issues/215 - ZigVar *var = find_variable(irb->codegen, scope, variable_name, nullptr); - if (var) { - output_vars[i] = var; - } else { - add_node_error(irb->codegen, node, - buf_sprintf("use of undeclared identifier '%s'", buf_ptr(variable_name))); - return irb->codegen->invalid_inst_src; - } - } - - const char modifier = *buf_ptr(asm_output->constraint); - if (modifier != '=') { - add_node_error(irb->codegen, node, - buf_sprintf("invalid modifier starting output constraint for '%s': '%c', only '=' is supported." - " Compiler TODO: see https://github.com/ziglang/zig/issues/215", - buf_ptr(asm_output->asm_symbolic_name), modifier)); - return irb->codegen->invalid_inst_src; - } - } - for (size_t i = 0; i < asm_expr->input_list.length; i += 1) { - AsmInput *asm_input = asm_expr->input_list.at(i); - IrInstSrc *input_value = ir_gen_node(irb, asm_input->expr, scope); - if (input_value == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - - input_list[i] = input_value; - } - - return ir_build_asm_src(irb, scope, node, asm_template, input_list, output_types, - output_vars, return_count, is_volatile, false); -} - -static IrInstSrc *ir_gen_if_optional_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, - ResultLoc *result_loc) -{ - assert(node->type == NodeTypeIfOptional); - - Buf *var_symbol = node->data.test_expr.var_symbol; - AstNode *expr_node = node->data.test_expr.target_node; - AstNode *then_node = node->data.test_expr.then_node; - AstNode *else_node = node->data.test_expr.else_node; - bool var_is_ptr = node->data.test_expr.var_is_ptr; - - ScopeExpr *spill_scope = create_expr_scope(irb->codegen, expr_node, scope); - spill_scope->spill_harder = true; - - IrInstSrc *maybe_val_ptr = ir_gen_node_extra(irb, expr_node, &spill_scope->base, LValPtr, nullptr); - if (maybe_val_ptr == irb->codegen->invalid_inst_src) - return maybe_val_ptr; - - IrInstSrc *maybe_val = ir_build_load_ptr(irb, scope, node, maybe_val_ptr); - IrInstSrc *is_non_null = ir_build_test_non_null_src(irb, scope, node, maybe_val); - - IrBasicBlockSrc *then_block = ir_create_basic_block(irb, scope, "OptionalThen"); - IrBasicBlockSrc *else_block = ir_create_basic_block(irb, scope, "OptionalElse"); - IrBasicBlockSrc *endif_block = ir_create_basic_block(irb, scope, "OptionalEndIf"); - - IrInstSrc *is_comptime; - if (ir_should_inline(irb->exec, scope)) { - is_comptime = ir_build_const_bool(irb, scope, node, true); - } else { - is_comptime = ir_build_test_comptime(irb, scope, node, is_non_null); - } - IrInstSrc *cond_br_inst = ir_build_cond_br(irb, scope, node, is_non_null, - then_block, else_block, is_comptime); - - ResultLocPeerParent *peer_parent = ir_build_binary_result_peers(irb, cond_br_inst, else_block, endif_block, - result_loc, is_comptime); - - ir_set_cursor_at_end_and_append_block(irb, then_block); - - Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, &spill_scope->base, is_comptime); - Scope *var_scope; - if (var_symbol) { - bool is_shadowable = false; - bool is_const = true; - ZigVar *var = ir_create_var(irb, node, subexpr_scope, - var_symbol, is_const, is_const, is_shadowable, is_comptime); - - IrInstSrc *payload_ptr = ir_build_optional_unwrap_ptr(irb, subexpr_scope, node, maybe_val_ptr, false); - IrInstSrc *var_value = var_is_ptr ? - payload_ptr : ir_build_load_ptr(irb, &spill_scope->base, node, payload_ptr); - build_decl_var_and_init(irb, subexpr_scope, node, var, var_value, buf_ptr(var_symbol), is_comptime); - var_scope = var->child_scope; - } else { - var_scope = subexpr_scope; - } - IrInstSrc *then_expr_result = ir_gen_node_extra(irb, then_node, var_scope, lval, - &peer_parent->peers.at(0)->base); - if (then_expr_result == irb->codegen->invalid_inst_src) - return then_expr_result; - IrBasicBlockSrc *after_then_block = irb->current_basic_block; - if (!instr_is_unreachable(then_expr_result)) - ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); - - ir_set_cursor_at_end_and_append_block(irb, else_block); - IrInstSrc *else_expr_result; - if (else_node) { - else_expr_result = ir_gen_node_extra(irb, else_node, subexpr_scope, lval, &peer_parent->peers.at(1)->base); - if (else_expr_result == irb->codegen->invalid_inst_src) - return else_expr_result; - } else { - else_expr_result = ir_build_const_void(irb, scope, node); - ir_build_end_expr(irb, scope, node, else_expr_result, &peer_parent->peers.at(1)->base); - } - IrBasicBlockSrc *after_else_block = irb->current_basic_block; - if (!instr_is_unreachable(else_expr_result)) - ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); - - ir_set_cursor_at_end_and_append_block(irb, endif_block); - IrInstSrc **incoming_values = heap::c_allocator.allocate(2); - incoming_values[0] = then_expr_result; - incoming_values[1] = else_expr_result; - IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate(2); - incoming_blocks[0] = after_then_block; - incoming_blocks[1] = after_else_block; - - IrInstSrc *phi = ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values, peer_parent); - return ir_expr_wrap(irb, scope, phi, result_loc); -} - -static IrInstSrc *ir_gen_if_err_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, - ResultLoc *result_loc) -{ - assert(node->type == NodeTypeIfErrorExpr); - - AstNode *target_node = node->data.if_err_expr.target_node; - AstNode *then_node = node->data.if_err_expr.then_node; - AstNode *else_node = node->data.if_err_expr.else_node; - bool var_is_ptr = node->data.if_err_expr.var_is_ptr; - bool var_is_const = true; - Buf *var_symbol = node->data.if_err_expr.var_symbol; - Buf *err_symbol = node->data.if_err_expr.err_symbol; - - IrInstSrc *err_val_ptr = ir_gen_node_extra(irb, target_node, scope, LValPtr, nullptr); - if (err_val_ptr == irb->codegen->invalid_inst_src) - return err_val_ptr; - - IrInstSrc *err_val = ir_build_load_ptr(irb, scope, node, err_val_ptr); - IrInstSrc *is_err = ir_build_test_err_src(irb, scope, node, err_val_ptr, true, false); - - IrBasicBlockSrc *ok_block = ir_create_basic_block(irb, scope, "TryOk"); - IrBasicBlockSrc *else_block = ir_create_basic_block(irb, scope, "TryElse"); - IrBasicBlockSrc *endif_block = ir_create_basic_block(irb, scope, "TryEnd"); - - bool force_comptime = ir_should_inline(irb->exec, scope); - IrInstSrc *is_comptime = force_comptime ? ir_build_const_bool(irb, scope, node, true) : ir_build_test_comptime(irb, scope, node, is_err); - IrInstSrc *cond_br_inst = ir_build_cond_br(irb, scope, node, is_err, else_block, ok_block, is_comptime); - - ResultLocPeerParent *peer_parent = ir_build_binary_result_peers(irb, cond_br_inst, else_block, endif_block, - result_loc, is_comptime); - - ir_set_cursor_at_end_and_append_block(irb, ok_block); - - Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime); - Scope *var_scope; - if (var_symbol) { - bool is_shadowable = false; - IrInstSrc *var_is_comptime = force_comptime ? ir_build_const_bool(irb, subexpr_scope, node, true) : ir_build_test_comptime(irb, subexpr_scope, node, err_val); - ZigVar *var = ir_create_var(irb, node, subexpr_scope, - var_symbol, var_is_const, var_is_const, is_shadowable, var_is_comptime); - - IrInstSrc *payload_ptr = ir_build_unwrap_err_payload_src(irb, subexpr_scope, node, err_val_ptr, false, false); - IrInstSrc *var_value = var_is_ptr ? - payload_ptr : ir_build_load_ptr(irb, subexpr_scope, node, payload_ptr); - build_decl_var_and_init(irb, subexpr_scope, node, var, var_value, buf_ptr(var_symbol), var_is_comptime); - var_scope = var->child_scope; - } else { - var_scope = subexpr_scope; - } - IrInstSrc *then_expr_result = ir_gen_node_extra(irb, then_node, var_scope, lval, - &peer_parent->peers.at(0)->base); - if (then_expr_result == irb->codegen->invalid_inst_src) - return then_expr_result; - IrBasicBlockSrc *after_then_block = irb->current_basic_block; - if (!instr_is_unreachable(then_expr_result)) - ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); - - ir_set_cursor_at_end_and_append_block(irb, else_block); - - IrInstSrc *else_expr_result; - if (else_node) { - Scope *err_var_scope; - if (err_symbol) { - bool is_shadowable = false; - bool is_const = true; - ZigVar *var = ir_create_var(irb, node, subexpr_scope, - err_symbol, is_const, is_const, is_shadowable, is_comptime); - - IrInstSrc *err_ptr = ir_build_unwrap_err_code_src(irb, subexpr_scope, node, err_val_ptr); - IrInstSrc *err_value = ir_build_load_ptr(irb, subexpr_scope, node, err_ptr); - build_decl_var_and_init(irb, subexpr_scope, node, var, err_value, buf_ptr(err_symbol), is_comptime); - err_var_scope = var->child_scope; - } else { - err_var_scope = subexpr_scope; - } - else_expr_result = ir_gen_node_extra(irb, else_node, err_var_scope, lval, &peer_parent->peers.at(1)->base); - if (else_expr_result == irb->codegen->invalid_inst_src) - return else_expr_result; - } else { - else_expr_result = ir_build_const_void(irb, scope, node); - ir_build_end_expr(irb, scope, node, else_expr_result, &peer_parent->peers.at(1)->base); - } - IrBasicBlockSrc *after_else_block = irb->current_basic_block; - if (!instr_is_unreachable(else_expr_result)) - ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); - - ir_set_cursor_at_end_and_append_block(irb, endif_block); - IrInstSrc **incoming_values = heap::c_allocator.allocate(2); - incoming_values[0] = then_expr_result; - incoming_values[1] = else_expr_result; - IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate(2); - incoming_blocks[0] = after_then_block; - incoming_blocks[1] = after_else_block; - - IrInstSrc *phi = ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values, peer_parent); - return ir_expr_wrap(irb, scope, phi, result_loc); -} - -static bool ir_gen_switch_prong_expr(IrBuilderSrc *irb, Scope *scope, AstNode *switch_node, AstNode *prong_node, - IrBasicBlockSrc *end_block, IrInstSrc *is_comptime, IrInstSrc *var_is_comptime, - IrInstSrc *target_value_ptr, IrInstSrc **prong_values, size_t prong_values_len, - ZigList *incoming_blocks, ZigList *incoming_values, - IrInstSrcSwitchElseVar **out_switch_else_var, LVal lval, ResultLoc *result_loc) -{ - assert(switch_node->type == NodeTypeSwitchExpr); - assert(prong_node->type == NodeTypeSwitchProng); - - AstNode *expr_node = prong_node->data.switch_prong.expr; - AstNode *var_symbol_node = prong_node->data.switch_prong.var_symbol; - Scope *child_scope; - if (var_symbol_node) { - assert(var_symbol_node->type == NodeTypeSymbol); - Buf *var_name = var_symbol_node->data.symbol_expr.symbol; - bool var_is_ptr = prong_node->data.switch_prong.var_is_ptr; - - bool is_shadowable = false; - bool is_const = true; - ZigVar *var = ir_create_var(irb, var_symbol_node, scope, - var_name, is_const, is_const, is_shadowable, var_is_comptime); - child_scope = var->child_scope; - IrInstSrc *var_value; - if (out_switch_else_var != nullptr) { - IrInstSrcSwitchElseVar *switch_else_var = ir_build_switch_else_var(irb, scope, var_symbol_node, - target_value_ptr); - *out_switch_else_var = switch_else_var; - IrInstSrc *payload_ptr = &switch_else_var->base; - var_value = var_is_ptr ? - payload_ptr : ir_build_load_ptr(irb, scope, var_symbol_node, payload_ptr); - } else if (prong_values != nullptr) { - IrInstSrc *payload_ptr = ir_build_switch_var(irb, scope, var_symbol_node, target_value_ptr, - prong_values, prong_values_len); - var_value = var_is_ptr ? - payload_ptr : ir_build_load_ptr(irb, scope, var_symbol_node, payload_ptr); - } else { - var_value = var_is_ptr ? - target_value_ptr : ir_build_load_ptr(irb, scope, var_symbol_node, target_value_ptr); - } - build_decl_var_and_init(irb, scope, var_symbol_node, var, var_value, buf_ptr(var_name), var_is_comptime); - } else { - child_scope = scope; - } - - IrInstSrc *expr_result = ir_gen_node_extra(irb, expr_node, child_scope, lval, result_loc); - if (expr_result == irb->codegen->invalid_inst_src) - return false; - if (!instr_is_unreachable(expr_result)) - ir_mark_gen(ir_build_br(irb, scope, switch_node, end_block, is_comptime)); - incoming_blocks->append(irb->current_basic_block); - incoming_values->append(expr_result); - return true; -} - -static IrInstSrc *ir_gen_switch_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, - ResultLoc *result_loc) -{ - assert(node->type == NodeTypeSwitchExpr); - - AstNode *target_node = node->data.switch_expr.expr; - IrInstSrc *target_value_ptr = ir_gen_node_extra(irb, target_node, scope, LValPtr, nullptr); - if (target_value_ptr == irb->codegen->invalid_inst_src) - return target_value_ptr; - IrInstSrc *target_value = ir_build_switch_target(irb, scope, node, target_value_ptr); - - IrBasicBlockSrc *else_block = ir_create_basic_block(irb, scope, "SwitchElse"); - IrBasicBlockSrc *end_block = ir_create_basic_block(irb, scope, "SwitchEnd"); - - size_t prong_count = node->data.switch_expr.prongs.length; - ZigList cases = {0}; - - IrInstSrc *is_comptime; - IrInstSrc *var_is_comptime; - if (ir_should_inline(irb->exec, scope)) { - is_comptime = ir_build_const_bool(irb, scope, node, true); - var_is_comptime = is_comptime; - } else { - is_comptime = ir_build_test_comptime(irb, scope, node, target_value); - var_is_comptime = ir_build_test_comptime(irb, scope, node, target_value_ptr); - } - - ZigList incoming_values = {0}; - ZigList incoming_blocks = {0}; - ZigList check_ranges = {0}; - - IrInstSrcSwitchElseVar *switch_else_var = nullptr; - - ResultLocPeerParent *peer_parent = heap::c_allocator.create(); - peer_parent->base.id = ResultLocIdPeerParent; - peer_parent->base.allow_write_through_const = result_loc->allow_write_through_const; - peer_parent->end_bb = end_block; - peer_parent->is_comptime = is_comptime; - peer_parent->parent = result_loc; - - ir_build_reset_result(irb, scope, node, &peer_parent->base); - - // First do the else and the ranges - Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime); - Scope *comptime_scope = create_comptime_scope(irb->codegen, node, scope); - AstNode *else_prong = nullptr; - AstNode *underscore_prong = nullptr; - for (size_t prong_i = 0; prong_i < prong_count; prong_i += 1) { - AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i); - size_t prong_item_count = prong_node->data.switch_prong.items.length; - if (prong_node->data.switch_prong.any_items_are_range) { - ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent); - - IrInstSrc *ok_bit = nullptr; - AstNode *last_item_node = nullptr; - for (size_t item_i = 0; item_i < prong_item_count; item_i += 1) { - AstNode *item_node = prong_node->data.switch_prong.items.at(item_i); - last_item_node = item_node; - if (item_node->type == NodeTypeSwitchRange) { - AstNode *start_node = item_node->data.switch_range.start; - AstNode *end_node = item_node->data.switch_range.end; - - IrInstSrc *start_value = ir_gen_node(irb, start_node, comptime_scope); - if (start_value == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - - IrInstSrc *end_value = ir_gen_node(irb, end_node, comptime_scope); - if (end_value == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - - IrInstSrcCheckSwitchProngsRange *check_range = check_ranges.add_one(); - check_range->start = start_value; - check_range->end = end_value; - - IrInstSrc *lower_range_ok = ir_build_bin_op(irb, scope, item_node, IrBinOpCmpGreaterOrEq, - target_value, start_value, false); - IrInstSrc *upper_range_ok = ir_build_bin_op(irb, scope, item_node, IrBinOpCmpLessOrEq, - target_value, end_value, false); - IrInstSrc *both_ok = ir_build_bin_op(irb, scope, item_node, IrBinOpBoolAnd, - lower_range_ok, upper_range_ok, false); - if (ok_bit) { - ok_bit = ir_build_bin_op(irb, scope, item_node, IrBinOpBoolOr, both_ok, ok_bit, false); - } else { - ok_bit = both_ok; - } - } else { - IrInstSrc *item_value = ir_gen_node(irb, item_node, comptime_scope); - if (item_value == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - - IrInstSrcCheckSwitchProngsRange *check_range = check_ranges.add_one(); - check_range->start = item_value; - check_range->end = item_value; - - IrInstSrc *cmp_ok = ir_build_bin_op(irb, scope, item_node, IrBinOpCmpEq, - item_value, target_value, false); - if (ok_bit) { - ok_bit = ir_build_bin_op(irb, scope, item_node, IrBinOpBoolOr, cmp_ok, ok_bit, false); - } else { - ok_bit = cmp_ok; - } - } - } - - IrBasicBlockSrc *range_block_yes = ir_create_basic_block(irb, scope, "SwitchRangeYes"); - IrBasicBlockSrc *range_block_no = ir_create_basic_block(irb, scope, "SwitchRangeNo"); - - assert(ok_bit); - assert(last_item_node); - IrInstSrc *br_inst = ir_mark_gen(ir_build_cond_br(irb, scope, last_item_node, ok_bit, - range_block_yes, range_block_no, is_comptime)); - if (peer_parent->base.source_instruction == nullptr) { - peer_parent->base.source_instruction = br_inst; - } - - if (peer_parent->peers.length > 0) { - peer_parent->peers.last()->next_bb = range_block_yes; - } - peer_parent->peers.append(this_peer_result_loc); - ir_set_cursor_at_end_and_append_block(irb, range_block_yes); - if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block, - is_comptime, var_is_comptime, target_value_ptr, nullptr, 0, - &incoming_blocks, &incoming_values, nullptr, LValNone, &this_peer_result_loc->base)) - { - return irb->codegen->invalid_inst_src; - } - - ir_set_cursor_at_end_and_append_block(irb, range_block_no); - } else { - if (prong_item_count == 0) { - if (else_prong) { - ErrorMsg *msg = add_node_error(irb->codegen, prong_node, - buf_sprintf("multiple else prongs in switch expression")); - add_error_note(irb->codegen, msg, else_prong, - buf_sprintf("previous else prong is here")); - return irb->codegen->invalid_inst_src; - } - else_prong = prong_node; - } else if (prong_item_count == 1 && - prong_node->data.switch_prong.items.at(0)->type == NodeTypeSymbol && - buf_eql_str(prong_node->data.switch_prong.items.at(0)->data.symbol_expr.symbol, "_")) { - if (underscore_prong) { - ErrorMsg *msg = add_node_error(irb->codegen, prong_node, - buf_sprintf("multiple '_' prongs in switch expression")); - add_error_note(irb->codegen, msg, underscore_prong, - buf_sprintf("previous '_' prong is here")); - return irb->codegen->invalid_inst_src; - } - underscore_prong = prong_node; - } else { - continue; - } - if (underscore_prong && else_prong) { - ErrorMsg *msg = add_node_error(irb->codegen, prong_node, - buf_sprintf("else and '_' prong in switch expression")); - if (underscore_prong == prong_node) - add_error_note(irb->codegen, msg, else_prong, - buf_sprintf("else prong is here")); - else - add_error_note(irb->codegen, msg, underscore_prong, - buf_sprintf("'_' prong is here")); - return irb->codegen->invalid_inst_src; - } - ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent); - - IrBasicBlockSrc *prev_block = irb->current_basic_block; - if (peer_parent->peers.length > 0) { - peer_parent->peers.last()->next_bb = else_block; - } - peer_parent->peers.append(this_peer_result_loc); - ir_set_cursor_at_end_and_append_block(irb, else_block); - if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block, - is_comptime, var_is_comptime, target_value_ptr, nullptr, 0, &incoming_blocks, &incoming_values, - &switch_else_var, LValNone, &this_peer_result_loc->base)) - { - return irb->codegen->invalid_inst_src; - } - ir_set_cursor_at_end(irb, prev_block); - } - } - - // next do the non-else non-ranges - for (size_t prong_i = 0; prong_i < prong_count; prong_i += 1) { - AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i); - size_t prong_item_count = prong_node->data.switch_prong.items.length; - if (prong_item_count == 0) - continue; - if (prong_node->data.switch_prong.any_items_are_range) - continue; - if (underscore_prong == prong_node) - continue; - - ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent); - - IrBasicBlockSrc *prong_block = ir_create_basic_block(irb, scope, "SwitchProng"); - IrInstSrc **items = heap::c_allocator.allocate(prong_item_count); - - for (size_t item_i = 0; item_i < prong_item_count; item_i += 1) { - AstNode *item_node = prong_node->data.switch_prong.items.at(item_i); - assert(item_node->type != NodeTypeSwitchRange); - - IrInstSrc *item_value = ir_gen_node(irb, item_node, comptime_scope); - if (item_value == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - - IrInstSrcCheckSwitchProngsRange *check_range = check_ranges.add_one(); - check_range->start = item_value; - check_range->end = item_value; - - IrInstSrcSwitchBrCase *this_case = cases.add_one(); - this_case->value = item_value; - this_case->block = prong_block; - - items[item_i] = item_value; - } - - IrBasicBlockSrc *prev_block = irb->current_basic_block; - if (peer_parent->peers.length > 0) { - peer_parent->peers.last()->next_bb = prong_block; - } - peer_parent->peers.append(this_peer_result_loc); - ir_set_cursor_at_end_and_append_block(irb, prong_block); - if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block, - is_comptime, var_is_comptime, target_value_ptr, items, prong_item_count, - &incoming_blocks, &incoming_values, nullptr, LValNone, &this_peer_result_loc->base)) - { - return irb->codegen->invalid_inst_src; - } - - ir_set_cursor_at_end(irb, prev_block); - - } - - IrInstSrc *switch_prongs_void = ir_build_check_switch_prongs(irb, scope, node, target_value, - check_ranges.items, check_ranges.length, else_prong, underscore_prong != nullptr); - - IrInstSrc *br_instruction; - if (cases.length == 0) { - br_instruction = ir_build_br(irb, scope, node, else_block, is_comptime); - } else { - IrInstSrcSwitchBr *switch_br = ir_build_switch_br_src(irb, scope, node, target_value, else_block, - cases.length, cases.items, is_comptime, switch_prongs_void); - if (switch_else_var != nullptr) { - switch_else_var->switch_br = switch_br; - } - br_instruction = &switch_br->base; - } - if (peer_parent->base.source_instruction == nullptr) { - peer_parent->base.source_instruction = br_instruction; - } - for (size_t i = 0; i < peer_parent->peers.length; i += 1) { - peer_parent->peers.at(i)->base.source_instruction = peer_parent->base.source_instruction; - } - - if (!else_prong && !underscore_prong) { - if (peer_parent->peers.length != 0) { - peer_parent->peers.last()->next_bb = else_block; - } - ir_set_cursor_at_end_and_append_block(irb, else_block); - ir_build_unreachable(irb, scope, node); - } else { - if (peer_parent->peers.length != 0) { - peer_parent->peers.last()->next_bb = end_block; - } - } - - ir_set_cursor_at_end_and_append_block(irb, end_block); - assert(incoming_blocks.length == incoming_values.length); - IrInstSrc *result_instruction; - if (incoming_blocks.length == 0) { - result_instruction = ir_build_const_void(irb, scope, node); - } else { - result_instruction = ir_build_phi(irb, scope, node, incoming_blocks.length, - incoming_blocks.items, incoming_values.items, peer_parent); - } - return ir_lval_wrap(irb, scope, result_instruction, lval, result_loc); -} - -static IrInstSrc *ir_gen_comptime(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node, LVal lval) { - assert(node->type == NodeTypeCompTime); - - Scope *child_scope = create_comptime_scope(irb->codegen, node, parent_scope); - // purposefully pass null for result_loc and let EndExpr handle it - return ir_gen_node_extra(irb, node->data.comptime_expr.expr, child_scope, lval, nullptr); -} - -static IrInstSrc *ir_gen_nosuspend(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node, LVal lval) { - assert(node->type == NodeTypeNoSuspend); - - Scope *child_scope = create_nosuspend_scope(irb->codegen, node, parent_scope); - // purposefully pass null for result_loc and let EndExpr handle it - return ir_gen_node_extra(irb, node->data.comptime_expr.expr, child_scope, lval, nullptr); -} - -static IrInstSrc *ir_gen_return_from_block(IrBuilderSrc *irb, Scope *break_scope, AstNode *node, ScopeBlock *block_scope) { - IrInstSrc *is_comptime; - if (ir_should_inline(irb->exec, break_scope)) { - is_comptime = ir_build_const_bool(irb, break_scope, node, true); - } else { - is_comptime = block_scope->is_comptime; - } - - IrInstSrc *result_value; - if (node->data.break_expr.expr) { - ResultLocPeer *peer_result = create_peer_result(block_scope->peer_parent); - block_scope->peer_parent->peers.append(peer_result); - - result_value = ir_gen_node_extra(irb, node->data.break_expr.expr, break_scope, block_scope->lval, - &peer_result->base); - if (result_value == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - } else { - result_value = ir_build_const_void(irb, break_scope, node); - } - - IrBasicBlockSrc *dest_block = block_scope->end_block; - if (!ir_gen_defers_for_block(irb, break_scope, dest_block->scope, nullptr, nullptr)) - return irb->codegen->invalid_inst_src; - - block_scope->incoming_blocks->append(irb->current_basic_block); - block_scope->incoming_values->append(result_value); - return ir_build_br(irb, break_scope, node, dest_block, is_comptime); -} - -static IrInstSrc *ir_gen_break(IrBuilderSrc *irb, Scope *break_scope, AstNode *node) { - assert(node->type == NodeTypeBreak); - - // Search up the scope. We'll find one of these things first: - // * function definition scope or global scope => error, break outside loop - // * defer expression scope => error, cannot break out of defer expression - // * loop scope => OK - // * (if it's a labeled break) labeled block => OK - - Scope *search_scope = break_scope; - ScopeLoop *loop_scope; - for (;;) { - if (search_scope == nullptr || search_scope->id == ScopeIdFnDef) { - if (node->data.break_expr.name != nullptr) { - add_node_error(irb->codegen, node, buf_sprintf("label not found: '%s'", buf_ptr(node->data.break_expr.name))); - return irb->codegen->invalid_inst_src; - } else { - add_node_error(irb->codegen, node, buf_sprintf("break expression outside loop")); - return irb->codegen->invalid_inst_src; - } - } else if (search_scope->id == ScopeIdDeferExpr) { - add_node_error(irb->codegen, node, buf_sprintf("cannot break out of defer expression")); - return irb->codegen->invalid_inst_src; - } else if (search_scope->id == ScopeIdLoop) { - ScopeLoop *this_loop_scope = (ScopeLoop *)search_scope; - if (node->data.break_expr.name == nullptr || - (this_loop_scope->name != nullptr && buf_eql_buf(node->data.break_expr.name, this_loop_scope->name))) - { - this_loop_scope->name_used = true; - loop_scope = this_loop_scope; - break; - } - } else if (search_scope->id == ScopeIdBlock) { - ScopeBlock *this_block_scope = (ScopeBlock *)search_scope; - if (node->data.break_expr.name != nullptr && - (this_block_scope->name != nullptr && buf_eql_buf(node->data.break_expr.name, this_block_scope->name))) - { - assert(this_block_scope->end_block != nullptr); - this_block_scope->name_used = true; - return ir_gen_return_from_block(irb, break_scope, node, this_block_scope); - } - } else if (search_scope->id == ScopeIdSuspend) { - add_node_error(irb->codegen, node, buf_sprintf("cannot break out of suspend block")); - return irb->codegen->invalid_inst_src; - } - search_scope = search_scope->parent; - } - - IrInstSrc *is_comptime; - if (ir_should_inline(irb->exec, break_scope)) { - is_comptime = ir_build_const_bool(irb, break_scope, node, true); - } else { - is_comptime = loop_scope->is_comptime; - } - - IrInstSrc *result_value; - if (node->data.break_expr.expr) { - ResultLocPeer *peer_result = create_peer_result(loop_scope->peer_parent); - loop_scope->peer_parent->peers.append(peer_result); - - result_value = ir_gen_node_extra(irb, node->data.break_expr.expr, break_scope, - loop_scope->lval, &peer_result->base); - if (result_value == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - } else { - result_value = ir_build_const_void(irb, break_scope, node); - } - - IrBasicBlockSrc *dest_block = loop_scope->break_block; - if (!ir_gen_defers_for_block(irb, break_scope, dest_block->scope, nullptr, nullptr)) - return irb->codegen->invalid_inst_src; - - loop_scope->incoming_blocks->append(irb->current_basic_block); - loop_scope->incoming_values->append(result_value); - return ir_build_br(irb, break_scope, node, dest_block, is_comptime); -} - -static IrInstSrc *ir_gen_continue(IrBuilderSrc *irb, Scope *continue_scope, AstNode *node) { - assert(node->type == NodeTypeContinue); - - // Search up the scope. We'll find one of these things first: - // * function definition scope or global scope => error, break outside loop - // * defer expression scope => error, cannot break out of defer expression - // * loop scope => OK - - ZigList runtime_scopes = {}; - - Scope *search_scope = continue_scope; - ScopeLoop *loop_scope; - for (;;) { - if (search_scope == nullptr || search_scope->id == ScopeIdFnDef) { - if (node->data.continue_expr.name != nullptr) { - add_node_error(irb->codegen, node, buf_sprintf("labeled loop not found: '%s'", buf_ptr(node->data.continue_expr.name))); - return irb->codegen->invalid_inst_src; - } else { - add_node_error(irb->codegen, node, buf_sprintf("continue expression outside loop")); - return irb->codegen->invalid_inst_src; - } - } else if (search_scope->id == ScopeIdDeferExpr) { - add_node_error(irb->codegen, node, buf_sprintf("cannot continue out of defer expression")); - return irb->codegen->invalid_inst_src; - } else if (search_scope->id == ScopeIdLoop) { - ScopeLoop *this_loop_scope = (ScopeLoop *)search_scope; - if (node->data.continue_expr.name == nullptr || - (this_loop_scope->name != nullptr && buf_eql_buf(node->data.continue_expr.name, this_loop_scope->name))) - { - this_loop_scope->name_used = true; - loop_scope = this_loop_scope; - break; - } - } else if (search_scope->id == ScopeIdRuntime) { - ScopeRuntime *scope_runtime = (ScopeRuntime *)search_scope; - runtime_scopes.append(scope_runtime); - } - search_scope = search_scope->parent; - } - - IrInstSrc *is_comptime; - if (ir_should_inline(irb->exec, continue_scope)) { - is_comptime = ir_build_const_bool(irb, continue_scope, node, true); - } else { - is_comptime = loop_scope->is_comptime; - } - - for (size_t i = 0; i < runtime_scopes.length; i += 1) { - ScopeRuntime *scope_runtime = runtime_scopes.at(i); - ir_mark_gen(ir_build_check_runtime_scope(irb, continue_scope, node, scope_runtime->is_comptime, is_comptime)); - } - - IrBasicBlockSrc *dest_block = loop_scope->continue_block; - if (!ir_gen_defers_for_block(irb, continue_scope, dest_block->scope, nullptr, nullptr)) - return irb->codegen->invalid_inst_src; - return ir_mark_gen(ir_build_br(irb, continue_scope, node, dest_block, is_comptime)); -} - -static IrInstSrc *ir_gen_error_type(IrBuilderSrc *irb, Scope *scope, AstNode *node) { - assert(node->type == NodeTypeErrorType); - return ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_global_error_set); -} - -static IrInstSrc *ir_gen_defer(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node) { - assert(node->type == NodeTypeDefer); - - ScopeDefer *defer_child_scope = create_defer_scope(irb->codegen, node, parent_scope); - node->data.defer.child_scope = &defer_child_scope->base; - - ScopeDeferExpr *defer_expr_scope = create_defer_expr_scope(irb->codegen, node, parent_scope); - node->data.defer.expr_scope = &defer_expr_scope->base; - - return ir_build_const_void(irb, parent_scope, node); -} - -static IrInstSrc *ir_gen_slice(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *result_loc) { - assert(node->type == NodeTypeSliceExpr); - - AstNodeSliceExpr *slice_expr = &node->data.slice_expr; - AstNode *array_node = slice_expr->array_ref_expr; - AstNode *start_node = slice_expr->start; - AstNode *end_node = slice_expr->end; - AstNode *sentinel_node = slice_expr->sentinel; - - IrInstSrc *ptr_value = ir_gen_node_extra(irb, array_node, scope, LValPtr, nullptr); - if (ptr_value == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - - IrInstSrc *start_value = ir_gen_node(irb, start_node, scope); - if (start_value == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - - IrInstSrc *end_value; - if (end_node) { - end_value = ir_gen_node(irb, end_node, scope); - if (end_value == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - } else { - end_value = nullptr; - } - - IrInstSrc *sentinel_value; - if (sentinel_node) { - sentinel_value = ir_gen_node(irb, sentinel_node, scope); - if (sentinel_value == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - } else { - sentinel_value = nullptr; - } - - IrInstSrc *slice = ir_build_slice_src(irb, scope, node, ptr_value, start_value, end_value, - sentinel_value, true, result_loc); - return ir_lval_wrap(irb, scope, slice, lval, result_loc); -} - -static IrInstSrc *ir_gen_catch(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node, LVal lval, - ResultLoc *result_loc) -{ - assert(node->type == NodeTypeCatchExpr); - - AstNode *op1_node = node->data.unwrap_err_expr.op1; - AstNode *op2_node = node->data.unwrap_err_expr.op2; - AstNode *var_node = node->data.unwrap_err_expr.symbol; - - if (op2_node->type == NodeTypeUnreachable) { - if (var_node != nullptr) { - assert(var_node->type == NodeTypeSymbol); - Buf *var_name = var_node->data.symbol_expr.symbol; - add_node_error(irb->codegen, var_node, buf_sprintf("unused variable: '%s'", buf_ptr(var_name))); - return irb->codegen->invalid_inst_src; - } - return ir_gen_catch_unreachable(irb, parent_scope, node, op1_node, lval, result_loc); - } - - - ScopeExpr *spill_scope = create_expr_scope(irb->codegen, op1_node, parent_scope); - spill_scope->spill_harder = true; - - IrInstSrc *err_union_ptr = ir_gen_node_extra(irb, op1_node, &spill_scope->base, LValPtr, nullptr); - if (err_union_ptr == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - - IrInstSrc *is_err = ir_build_test_err_src(irb, parent_scope, node, err_union_ptr, true, false); - - IrInstSrc *is_comptime; - if (ir_should_inline(irb->exec, parent_scope)) { - is_comptime = ir_build_const_bool(irb, parent_scope, node, true); - } else { - is_comptime = ir_build_test_comptime(irb, parent_scope, node, is_err); - } - - IrBasicBlockSrc *ok_block = ir_create_basic_block(irb, parent_scope, "UnwrapErrOk"); - IrBasicBlockSrc *err_block = ir_create_basic_block(irb, parent_scope, "UnwrapErrError"); - IrBasicBlockSrc *end_block = ir_create_basic_block(irb, parent_scope, "UnwrapErrEnd"); - IrInstSrc *cond_br_inst = ir_build_cond_br(irb, parent_scope, node, is_err, err_block, ok_block, is_comptime); - - ResultLocPeerParent *peer_parent = ir_build_binary_result_peers(irb, cond_br_inst, ok_block, end_block, result_loc, - is_comptime); - - ir_set_cursor_at_end_and_append_block(irb, err_block); - Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, &spill_scope->base, is_comptime); - Scope *err_scope; - if (var_node) { - assert(var_node->type == NodeTypeSymbol); - Buf *var_name = var_node->data.symbol_expr.symbol; - bool is_const = true; - bool is_shadowable = false; - ZigVar *var = ir_create_var(irb, node, subexpr_scope, var_name, - is_const, is_const, is_shadowable, is_comptime); - err_scope = var->child_scope; - IrInstSrc *err_ptr = ir_build_unwrap_err_code_src(irb, err_scope, node, err_union_ptr); - IrInstSrc *err_value = ir_build_load_ptr(irb, err_scope, var_node, err_ptr); - build_decl_var_and_init(irb, err_scope, var_node, var, err_value, buf_ptr(var_name), is_comptime); - } else { - err_scope = subexpr_scope; - } - IrInstSrc *err_result = ir_gen_node_extra(irb, op2_node, err_scope, LValNone, &peer_parent->peers.at(0)->base); - if (err_result == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - IrBasicBlockSrc *after_err_block = irb->current_basic_block; - if (!instr_is_unreachable(err_result)) - ir_mark_gen(ir_build_br(irb, parent_scope, node, end_block, is_comptime)); - - ir_set_cursor_at_end_and_append_block(irb, ok_block); - IrInstSrc *unwrapped_ptr = ir_build_unwrap_err_payload_src(irb, parent_scope, node, err_union_ptr, false, false); - IrInstSrc *unwrapped_payload = ir_build_load_ptr(irb, parent_scope, node, unwrapped_ptr); - ir_build_end_expr(irb, parent_scope, node, unwrapped_payload, &peer_parent->peers.at(1)->base); - IrBasicBlockSrc *after_ok_block = irb->current_basic_block; - ir_build_br(irb, parent_scope, node, end_block, is_comptime); - - ir_set_cursor_at_end_and_append_block(irb, end_block); - IrInstSrc **incoming_values = heap::c_allocator.allocate(2); - incoming_values[0] = err_result; - incoming_values[1] = unwrapped_payload; - IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate(2); - incoming_blocks[0] = after_err_block; - incoming_blocks[1] = after_ok_block; - IrInstSrc *phi = ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values, peer_parent); - return ir_lval_wrap(irb, parent_scope, phi, lval, result_loc); -} - -static bool render_instance_name_recursive(CodeGen *codegen, Buf *name, Scope *outer_scope, Scope *inner_scope) { - if (inner_scope == nullptr || inner_scope == outer_scope) return false; - bool need_comma = render_instance_name_recursive(codegen, name, outer_scope, inner_scope->parent); - if (inner_scope->id != ScopeIdVarDecl) - return need_comma; - - ScopeVarDecl *var_scope = (ScopeVarDecl *)inner_scope; - if (need_comma) - buf_append_char(name, ','); - // TODO: const ptr reinterpret here to make the var type agree with the value? - render_const_value(codegen, name, var_scope->var->const_value); - return true; -} - -static Buf *get_anon_type_name(CodeGen *codegen, IrExecutableSrc *exec, const char *kind_name, - Scope *scope, AstNode *source_node, Buf *out_bare_name) -{ - if (exec != nullptr && exec->name) { - ZigType *import = get_scope_import(scope); - Buf *namespace_name = buf_alloc(); - append_namespace_qualification(codegen, namespace_name, import); - buf_append_buf(namespace_name, exec->name); - buf_init_from_buf(out_bare_name, exec->name); - return namespace_name; - } else if (exec != nullptr && exec->name_fn != nullptr) { - Buf *name = buf_alloc(); - buf_append_buf(name, &exec->name_fn->symbol_name); - buf_appendf(name, "("); - render_instance_name_recursive(codegen, name, &exec->name_fn->fndef_scope->base, exec->begin_scope); - buf_appendf(name, ")"); - buf_init_from_buf(out_bare_name, name); - return name; - } else { - ZigType *import = get_scope_import(scope); - Buf *namespace_name = buf_alloc(); - append_namespace_qualification(codegen, namespace_name, import); - buf_appendf(namespace_name, "%s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize, kind_name, - source_node->line + 1, source_node->column + 1); - buf_init_from_buf(out_bare_name, namespace_name); - return namespace_name; - } -} - -static IrInstSrc *ir_gen_container_decl(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node) { - assert(node->type == NodeTypeContainerDecl); - - ContainerKind kind = node->data.container_decl.kind; - Buf *bare_name = buf_alloc(); - Buf *name = get_anon_type_name(irb->codegen, irb->exec, container_string(kind), parent_scope, node, bare_name); - - ContainerLayout layout = node->data.container_decl.layout; - ZigType *container_type = get_partial_container_type(irb->codegen, parent_scope, - kind, node, buf_ptr(name), bare_name, layout); - ScopeDecls *child_scope = get_container_scope(container_type); - - for (size_t i = 0; i < node->data.container_decl.decls.length; i += 1) { - AstNode *child_node = node->data.container_decl.decls.at(i); - scan_decls(irb->codegen, child_scope, child_node); - } - - TldContainer *tld_container = heap::c_allocator.create(); - init_tld(&tld_container->base, TldIdContainer, bare_name, VisibModPub, node, parent_scope); - tld_container->type_entry = container_type; - tld_container->decls_scope = child_scope; - irb->codegen->resolve_queue.append(&tld_container->base); - - // Add this to the list to mark as invalid if analyzing this exec fails. - irb->exec->tld_list.append(&tld_container->base); - - return ir_build_const_type(irb, parent_scope, node, container_type); -} - -// errors should be populated with set1's values -static ZigType *get_error_set_union(CodeGen *g, ErrorTableEntry **errors, ZigType *set1, ZigType *set2, - Buf *type_name) -{ - assert(set1->id == ZigTypeIdErrorSet); - assert(set2->id == ZigTypeIdErrorSet); - - ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet); - 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; - if (type_name == nullptr) { - buf_resize(&err_set_type->name, 0); - buf_appendf(&err_set_type->name, "error{"); - } else { - buf_init_from_buf(&err_set_type->name, type_name); - } - - for (uint32_t i = 0, count = set1->data.error_set.err_count; i < count; i += 1) { - assert(errors[set1->data.error_set.errors[i]->value] == set1->data.error_set.errors[i]); - } - - uint32_t count = set1->data.error_set.err_count; - for (uint32_t i = 0; i < set2->data.error_set.err_count; i += 1) { - ErrorTableEntry *error_entry = set2->data.error_set.errors[i]; - if (errors[error_entry->value] == nullptr) { - count += 1; - } - } - - err_set_type->data.error_set.err_count = count; - err_set_type->data.error_set.errors = heap::c_allocator.allocate(count); - - bool need_comma = false; - for (uint32_t i = 0; i < set1->data.error_set.err_count; i += 1) { - ErrorTableEntry *error_entry = set1->data.error_set.errors[i]; - if (type_name == nullptr) { - const char *comma = need_comma ? "," : ""; - need_comma = true; - buf_appendf(&err_set_type->name, "%s%s", comma, buf_ptr(&error_entry->name)); - } - err_set_type->data.error_set.errors[i] = error_entry; - } - - uint32_t index = set1->data.error_set.err_count; - for (uint32_t i = 0; i < set2->data.error_set.err_count; i += 1) { - ErrorTableEntry *error_entry = set2->data.error_set.errors[i]; - if (errors[error_entry->value] == nullptr) { - errors[error_entry->value] = error_entry; - if (type_name == nullptr) { - const char *comma = need_comma ? "," : ""; - need_comma = true; - buf_appendf(&err_set_type->name, "%s%s", comma, buf_ptr(&error_entry->name)); - } - err_set_type->data.error_set.errors[index] = error_entry; - index += 1; - } - } - assert(index == count); - - if (type_name == nullptr) { - buf_appendf(&err_set_type->name, "}"); - } - - return err_set_type; - -} - -static ZigType *make_err_set_with_one_item(CodeGen *g, Scope *parent_scope, AstNode *node, - ErrorTableEntry *err_entry) -{ - ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet); - buf_resize(&err_set_type->name, 0); - buf_appendf(&err_set_type->name, "error{%s}", buf_ptr(&err_entry->name)); - 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; - err_set_type->data.error_set.err_count = 1; - err_set_type->data.error_set.errors = heap::c_allocator.create(); - - err_set_type->data.error_set.errors[0] = err_entry; - - return err_set_type; -} - -static AstNode *ast_field_to_symbol_node(AstNode *err_set_field_node) { - if (err_set_field_node->type == NodeTypeSymbol) { - return err_set_field_node; - } else if (err_set_field_node->type == NodeTypeErrorSetField) { - assert(err_set_field_node->data.err_set_field.field_name->type == NodeTypeSymbol); - return err_set_field_node->data.err_set_field.field_name; - } else { - return err_set_field_node; - } -} - -static IrInstSrc *ir_gen_err_set_decl(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node) { - assert(node->type == NodeTypeErrorSetDecl); - - uint32_t err_count = node->data.err_set_decl.decls.length; - - Buf bare_name = BUF_INIT; - Buf *type_name = get_anon_type_name(irb->codegen, irb->exec, "error", parent_scope, node, &bare_name); - ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet); - buf_init_from_buf(&err_set_type->name, type_name); - err_set_type->data.error_set.err_count = err_count; - err_set_type->size_in_bits = irb->codegen->builtin_types.entry_global_error_set->size_in_bits; - err_set_type->abi_align = irb->codegen->builtin_types.entry_global_error_set->abi_align; - err_set_type->abi_size = irb->codegen->builtin_types.entry_global_error_set->abi_size; - err_set_type->data.error_set.errors = heap::c_allocator.allocate(err_count); - - size_t errors_count = irb->codegen->errors_by_index.length + err_count; - ErrorTableEntry **errors = heap::c_allocator.allocate(errors_count); - - for (uint32_t i = 0; i < err_count; i += 1) { - AstNode *field_node = node->data.err_set_decl.decls.at(i); - AstNode *symbol_node = ast_field_to_symbol_node(field_node); - Buf *err_name = symbol_node->data.symbol_expr.symbol; - ErrorTableEntry *err = heap::c_allocator.create(); - err->decl_node = field_node; - buf_init_from_buf(&err->name, err_name); - - auto existing_entry = irb->codegen->error_table.put_unique(err_name, err); - if (existing_entry) { - err->value = existing_entry->value->value; - } else { - size_t error_value_count = irb->codegen->errors_by_index.length; - assert((uint32_t)error_value_count < (((uint32_t)1) << (uint32_t)irb->codegen->err_tag_type->data.integral.bit_count)); - err->value = error_value_count; - irb->codegen->errors_by_index.append(err); - } - err_set_type->data.error_set.errors[i] = err; - - ErrorTableEntry *prev_err = errors[err->value]; - if (prev_err != nullptr) { - ErrorMsg *msg = add_node_error(irb->codegen, ast_field_to_symbol_node(err->decl_node), - buf_sprintf("duplicate error: '%s'", buf_ptr(&err->name))); - add_error_note(irb->codegen, msg, ast_field_to_symbol_node(prev_err->decl_node), - buf_sprintf("other error here")); - return irb->codegen->invalid_inst_src; - } - errors[err->value] = err; - } - heap::c_allocator.deallocate(errors, errors_count); - return ir_build_const_type(irb, parent_scope, node, err_set_type); -} - -static IrInstSrc *ir_gen_fn_proto(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node) { - assert(node->type == NodeTypeFnProto); - - size_t param_count = node->data.fn_proto.params.length; - IrInstSrc **param_types = heap::c_allocator.allocate(param_count); - - bool is_var_args = false; - for (size_t i = 0; i < param_count; i += 1) { - AstNode *param_node = node->data.fn_proto.params.at(i); - if (param_node->data.param_decl.is_var_args) { - is_var_args = true; - break; - } - if (param_node->data.param_decl.anytype_token == nullptr) { - AstNode *type_node = param_node->data.param_decl.type; - IrInstSrc *type_value = ir_gen_node(irb, type_node, parent_scope); - if (type_value == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - param_types[i] = type_value; - } else { - param_types[i] = nullptr; - } - } - - IrInstSrc *align_value = nullptr; - if (node->data.fn_proto.align_expr != nullptr) { - align_value = ir_gen_node(irb, node->data.fn_proto.align_expr, parent_scope); - if (align_value == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - } - - IrInstSrc *callconv_value = nullptr; - if (node->data.fn_proto.callconv_expr != nullptr) { - callconv_value = ir_gen_node(irb, node->data.fn_proto.callconv_expr, parent_scope); - if (callconv_value == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - } - - IrInstSrc *return_type; - if (node->data.fn_proto.return_anytype_token == nullptr) { - if (node->data.fn_proto.return_type == nullptr) { - return_type = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_void); - } else { - return_type = ir_gen_node(irb, node->data.fn_proto.return_type, parent_scope); - if (return_type == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - } - } else { - add_node_error(irb->codegen, node, - buf_sprintf("TODO implement inferred return types https://github.com/ziglang/zig/issues/447")); - return irb->codegen->invalid_inst_src; - //return_type = nullptr; - } - - return ir_build_fn_proto(irb, parent_scope, node, param_types, align_value, callconv_value, return_type, is_var_args); -} - -static IrInstSrc *ir_gen_resume(IrBuilderSrc *irb, Scope *scope, AstNode *node) { - assert(node->type == NodeTypeResume); - if (get_scope_nosuspend(scope) != nullptr) { - add_node_error(irb->codegen, node, buf_sprintf("resume in nosuspend scope")); - return irb->codegen->invalid_inst_src; - } - - IrInstSrc *target_inst = ir_gen_node_extra(irb, node->data.resume_expr.expr, scope, LValPtr, nullptr); - if (target_inst == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - - return ir_build_resume_src(irb, scope, node, target_inst); -} - -static IrInstSrc *ir_gen_await_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, - ResultLoc *result_loc) -{ - assert(node->type == NodeTypeAwaitExpr); - - bool is_nosuspend = get_scope_nosuspend(scope) != nullptr; - - AstNode *expr_node = node->data.await_expr.expr; - if (expr_node->type == NodeTypeFnCallExpr && expr_node->data.fn_call_expr.modifier == CallModifierBuiltin) { - AstNode *fn_ref_expr = expr_node->data.fn_call_expr.fn_ref_expr; - Buf *name = fn_ref_expr->data.symbol_expr.symbol; - auto entry = irb->codegen->builtin_fn_table.maybe_get(name); - if (entry != nullptr) { - BuiltinFnEntry *builtin_fn = entry->value; - if (builtin_fn->id == BuiltinFnIdAsyncCall) { - return ir_gen_async_call(irb, scope, node, expr_node, lval, result_loc); - } - } - } - - ZigFn *fn_entry = exec_fn_entry(irb->exec); - if (!fn_entry) { - add_node_error(irb->codegen, node, buf_sprintf("await outside function definition")); - return irb->codegen->invalid_inst_src; - } - ScopeSuspend *existing_suspend_scope = get_scope_suspend(scope); - if (existing_suspend_scope) { - if (!existing_suspend_scope->reported_err) { - ErrorMsg *msg = add_node_error(irb->codegen, node, buf_sprintf("cannot await inside suspend block")); - add_error_note(irb->codegen, msg, existing_suspend_scope->base.source_node, buf_sprintf("suspend block here")); - existing_suspend_scope->reported_err = true; - } - return irb->codegen->invalid_inst_src; - } - - IrInstSrc *target_inst = ir_gen_node_extra(irb, expr_node, scope, LValPtr, nullptr); - if (target_inst == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - - IrInstSrc *await_inst = ir_build_await_src(irb, scope, node, target_inst, result_loc, is_nosuspend); - return ir_lval_wrap(irb, scope, await_inst, lval, result_loc); -} - -static IrInstSrc *ir_gen_suspend(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node) { - assert(node->type == NodeTypeSuspend); - - ZigFn *fn_entry = exec_fn_entry(irb->exec); - if (!fn_entry) { - add_node_error(irb->codegen, node, buf_sprintf("suspend outside function definition")); - return irb->codegen->invalid_inst_src; - } - if (get_scope_nosuspend(parent_scope) != nullptr) { - add_node_error(irb->codegen, node, buf_sprintf("suspend in nosuspend scope")); - return irb->codegen->invalid_inst_src; - } - - ScopeSuspend *existing_suspend_scope = get_scope_suspend(parent_scope); - if (existing_suspend_scope) { - if (!existing_suspend_scope->reported_err) { - ErrorMsg *msg = add_node_error(irb->codegen, node, buf_sprintf("cannot suspend inside suspend block")); - add_error_note(irb->codegen, msg, existing_suspend_scope->base.source_node, buf_sprintf("other suspend block here")); - existing_suspend_scope->reported_err = true; - } - return irb->codegen->invalid_inst_src; - } - - IrInstSrcSuspendBegin *begin = ir_build_suspend_begin_src(irb, parent_scope, node); - if (node->data.suspend.block != nullptr) { - ScopeSuspend *suspend_scope = create_suspend_scope(irb->codegen, node, parent_scope); - Scope *child_scope = &suspend_scope->base; - IrInstSrc *susp_res = ir_gen_node(irb, node->data.suspend.block, child_scope); - if (susp_res == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, node->data.suspend.block, susp_res)); - } - - return ir_mark_gen(ir_build_suspend_finish_src(irb, parent_scope, node, begin)); -} - -static IrInstSrc *ir_gen_node_raw(IrBuilderSrc *irb, AstNode *node, Scope *scope, - LVal lval, ResultLoc *result_loc) -{ - assert(scope); - switch (node->type) { - case NodeTypeStructValueField: - case NodeTypeParamDecl: - case NodeTypeUsingNamespace: - case NodeTypeSwitchProng: - case NodeTypeSwitchRange: - case NodeTypeStructField: - case NodeTypeErrorSetField: - case NodeTypeFnDef: - case NodeTypeTestDecl: - zig_unreachable(); - case NodeTypeBlock: - return ir_gen_block(irb, scope, node, lval, result_loc); - case NodeTypeGroupedExpr: - return ir_gen_node_raw(irb, node->data.grouped_expr, scope, lval, result_loc); - case NodeTypeBinOpExpr: - return ir_gen_bin_op(irb, scope, node, lval, result_loc); - case NodeTypeIntLiteral: - return ir_lval_wrap(irb, scope, ir_gen_int_lit(irb, scope, node), lval, result_loc); - case NodeTypeFloatLiteral: - return ir_lval_wrap(irb, scope, ir_gen_float_lit(irb, scope, node), lval, result_loc); - case NodeTypeCharLiteral: - return ir_lval_wrap(irb, scope, ir_gen_char_lit(irb, scope, node), lval, result_loc); - case NodeTypeSymbol: - return ir_gen_symbol(irb, scope, node, lval, result_loc); - case NodeTypeFnCallExpr: - return ir_gen_fn_call(irb, scope, node, lval, result_loc); - case NodeTypeIfBoolExpr: - return ir_gen_if_bool_expr(irb, scope, node, lval, result_loc); - case NodeTypePrefixOpExpr: - return ir_gen_prefix_op_expr(irb, scope, node, lval, result_loc); - case NodeTypeContainerInitExpr: - return ir_gen_container_init_expr(irb, scope, node, lval, result_loc); - case NodeTypeVariableDeclaration: - return ir_gen_var_decl(irb, scope, node); - case NodeTypeWhileExpr: - return ir_gen_while_expr(irb, scope, node, lval, result_loc); - case NodeTypeForExpr: - return ir_gen_for_expr(irb, scope, node, lval, result_loc); - case NodeTypeArrayAccessExpr: - return ir_gen_array_access(irb, scope, node, lval, result_loc); - case NodeTypeReturnExpr: - return ir_gen_return(irb, scope, node, lval, result_loc); - case NodeTypeFieldAccessExpr: - { - IrInstSrc *ptr_instruction = ir_gen_field_access(irb, scope, node); - if (ptr_instruction == irb->codegen->invalid_inst_src) - return ptr_instruction; - if (lval == LValPtr || lval == LValAssign) - return ptr_instruction; - - IrInstSrc *load_ptr = ir_build_load_ptr(irb, scope, node, ptr_instruction); - return ir_expr_wrap(irb, scope, load_ptr, result_loc); - } - case NodeTypePtrDeref: { - AstNode *expr_node = node->data.ptr_deref_expr.target; - - LVal child_lval = lval; - if (child_lval == LValAssign) - child_lval = LValPtr; - - IrInstSrc *value = ir_gen_node_extra(irb, expr_node, scope, child_lval, nullptr); - if (value == irb->codegen->invalid_inst_src) - return value; - - // We essentially just converted any lvalue from &(x.*) to (&x).*; - // this inhibits checking that x is a pointer later, so we directly - // record whether the pointer check is needed - IrInstSrc *un_op = ir_build_un_op_lval(irb, scope, node, IrUnOpDereference, value, lval, result_loc); - return ir_expr_wrap(irb, scope, un_op, result_loc); - } - case NodeTypeUnwrapOptional: { - AstNode *expr_node = node->data.unwrap_optional.expr; - - IrInstSrc *maybe_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr, nullptr); - if (maybe_ptr == irb->codegen->invalid_inst_src) - return irb->codegen->invalid_inst_src; - - IrInstSrc *unwrapped_ptr = ir_build_optional_unwrap_ptr(irb, scope, node, maybe_ptr, true ); - if (lval == LValPtr || lval == LValAssign) - return unwrapped_ptr; - - IrInstSrc *load_ptr = ir_build_load_ptr(irb, scope, node, unwrapped_ptr); - return ir_expr_wrap(irb, scope, load_ptr, result_loc); - } - case NodeTypeBoolLiteral: - return ir_lval_wrap(irb, scope, ir_gen_bool_literal(irb, scope, node), lval, result_loc); - case NodeTypeArrayType: - return ir_lval_wrap(irb, scope, ir_gen_array_type(irb, scope, node), lval, result_loc); - case NodeTypePointerType: - return ir_lval_wrap(irb, scope, ir_gen_pointer_type(irb, scope, node), lval, result_loc); - case NodeTypeAnyFrameType: - return ir_lval_wrap(irb, scope, ir_gen_anyframe_type(irb, scope, node), lval, result_loc); - case NodeTypeStringLiteral: - return ir_lval_wrap(irb, scope, ir_gen_string_literal(irb, scope, node), lval, result_loc); - case NodeTypeUndefinedLiteral: - return ir_lval_wrap(irb, scope, ir_gen_undefined_literal(irb, scope, node), lval, result_loc); - case NodeTypeAsmExpr: - return ir_lval_wrap(irb, scope, ir_gen_asm_expr(irb, scope, node), lval, result_loc); - case NodeTypeNullLiteral: - return ir_lval_wrap(irb, scope, ir_gen_null_literal(irb, scope, node), lval, result_loc); - case NodeTypeIfErrorExpr: - return ir_gen_if_err_expr(irb, scope, node, lval, result_loc); - case NodeTypeIfOptional: - return ir_gen_if_optional_expr(irb, scope, node, lval, result_loc); - case NodeTypeSwitchExpr: - return ir_gen_switch_expr(irb, scope, node, lval, result_loc); - case NodeTypeCompTime: - return ir_expr_wrap(irb, scope, ir_gen_comptime(irb, scope, node, lval), result_loc); - case NodeTypeNoSuspend: - return ir_expr_wrap(irb, scope, ir_gen_nosuspend(irb, scope, node, lval), result_loc); - case NodeTypeErrorType: - return ir_lval_wrap(irb, scope, ir_gen_error_type(irb, scope, node), lval, result_loc); - case NodeTypeBreak: - return ir_lval_wrap(irb, scope, ir_gen_break(irb, scope, node), lval, result_loc); - case NodeTypeContinue: - return ir_lval_wrap(irb, scope, ir_gen_continue(irb, scope, node), lval, result_loc); - case NodeTypeUnreachable: - return ir_build_unreachable(irb, scope, node); - case NodeTypeDefer: - return ir_lval_wrap(irb, scope, ir_gen_defer(irb, scope, node), lval, result_loc); - case NodeTypeSliceExpr: - return ir_gen_slice(irb, scope, node, lval, result_loc); - case NodeTypeCatchExpr: - return ir_gen_catch(irb, scope, node, lval, result_loc); - case NodeTypeContainerDecl: - return ir_lval_wrap(irb, scope, ir_gen_container_decl(irb, scope, node), lval, result_loc); - case NodeTypeFnProto: - return ir_lval_wrap(irb, scope, ir_gen_fn_proto(irb, scope, node), lval, result_loc); - case NodeTypeErrorSetDecl: - return ir_lval_wrap(irb, scope, ir_gen_err_set_decl(irb, scope, node), lval, result_loc); - case NodeTypeResume: - return ir_lval_wrap(irb, scope, ir_gen_resume(irb, scope, node), lval, result_loc); - case NodeTypeAwaitExpr: - return ir_gen_await_expr(irb, scope, node, lval, result_loc); - case NodeTypeSuspend: - return ir_lval_wrap(irb, scope, ir_gen_suspend(irb, scope, node), lval, result_loc); - case NodeTypeEnumLiteral: - return ir_lval_wrap(irb, scope, ir_gen_enum_literal(irb, scope, node), lval, result_loc); - case NodeTypeInferredArrayType: - add_node_error(irb->codegen, node, - buf_sprintf("inferred array size invalid here")); - return irb->codegen->invalid_inst_src; - case NodeTypeAnyTypeField: - return ir_lval_wrap(irb, scope, - ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_anytype), lval, result_loc); - } - zig_unreachable(); -} - -static ResultLoc *no_result_loc(void) { - ResultLocNone *result_loc_none = heap::c_allocator.create(); - result_loc_none->base.id = ResultLocIdNone; - return &result_loc_none->base; -} - -static IrInstSrc *ir_gen_node_extra(IrBuilderSrc *irb, AstNode *node, Scope *scope, LVal lval, - ResultLoc *result_loc) -{ - if (lval == LValAssign) { - switch (node->type) { - case NodeTypeStructValueField: - case NodeTypeParamDecl: - case NodeTypeUsingNamespace: - case NodeTypeSwitchProng: - case NodeTypeSwitchRange: - case NodeTypeStructField: - case NodeTypeErrorSetField: - case NodeTypeFnDef: - case NodeTypeTestDecl: - zig_unreachable(); - - // cannot be assigned to - case NodeTypeBlock: - case NodeTypeGroupedExpr: - case NodeTypeBinOpExpr: - case NodeTypeIntLiteral: - case NodeTypeFloatLiteral: - case NodeTypeCharLiteral: - case NodeTypeIfBoolExpr: - case NodeTypeContainerInitExpr: - case NodeTypeVariableDeclaration: - case NodeTypeWhileExpr: - case NodeTypeForExpr: - case NodeTypeReturnExpr: - case NodeTypeBoolLiteral: - case NodeTypeArrayType: - case NodeTypePointerType: - case NodeTypeAnyFrameType: - case NodeTypeStringLiteral: - case NodeTypeUndefinedLiteral: - case NodeTypeAsmExpr: - case NodeTypeNullLiteral: - case NodeTypeIfErrorExpr: - case NodeTypeIfOptional: - case NodeTypeSwitchExpr: - case NodeTypeCompTime: - case NodeTypeNoSuspend: - case NodeTypeErrorType: - case NodeTypeBreak: - case NodeTypeContinue: - case NodeTypeUnreachable: - case NodeTypeDefer: - case NodeTypeSliceExpr: - case NodeTypeCatchExpr: - case NodeTypeContainerDecl: - case NodeTypeFnProto: - case NodeTypeErrorSetDecl: - case NodeTypeResume: - case NodeTypeAwaitExpr: - case NodeTypeSuspend: - case NodeTypeEnumLiteral: - case NodeTypeInferredArrayType: - case NodeTypeAnyTypeField: - case NodeTypePrefixOpExpr: - add_node_error(irb->codegen, node, - buf_sprintf("invalid left-hand side to assignment")); - return irb->codegen->invalid_inst_src; - - // @field can be assigned to - case NodeTypeFnCallExpr: - if (node->data.fn_call_expr.modifier == CallModifierBuiltin) { - AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr; - Buf *name = fn_ref_expr->data.symbol_expr.symbol; - auto entry = irb->codegen->builtin_fn_table.maybe_get(name); - - if (!entry) { - add_node_error(irb->codegen, node, - buf_sprintf("invalid builtin function: '%s'", buf_ptr(name))); - return irb->codegen->invalid_inst_src; - } - - if (entry->value->id == BuiltinFnIdField) { - break; - } - } - add_node_error(irb->codegen, node, - buf_sprintf("invalid left-hand side to assignment")); - return irb->codegen->invalid_inst_src; - - - // can be assigned to - case NodeTypeUnwrapOptional: - case NodeTypePtrDeref: - case NodeTypeFieldAccessExpr: - case NodeTypeArrayAccessExpr: - case NodeTypeSymbol: - break; - } - } - if (result_loc == nullptr) { - // Create a result location indicating there is none - but if one gets created - // it will be properly distributed. - result_loc = no_result_loc(); - ir_build_reset_result(irb, scope, node, result_loc); - } - Scope *child_scope; - if (irb->exec->is_inline || - (irb->exec->fn_entry != nullptr && irb->exec->fn_entry->child_scope == scope)) - { - child_scope = scope; - } else { - child_scope = &create_expr_scope(irb->codegen, node, scope)->base; - } - IrInstSrc *result = ir_gen_node_raw(irb, node, child_scope, lval, result_loc); - if (result == irb->codegen->invalid_inst_src) { - if (irb->exec->first_err_trace_msg == nullptr) { - irb->exec->first_err_trace_msg = irb->codegen->trace_err; - } - } - return result; -} - -static IrInstSrc *ir_gen_node(IrBuilderSrc *irb, AstNode *node, Scope *scope) { - return ir_gen_node_extra(irb, node, scope, LValNone, nullptr); -} - -static void invalidate_exec(IrExecutableSrc *exec, ErrorMsg *msg) { - if (exec->first_err_trace_msg != nullptr) - return; - - exec->first_err_trace_msg = msg; - - for (size_t i = 0; i < exec->tld_list.length; i += 1) { - exec->tld_list.items[i]->resolution = TldResolutionInvalid; - } -} - -static void invalidate_exec_gen(IrExecutableGen *exec, ErrorMsg *msg) { - if (exec->first_err_trace_msg != nullptr) - return; - - exec->first_err_trace_msg = msg; - - for (size_t i = 0; i < exec->tld_list.length; i += 1) { - exec->tld_list.items[i]->resolution = TldResolutionInvalid; - } - - if (exec->source_exec != nullptr) - invalidate_exec(exec->source_exec, msg); -} - - -bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutableSrc *ir_executable) { - assert(node->owner); - - IrBuilderSrc ir_builder = {0}; - IrBuilderSrc *irb = &ir_builder; - - irb->codegen = codegen; - irb->exec = ir_executable; - irb->main_block_node = node; - - IrBasicBlockSrc *entry_block = ir_create_basic_block(irb, scope, "Entry"); - ir_set_cursor_at_end_and_append_block(irb, entry_block); - // Entry block gets a reference because we enter it to begin. - ir_ref_bb(irb->current_basic_block); - - IrInstSrc *result = ir_gen_node_extra(irb, node, scope, LValNone, nullptr); - - if (result == irb->codegen->invalid_inst_src) - return false; - - if (irb->exec->first_err_trace_msg != nullptr) { - codegen->trace_err = irb->exec->first_err_trace_msg; - return false; - } - - if (!instr_is_unreachable(result)) { - ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, result->base.source_node, result, nullptr)); - // no need for save_err_ret_addr because this cannot return error - ResultLocReturn *result_loc_ret = heap::c_allocator.create(); - result_loc_ret->base.id = ResultLocIdReturn; - ir_build_reset_result(irb, scope, node, &result_loc_ret->base); - ir_mark_gen(ir_build_end_expr(irb, scope, node, result, &result_loc_ret->base)); - ir_mark_gen(ir_build_return_src(irb, scope, result->base.source_node, result)); - } - - return true; -} - -bool ir_gen_fn(CodeGen *codegen, ZigFn *fn_entry) { - assert(fn_entry); - - IrExecutableSrc *ir_executable = fn_entry->ir_executable; - AstNode *body_node = fn_entry->body_node; - - assert(fn_entry->child_scope); - - return ir_gen(codegen, body_node, fn_entry->child_scope, ir_executable); -} - -static void ir_add_call_stack_errors_gen(CodeGen *codegen, IrExecutableGen *exec, ErrorMsg *err_msg, int limit) { - if (!exec || !exec->source_node || limit < 0) return; - add_error_note(codegen, err_msg, exec->source_node, buf_sprintf("called from here")); - - ir_add_call_stack_errors_gen(codegen, exec->parent_exec, err_msg, limit - 1); -} - -static void ir_add_call_stack_errors(CodeGen *codegen, IrExecutableSrc *exec, ErrorMsg *err_msg, int limit) { - if (!exec || !exec->source_node || limit < 0) return; - add_error_note(codegen, err_msg, exec->source_node, buf_sprintf("called from here")); - - ir_add_call_stack_errors_gen(codegen, exec->parent_exec, err_msg, limit - 1); -} - -static ErrorMsg *exec_add_error_node(CodeGen *codegen, IrExecutableSrc *exec, AstNode *source_node, Buf *msg) { - ErrorMsg *err_msg = add_node_error(codegen, source_node, msg); - invalidate_exec(exec, err_msg); - if (exec->parent_exec) { - ir_add_call_stack_errors(codegen, exec, err_msg, 10); - } - return err_msg; -} - -static ErrorMsg *exec_add_error_node_gen(CodeGen *codegen, IrExecutableGen *exec, AstNode *source_node, Buf *msg) { - ErrorMsg *err_msg = add_node_error(codegen, source_node, msg); - invalidate_exec_gen(exec, err_msg); - if (exec->parent_exec) { - ir_add_call_stack_errors_gen(codegen, exec, err_msg, 10); - } - return err_msg; -} - -static ErrorMsg *ir_add_error_node(IrAnalyze *ira, AstNode *source_node, Buf *msg) { - return exec_add_error_node_gen(ira->codegen, ira->new_irb.exec, source_node, msg); -} - -static ErrorMsg *opt_ir_add_error_node(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, Buf *msg) { - if (ira != nullptr) - return exec_add_error_node_gen(codegen, ira->new_irb.exec, source_node, msg); - else - return add_node_error(codegen, source_node, msg); -} - -static ErrorMsg *ir_add_error(IrAnalyze *ira, IrInst *source_instruction, Buf *msg) { - return ir_add_error_node(ira, source_instruction->source_node, msg); -} - -static void ir_assert_impl(bool ok, IrInst *source_instruction, char const *file, unsigned int line) { - if (ok) return; - src_assert_impl(ok, source_instruction->source_node, file, line); -} - -static void ir_assert_gen_impl(bool ok, IrInstGen *source_instruction, char const *file, unsigned int line) { - if (ok) return; - src_assert_impl(ok, source_instruction->base.source_node, file, line); -} - -// This function takes a comptime ptr and makes the child const value conform to the type -// described by the pointer. -static Error eval_comptime_ptr_reinterpret(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, - ZigValue *ptr_val) -{ - Error err; - assert(ptr_val->type->id == ZigTypeIdPointer); - assert(ptr_val->special == ConstValSpecialStatic); - ZigValue tmp = {}; - tmp.special = ConstValSpecialStatic; - tmp.type = ptr_val->type->data.pointer.child_type; - if ((err = ir_read_const_ptr(ira, codegen, source_node, &tmp, ptr_val))) - return err; - ZigValue *child_val = const_ptr_pointee_unchecked(codegen, ptr_val); - copy_const_val(codegen, child_val, &tmp); - return ErrorNone; -} - -ZigValue *const_ptr_pointee(IrAnalyze *ira, CodeGen *codegen, ZigValue *const_val, - AstNode *source_node) -{ - Error err; - ZigValue *val = const_ptr_pointee_unchecked(codegen, const_val); - if (val == nullptr) return nullptr; - assert(const_val->type->id == ZigTypeIdPointer); - ZigType *expected_type = const_val->type->data.pointer.child_type; - if (expected_type == codegen->builtin_types.entry_anytype) { - return val; - } - switch (type_has_one_possible_value(codegen, expected_type)) { - case OnePossibleValueInvalid: - return nullptr; - case OnePossibleValueNo: - break; - case OnePossibleValueYes: - return get_the_one_possible_value(codegen, expected_type); - } - if (!types_have_same_zig_comptime_repr(codegen, expected_type, val->type)) { - if ((err = eval_comptime_ptr_reinterpret(ira, codegen, source_node, const_val))) - return nullptr; - return const_ptr_pointee_unchecked(codegen, const_val); - } - return val; -} - -static Error ir_exec_scan_for_side_effects(CodeGen *codegen, IrExecutableGen *exec) { - IrBasicBlockGen *bb = exec->basic_block_list.at(0); - for (size_t i = 0; i < bb->instruction_list.length; i += 1) { - IrInstGen *instruction = bb->instruction_list.at(i); - if (instruction->id == IrInstGenIdReturn) { - return ErrorNone; - } else if (ir_inst_gen_has_side_effects(instruction)) { - if (instr_is_comptime(instruction)) { - switch (instruction->id) { - case IrInstGenIdUnwrapErrPayload: - case IrInstGenIdOptionalUnwrapPtr: - case IrInstGenIdUnionFieldPtr: - continue; - default: - break; - } - } - if (get_scope_typeof(instruction->base.scope) != nullptr) { - // doesn't count, it's inside a @TypeOf() - continue; - } - exec_add_error_node_gen(codegen, exec, instruction->base.source_node, - buf_sprintf("unable to evaluate constant expression")); - return ErrorSemanticAnalyzeFail; - } - } - zig_unreachable(); -} - -static bool ir_emit_global_runtime_side_effect(IrAnalyze *ira, IrInst* source_instruction) { - if (ir_should_inline(ira->old_irb.exec, source_instruction->scope)) { - ir_add_error(ira, source_instruction, buf_sprintf("unable to evaluate constant expression")); - return false; - } - return true; -} - -static bool const_val_fits_in_num_lit(ZigValue *const_val, ZigType *num_lit_type) { - return ((num_lit_type->id == ZigTypeIdComptimeFloat && - (const_val->type->id == ZigTypeIdFloat || const_val->type->id == ZigTypeIdComptimeFloat)) || - (num_lit_type->id == ZigTypeIdComptimeInt && - (const_val->type->id == ZigTypeIdInt || const_val->type->id == ZigTypeIdComptimeInt))); -} - -static bool float_has_fraction(ZigValue *const_val) { - if (const_val->type->id == ZigTypeIdComptimeFloat) { - return bigfloat_has_fraction(&const_val->data.x_bigfloat); - } else if (const_val->type->id == ZigTypeIdFloat) { - switch (const_val->type->data.floating.bit_count) { - case 16: - { - float16_t floored = f16_roundToInt(const_val->data.x_f16, softfloat_round_minMag, false); - return !f16_eq(floored, const_val->data.x_f16); - } - case 32: - return floorf(const_val->data.x_f32) != const_val->data.x_f32; - case 64: - return floor(const_val->data.x_f64) != const_val->data.x_f64; - case 128: - { - float128_t floored; - f128M_roundToInt(&const_val->data.x_f128, softfloat_round_minMag, false, &floored); - return !f128M_eq(&floored, &const_val->data.x_f128); - } - default: - zig_unreachable(); - } - } else { - zig_unreachable(); - } -} - -static void float_append_buf(Buf *buf, ZigValue *const_val) { - if (const_val->type->id == ZigTypeIdComptimeFloat) { - bigfloat_append_buf(buf, &const_val->data.x_bigfloat); - } else if (const_val->type->id == ZigTypeIdFloat) { - switch (const_val->type->data.floating.bit_count) { - case 16: - buf_appendf(buf, "%f", zig_f16_to_double(const_val->data.x_f16)); - break; - case 32: - buf_appendf(buf, "%f", const_val->data.x_f32); - break; - case 64: - buf_appendf(buf, "%f", const_val->data.x_f64); - break; - case 128: - { - // TODO actual implementation - 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)); - - int len = snprintf(buf_ptr(buf) + old_len, extra_len, "%f", double_value); - assert(len > 0); - buf_resize(buf, old_len + len); - break; - } - default: - zig_unreachable(); - } - } else { - zig_unreachable(); - } -} - -static void float_init_bigint(BigInt *bigint, ZigValue *const_val) { - if (const_val->type->id == ZigTypeIdComptimeFloat) { - bigint_init_bigfloat(bigint, &const_val->data.x_bigfloat); - } else if (const_val->type->id == ZigTypeIdFloat) { - switch (const_val->type->data.floating.bit_count) { - case 16: - { - double x = zig_f16_to_double(const_val->data.x_f16); - if (x >= 0) { - bigint_init_unsigned(bigint, (uint64_t)x); - } else { - bigint_init_unsigned(bigint, (uint64_t)-x); - bigint->is_negative = true; - } - break; - } - case 32: - if (const_val->data.x_f32 >= 0) { - bigint_init_unsigned(bigint, (uint64_t)(const_val->data.x_f32)); - } else { - bigint_init_unsigned(bigint, (uint64_t)(-const_val->data.x_f32)); - bigint->is_negative = true; - } - break; - case 64: - if (const_val->data.x_f64 >= 0) { - bigint_init_unsigned(bigint, (uint64_t)(const_val->data.x_f64)); - } else { - bigint_init_unsigned(bigint, (uint64_t)(-const_val->data.x_f64)); - bigint->is_negative = true; - } - break; - case 128: - { - BigFloat tmp_float; - bigfloat_init_128(&tmp_float, const_val->data.x_f128); - bigint_init_bigfloat(bigint, &tmp_float); - } - break; - default: - zig_unreachable(); - } - } else { - zig_unreachable(); - } -} - -static void float_init_bigfloat(ZigValue *dest_val, BigFloat *bigfloat) { - if (dest_val->type->id == ZigTypeIdComptimeFloat) { - bigfloat_init_bigfloat(&dest_val->data.x_bigfloat, bigfloat); - } else if (dest_val->type->id == ZigTypeIdFloat) { - switch (dest_val->type->data.floating.bit_count) { - case 16: - dest_val->data.x_f16 = bigfloat_to_f16(bigfloat); - break; - case 32: - dest_val->data.x_f32 = bigfloat_to_f32(bigfloat); - break; - case 64: - dest_val->data.x_f64 = bigfloat_to_f64(bigfloat); - break; - case 80: - zig_panic("TODO"); - case 128: - dest_val->data.x_f128 = bigfloat_to_f128(bigfloat); - break; - default: - zig_unreachable(); - } - } else { - zig_unreachable(); - } -} - -static void float_init_f16(ZigValue *dest_val, float16_t x) { - if (dest_val->type->id == ZigTypeIdComptimeFloat) { - bigfloat_init_16(&dest_val->data.x_bigfloat, x); - } else if (dest_val->type->id == ZigTypeIdFloat) { - switch (dest_val->type->data.floating.bit_count) { - case 16: - dest_val->data.x_f16 = x; - break; - case 32: - dest_val->data.x_f32 = zig_f16_to_double(x); - break; - case 64: - dest_val->data.x_f64 = zig_f16_to_double(x); - break; - case 128: - f16_to_f128M(x, &dest_val->data.x_f128); - break; - default: - zig_unreachable(); - } - } else { - zig_unreachable(); - } -} - -static void float_init_f32(ZigValue *dest_val, float x) { - if (dest_val->type->id == ZigTypeIdComptimeFloat) { - bigfloat_init_32(&dest_val->data.x_bigfloat, x); - } else if (dest_val->type->id == ZigTypeIdFloat) { - switch (dest_val->type->data.floating.bit_count) { - case 16: - dest_val->data.x_f16 = zig_double_to_f16(x); - break; - case 32: - dest_val->data.x_f32 = x; - break; - case 64: - dest_val->data.x_f64 = x; - break; - case 128: - { - float32_t x_f32; - memcpy(&x_f32, &x, sizeof(float)); - f32_to_f128M(x_f32, &dest_val->data.x_f128); - break; - } - default: - zig_unreachable(); - } - } else { - zig_unreachable(); - } -} - -static void float_init_f64(ZigValue *dest_val, double x) { - if (dest_val->type->id == ZigTypeIdComptimeFloat) { - bigfloat_init_64(&dest_val->data.x_bigfloat, x); - } else if (dest_val->type->id == ZigTypeIdFloat) { - switch (dest_val->type->data.floating.bit_count) { - case 16: - dest_val->data.x_f16 = zig_double_to_f16(x); - break; - case 32: - dest_val->data.x_f32 = x; - break; - case 64: - dest_val->data.x_f64 = x; - break; - case 128: - { - float64_t x_f64; - memcpy(&x_f64, &x, sizeof(double)); - f64_to_f128M(x_f64, &dest_val->data.x_f128); - break; - } - default: - zig_unreachable(); - } - } else { - zig_unreachable(); - } -} - -static void float_init_f128(ZigValue *dest_val, float128_t x) { - if (dest_val->type->id == ZigTypeIdComptimeFloat) { - bigfloat_init_128(&dest_val->data.x_bigfloat, x); - } else if (dest_val->type->id == ZigTypeIdFloat) { - switch (dest_val->type->data.floating.bit_count) { - case 16: - dest_val->data.x_f16 = f128M_to_f16(&x); - break; - case 32: - { - float32_t f32_val = f128M_to_f32(&x); - memcpy(&dest_val->data.x_f32, &f32_val, sizeof(float)); - break; - } - case 64: - { - float64_t f64_val = f128M_to_f64(&x); - memcpy(&dest_val->data.x_f64, &f64_val, sizeof(double)); - break; - } - case 128: - { - memcpy(&dest_val->data.x_f128, &x, sizeof(float128_t)); - break; - } - default: - zig_unreachable(); - } - } else { - zig_unreachable(); - } -} - -static void float_init_float(ZigValue *dest_val, ZigValue *src_val) { - if (src_val->type->id == ZigTypeIdComptimeFloat) { - float_init_bigfloat(dest_val, &src_val->data.x_bigfloat); - } else if (src_val->type->id == ZigTypeIdFloat) { - switch (src_val->type->data.floating.bit_count) { - case 16: - float_init_f16(dest_val, src_val->data.x_f16); - break; - case 32: - float_init_f32(dest_val, src_val->data.x_f32); - break; - case 64: - float_init_f64(dest_val, src_val->data.x_f64); - break; - case 128: - float_init_f128(dest_val, src_val->data.x_f128); - break; - default: - zig_unreachable(); - } - } else { - zig_unreachable(); - } -} - -static bool float_is_nan(ZigValue *op) { - if (op->type->id == ZigTypeIdComptimeFloat) { - return bigfloat_is_nan(&op->data.x_bigfloat); - } else if (op->type->id == ZigTypeIdFloat) { - switch (op->type->data.floating.bit_count) { - case 16: - return f16_isSignalingNaN(op->data.x_f16); - case 32: - return op->data.x_f32 != op->data.x_f32; - case 64: - return op->data.x_f64 != op->data.x_f64; - case 128: - return f128M_isSignalingNaN(&op->data.x_f128); - default: - zig_unreachable(); - } - } else { - zig_unreachable(); - } -} - -static Cmp float_cmp(ZigValue *op1, ZigValue *op2) { - if (op1->type == op2->type) { - if (op1->type->id == ZigTypeIdComptimeFloat) { - return bigfloat_cmp(&op1->data.x_bigfloat, &op2->data.x_bigfloat); - } else if (op1->type->id == ZigTypeIdFloat) { - switch (op1->type->data.floating.bit_count) { - case 16: - if (f16_lt(op1->data.x_f16, op2->data.x_f16)) { - return CmpLT; - } else if (f16_lt(op2->data.x_f16, op1->data.x_f16)) { - return CmpGT; - } else { - return CmpEQ; - } - case 32: - if (op1->data.x_f32 > op2->data.x_f32) { - return CmpGT; - } else if (op1->data.x_f32 < op2->data.x_f32) { - return CmpLT; - } else { - return CmpEQ; - } - case 64: - if (op1->data.x_f64 > op2->data.x_f64) { - return CmpGT; - } else if (op1->data.x_f64 < op2->data.x_f64) { - return CmpLT; - } else { - return CmpEQ; - } - case 128: - if (f128M_lt(&op1->data.x_f128, &op2->data.x_f128)) { - return CmpLT; - } else if (f128M_eq(&op1->data.x_f128, &op2->data.x_f128)) { - return CmpEQ; - } else { - return CmpGT; - } - default: - zig_unreachable(); - } - } else { - zig_unreachable(); - } - } - BigFloat op1_big; - BigFloat op2_big; - value_to_bigfloat(&op1_big, op1); - value_to_bigfloat(&op2_big, op2); - return bigfloat_cmp(&op1_big, &op2_big); -} - -// This function cannot handle NaN -static Cmp float_cmp_zero(ZigValue *op) { - if (op->type->id == ZigTypeIdComptimeFloat) { - return bigfloat_cmp_zero(&op->data.x_bigfloat); - } else if (op->type->id == ZigTypeIdFloat) { - switch (op->type->data.floating.bit_count) { - case 16: - { - const float16_t zero = zig_double_to_f16(0); - if (f16_lt(op->data.x_f16, zero)) { - return CmpLT; - } else if (f16_lt(zero, op->data.x_f16)) { - return CmpGT; - } else { - return CmpEQ; - } - } - case 32: - if (op->data.x_f32 < 0.0) { - return CmpLT; - } else if (op->data.x_f32 > 0.0) { - return CmpGT; - } else { - return CmpEQ; - } - case 64: - if (op->data.x_f64 < 0.0) { - return CmpLT; - } else if (op->data.x_f64 > 0.0) { - return CmpGT; - } else { - return CmpEQ; - } - case 128: - float128_t zero_float; - ui32_to_f128M(0, &zero_float); - if (f128M_lt(&op->data.x_f128, &zero_float)) { - return CmpLT; - } else if (f128M_eq(&op->data.x_f128, &zero_float)) { - return CmpEQ; - } else { - return CmpGT; - } - default: - zig_unreachable(); - } - } else { - zig_unreachable(); - } -} - -static void float_add(ZigValue *out_val, ZigValue *op1, ZigValue *op2) { - assert(op1->type == op2->type); - out_val->type = op1->type; - if (op1->type->id == ZigTypeIdComptimeFloat) { - bigfloat_add(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); - } else if (op1->type->id == ZigTypeIdFloat) { - switch (op1->type->data.floating.bit_count) { - case 16: - out_val->data.x_f16 = f16_add(op1->data.x_f16, op2->data.x_f16); - return; - case 32: - out_val->data.x_f32 = op1->data.x_f32 + op2->data.x_f32; - return; - case 64: - out_val->data.x_f64 = op1->data.x_f64 + op2->data.x_f64; - return; - case 128: - f128M_add(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); - return; - default: - zig_unreachable(); - } - } else { - zig_unreachable(); - } -} - -static void float_sub(ZigValue *out_val, ZigValue *op1, ZigValue *op2) { - assert(op1->type == op2->type); - out_val->type = op1->type; - if (op1->type->id == ZigTypeIdComptimeFloat) { - bigfloat_sub(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); - } else if (op1->type->id == ZigTypeIdFloat) { - switch (op1->type->data.floating.bit_count) { - case 16: - out_val->data.x_f16 = f16_sub(op1->data.x_f16, op2->data.x_f16); - return; - case 32: - out_val->data.x_f32 = op1->data.x_f32 - op2->data.x_f32; - return; - case 64: - out_val->data.x_f64 = op1->data.x_f64 - op2->data.x_f64; - return; - case 128: - f128M_sub(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); - return; - default: - zig_unreachable(); - } - } else { - zig_unreachable(); - } -} - -static void float_mul(ZigValue *out_val, ZigValue *op1, ZigValue *op2) { - assert(op1->type == op2->type); - out_val->type = op1->type; - if (op1->type->id == ZigTypeIdComptimeFloat) { - bigfloat_mul(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); - } else if (op1->type->id == ZigTypeIdFloat) { - switch (op1->type->data.floating.bit_count) { - case 16: - out_val->data.x_f16 = f16_mul(op1->data.x_f16, op2->data.x_f16); - return; - case 32: - out_val->data.x_f32 = op1->data.x_f32 * op2->data.x_f32; - return; - case 64: - out_val->data.x_f64 = op1->data.x_f64 * op2->data.x_f64; - return; - case 128: - f128M_mul(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); - return; - default: - zig_unreachable(); - } - } else { - zig_unreachable(); - } -} - -static void float_div(ZigValue *out_val, ZigValue *op1, ZigValue *op2) { - assert(op1->type == op2->type); - out_val->type = op1->type; - if (op1->type->id == ZigTypeIdComptimeFloat) { - bigfloat_div(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); - } else if (op1->type->id == ZigTypeIdFloat) { - switch (op1->type->data.floating.bit_count) { - case 16: - out_val->data.x_f16 = f16_div(op1->data.x_f16, op2->data.x_f16); - return; - case 32: - out_val->data.x_f32 = op1->data.x_f32 / op2->data.x_f32; - return; - case 64: - out_val->data.x_f64 = op1->data.x_f64 / op2->data.x_f64; - return; - case 128: - f128M_div(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); - return; - default: - zig_unreachable(); - } - } else { - zig_unreachable(); - } -} - -static void float_div_trunc(ZigValue *out_val, ZigValue *op1, ZigValue *op2) { - assert(op1->type == op2->type); - out_val->type = op1->type; - if (op1->type->id == ZigTypeIdComptimeFloat) { - bigfloat_div_trunc(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); - } else if (op1->type->id == ZigTypeIdFloat) { - switch (op1->type->data.floating.bit_count) { - case 16: - out_val->data.x_f16 = f16_div(op1->data.x_f16, op2->data.x_f16); - out_val->data.x_f16 = f16_roundToInt(out_val->data.x_f16, softfloat_round_minMag, false); - return; - case 32: - out_val->data.x_f32 = truncf(op1->data.x_f32 / op2->data.x_f32); - return; - case 64: - out_val->data.x_f64 = trunc(op1->data.x_f64 / op2->data.x_f64); - return; - case 128: - f128M_div(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); - f128M_roundToInt(&out_val->data.x_f128, softfloat_round_minMag, false, &out_val->data.x_f128); - return; - default: - zig_unreachable(); - } - } else { - zig_unreachable(); - } -} - -static void float_div_floor(ZigValue *out_val, ZigValue *op1, ZigValue *op2) { - assert(op1->type == op2->type); - out_val->type = op1->type; - if (op1->type->id == ZigTypeIdComptimeFloat) { - bigfloat_div_floor(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); - } else if (op1->type->id == ZigTypeIdFloat) { - switch (op1->type->data.floating.bit_count) { - case 16: - out_val->data.x_f16 = f16_div(op1->data.x_f16, op2->data.x_f16); - out_val->data.x_f16 = f16_roundToInt(out_val->data.x_f16, softfloat_round_min, false); - return; - case 32: - out_val->data.x_f32 = floorf(op1->data.x_f32 / op2->data.x_f32); - return; - case 64: - out_val->data.x_f64 = floor(op1->data.x_f64 / op2->data.x_f64); - return; - case 128: - f128M_div(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); - f128M_roundToInt(&out_val->data.x_f128, softfloat_round_min, false, &out_val->data.x_f128); - return; - default: - zig_unreachable(); - } - } else { - zig_unreachable(); - } -} - -static void float_rem(ZigValue *out_val, ZigValue *op1, ZigValue *op2) { - assert(op1->type == op2->type); - out_val->type = op1->type; - if (op1->type->id == ZigTypeIdComptimeFloat) { - bigfloat_rem(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); - } else if (op1->type->id == ZigTypeIdFloat) { - switch (op1->type->data.floating.bit_count) { - case 16: - out_val->data.x_f16 = f16_rem(op1->data.x_f16, op2->data.x_f16); - return; - case 32: - out_val->data.x_f32 = fmodf(op1->data.x_f32, op2->data.x_f32); - return; - case 64: - out_val->data.x_f64 = fmod(op1->data.x_f64, op2->data.x_f64); - return; - case 128: - f128M_rem(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); - return; - default: - zig_unreachable(); - } - } else { - zig_unreachable(); - } -} - -// c = a - b * trunc(a / b) -static float16_t zig_f16_mod(float16_t a, float16_t b) { - float16_t c; - c = f16_div(a, b); - c = f16_roundToInt(c, softfloat_round_min, true); - c = f16_mul(b, c); - c = f16_sub(a, c); - return c; -} - -// c = a - b * trunc(a / b) -static void zig_f128M_mod(const float128_t* a, const float128_t* b, float128_t* c) { - f128M_div(a, b, c); - f128M_roundToInt(c, softfloat_round_min, true, c); - f128M_mul(b, c, c); - f128M_sub(a, c, c); -} - -static void float_mod(ZigValue *out_val, ZigValue *op1, ZigValue *op2) { - assert(op1->type == op2->type); - out_val->type = op1->type; - if (op1->type->id == ZigTypeIdComptimeFloat) { - bigfloat_mod(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); - } else if (op1->type->id == ZigTypeIdFloat) { - switch (op1->type->data.floating.bit_count) { - case 16: - out_val->data.x_f16 = zig_f16_mod(op1->data.x_f16, op2->data.x_f16); - return; - case 32: - out_val->data.x_f32 = fmodf(fmodf(op1->data.x_f32, op2->data.x_f32) + op2->data.x_f32, op2->data.x_f32); - return; - case 64: - out_val->data.x_f64 = fmod(fmod(op1->data.x_f64, op2->data.x_f64) + op2->data.x_f64, op2->data.x_f64); - return; - case 128: - zig_f128M_mod(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); - return; - default: - zig_unreachable(); - } - } else { - zig_unreachable(); - } -} - -static void float_negate(ZigValue *out_val, ZigValue *op) { - out_val->type = op->type; - if (op->type->id == ZigTypeIdComptimeFloat) { - bigfloat_negate(&out_val->data.x_bigfloat, &op->data.x_bigfloat); - } else if (op->type->id == ZigTypeIdFloat) { - switch (op->type->data.floating.bit_count) { - case 16: - { - const float16_t zero = zig_double_to_f16(0); - out_val->data.x_f16 = f16_sub(zero, op->data.x_f16); - return; - } - case 32: - out_val->data.x_f32 = -op->data.x_f32; - return; - case 64: - out_val->data.x_f64 = -op->data.x_f64; - return; - case 128: - float128_t zero_f128; - ui32_to_f128M(0, &zero_f128); - f128M_sub(&zero_f128, &op->data.x_f128, &out_val->data.x_f128); - return; - default: - zig_unreachable(); - } - } else { - zig_unreachable(); - } -} - -void float_write_ieee597(ZigValue *op, uint8_t *buf, bool is_big_endian) { - if (op->type->id != ZigTypeIdFloat) - zig_unreachable(); - - const unsigned n = op->type->data.floating.bit_count / 8; - assert(n <= 16); - - switch (op->type->data.floating.bit_count) { - case 16: - memcpy(buf, &op->data.x_f16, 2); - break; - case 32: - memcpy(buf, &op->data.x_f32, 4); - break; - case 64: - memcpy(buf, &op->data.x_f64, 8); - break; - case 128: - memcpy(buf, &op->data.x_f128, 16); - break; - default: - zig_unreachable(); - } - - if (is_big_endian) { - // Byteswap in place if needed - for (size_t i = 0; i < n / 2; i++) { - uint8_t u = buf[i]; - buf[i] = buf[n - 1 - i]; - buf[n - 1 - i] = u; - } - } -} - -void float_read_ieee597(ZigValue *val, uint8_t *buf, bool is_big_endian) { - if (val->type->id != ZigTypeIdFloat) - zig_unreachable(); - - const unsigned n = val->type->data.floating.bit_count / 8; - assert(n <= 16); - - uint8_t tmp[16]; - uint8_t *ptr = buf; - - if (is_big_endian) { - memcpy(tmp, buf, n); - - // Byteswap if needed - for (size_t i = 0; i < n / 2; i++) { - uint8_t u = tmp[i]; - tmp[i] = tmp[n - 1 - i]; - tmp[n - 1 - i] = u; - } - - ptr = tmp; - } - - switch (val->type->data.floating.bit_count) { - case 16: - memcpy(&val->data.x_f16, ptr, 2); - return; - case 32: - memcpy(&val->data.x_f32, ptr, 4); - return; - case 64: - memcpy(&val->data.x_f64, ptr, 8); - return; - case 128: - memcpy(&val->data.x_f128, ptr, 16); - return; - default: - zig_unreachable(); - } -} - -static void value_to_bigfloat(BigFloat *out, ZigValue *val) { - switch (val->type->id) { - case ZigTypeIdInt: - case ZigTypeIdComptimeInt: - bigfloat_init_bigint(out, &val->data.x_bigint); - return; - case ZigTypeIdComptimeFloat: - *out = val->data.x_bigfloat; - return; - case ZigTypeIdFloat: switch (val->type->data.floating.bit_count) { - case 16: - bigfloat_init_16(out, val->data.x_f16); - return; - case 32: - bigfloat_init_32(out, val->data.x_f32); - return; - case 64: - bigfloat_init_64(out, val->data.x_f64); - return; - case 80: - zig_panic("TODO"); - case 128: - bigfloat_init_128(out, val->data.x_f128); - return; - default: - zig_unreachable(); - } - default: - zig_unreachable(); - } -} - -static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstGen *instruction, ZigType *other_type, - bool explicit_cast) -{ - if (type_is_invalid(other_type)) { - return false; - } - - ZigValue *const_val = ir_resolve_const(ira, instruction, LazyOkNoUndef); - if (const_val == nullptr) - return false; - - if (const_val->special == ConstValSpecialLazy) { - switch (const_val->data.x_lazy->id) { - case LazyValueIdAlignOf: { - // This is guaranteed to fit into a u29 - if (other_type->id == ZigTypeIdComptimeInt) - return true; - size_t align_bits = get_align_amt_type(ira->codegen)->data.integral.bit_count; - if (other_type->id == ZigTypeIdInt && !other_type->data.integral.is_signed && - other_type->data.integral.bit_count >= align_bits) - { - return true; - } - break; - } - case LazyValueIdSizeOf: { - // This is guaranteed to fit into a usize - if (other_type->id == ZigTypeIdComptimeInt) - return true; - size_t usize_bits = ira->codegen->builtin_types.entry_usize->data.integral.bit_count; - if (other_type->id == ZigTypeIdInt && !other_type->data.integral.is_signed && - other_type->data.integral.bit_count >= usize_bits) - { - return true; - } - break; - } - default: - break; - } - } - - const_val = ir_resolve_const(ira, instruction, UndefBad); - if (const_val == nullptr) - return false; - - bool const_val_is_int = (const_val->type->id == ZigTypeIdInt || const_val->type->id == ZigTypeIdComptimeInt); - bool const_val_is_float = (const_val->type->id == ZigTypeIdFloat || const_val->type->id == ZigTypeIdComptimeFloat); - assert(const_val_is_int || const_val_is_float); - - if (const_val_is_int && other_type->id == ZigTypeIdComptimeFloat) { - return true; - } - if (other_type->id == ZigTypeIdFloat) { - if (const_val->type->id == ZigTypeIdComptimeInt || const_val->type->id == ZigTypeIdComptimeFloat) { - return true; - } - if (const_val->type->id == ZigTypeIdInt) { - BigFloat tmp_bf; - bigfloat_init_bigint(&tmp_bf, &const_val->data.x_bigint); - BigFloat orig_bf; - switch (other_type->data.floating.bit_count) { - case 16: { - float16_t tmp = bigfloat_to_f16(&tmp_bf); - bigfloat_init_16(&orig_bf, tmp); - break; - } - case 32: { - float tmp = bigfloat_to_f32(&tmp_bf); - bigfloat_init_32(&orig_bf, tmp); - break; - } - case 64: { - double tmp = bigfloat_to_f64(&tmp_bf); - bigfloat_init_64(&orig_bf, tmp); - break; - } - case 80: - zig_panic("TODO"); - case 128: { - float128_t tmp = bigfloat_to_f128(&tmp_bf); - bigfloat_init_128(&orig_bf, tmp); - break; - } - default: - zig_unreachable(); - } - BigInt orig_bi; - bigint_init_bigfloat(&orig_bi, &orig_bf); - if (bigint_cmp(&orig_bi, &const_val->data.x_bigint) == CmpEQ) { - return true; - } - Buf *val_buf = buf_alloc(); - bigint_append_buf(val_buf, &const_val->data.x_bigint, 10); - ir_add_error_node(ira, instruction->base.source_node, - buf_sprintf("type %s cannot represent integer value %s", - buf_ptr(&other_type->name), - buf_ptr(val_buf))); - return false; - } - if (other_type->data.floating.bit_count >= const_val->type->data.floating.bit_count) { - return true; - } - switch (other_type->data.floating.bit_count) { - case 16: - switch (const_val->type->data.floating.bit_count) { - case 32: { - float16_t tmp = zig_double_to_f16(const_val->data.x_f32); - float orig = zig_f16_to_double(tmp); - if (const_val->data.x_f32 == orig) { - return true; - } - break; - } - case 64: { - float16_t tmp = zig_double_to_f16(const_val->data.x_f64); - double orig = zig_f16_to_double(tmp); - if (const_val->data.x_f64 == orig) { - return true; - } - break; - } - case 80: - zig_panic("TODO"); - case 128: { - float16_t tmp = f128M_to_f16(&const_val->data.x_f128); - float128_t orig; - f16_to_f128M(tmp, &orig); - if (f128M_eq(&orig, &const_val->data.x_f128)) { - return true; - } - break; - } - default: - zig_unreachable(); - } - break; - case 32: - switch (const_val->type->data.floating.bit_count) { - case 64: { - float tmp = const_val->data.x_f64; - double orig = tmp; - if (const_val->data.x_f64 == orig) { - return true; - } - break; - } - case 80: - zig_panic("TODO"); - case 128: { - float32_t tmp = f128M_to_f32(&const_val->data.x_f128); - float128_t orig; - f32_to_f128M(tmp, &orig); - if (f128M_eq(&orig, &const_val->data.x_f128)) { - return true; - } - break; - } - default: - zig_unreachable(); - } - break; - case 64: - switch (const_val->type->data.floating.bit_count) { - case 80: - zig_panic("TODO"); - case 128: { - float64_t tmp = f128M_to_f64(&const_val->data.x_f128); - float128_t orig; - f64_to_f128M(tmp, &orig); - if (f128M_eq(&orig, &const_val->data.x_f128)) { - return true; - } - break; - } - default: - zig_unreachable(); - } - break; - case 80: - assert(const_val->type->data.floating.bit_count == 128); - zig_panic("TODO"); - case 128: - return true; - default: - zig_unreachable(); - } - Buf *val_buf = buf_alloc(); - float_append_buf(val_buf, const_val); - ir_add_error_node(ira, instruction->base.source_node, - buf_sprintf("cast of value %s to type '%s' loses information", - buf_ptr(val_buf), - buf_ptr(&other_type->name))); - return false; - } else if (other_type->id == ZigTypeIdInt && const_val_is_int) { - if (!other_type->data.integral.is_signed && const_val->data.x_bigint.is_negative) { - Buf *val_buf = buf_alloc(); - bigint_append_buf(val_buf, &const_val->data.x_bigint, 10); - ir_add_error_node(ira, instruction->base.source_node, - buf_sprintf("cannot cast negative value %s to unsigned integer type '%s'", - buf_ptr(val_buf), - buf_ptr(&other_type->name))); - return false; - } - if (bigint_fits_in_bits(&const_val->data.x_bigint, other_type->data.integral.bit_count, - other_type->data.integral.is_signed)) - { - return true; - } - } else if (const_val_fits_in_num_lit(const_val, other_type)) { - return true; - } else if (other_type->id == ZigTypeIdOptional) { - ZigType *child_type = other_type->data.maybe.child_type; - if (const_val_fits_in_num_lit(const_val, child_type)) { - return true; - } else if (child_type->id == ZigTypeIdInt && const_val_is_int) { - if (!child_type->data.integral.is_signed && const_val->data.x_bigint.is_negative) { - Buf *val_buf = buf_alloc(); - bigint_append_buf(val_buf, &const_val->data.x_bigint, 10); - ir_add_error_node(ira, instruction->base.source_node, - buf_sprintf("cannot cast negative value %s to unsigned integer type '%s'", - buf_ptr(val_buf), - buf_ptr(&child_type->name))); - return false; - } - if (bigint_fits_in_bits(&const_val->data.x_bigint, - child_type->data.integral.bit_count, - child_type->data.integral.is_signed)) - { - return true; - } - } else if (child_type->id == ZigTypeIdFloat && const_val_is_float) { - return true; - } - } - if (explicit_cast && (other_type->id == ZigTypeIdInt || other_type->id == ZigTypeIdComptimeInt) && - const_val_is_float) - { - if (float_has_fraction(const_val)) { - Buf *val_buf = buf_alloc(); - float_append_buf(val_buf, const_val); - - ir_add_error_node(ira, instruction->base.source_node, - buf_sprintf("fractional component prevents float value %s from being casted to type '%s'", - buf_ptr(val_buf), - buf_ptr(&other_type->name))); - return false; - } else { - if (other_type->id == ZigTypeIdComptimeInt) { - return true; - } else { - BigInt bigint; - float_init_bigint(&bigint, const_val); - if (bigint_fits_in_bits(&bigint, other_type->data.integral.bit_count, - other_type->data.integral.is_signed)) - { - return true; - } - } - } - } - - const char *num_lit_str; - Buf *val_buf = buf_alloc(); - if (const_val_is_float) { - num_lit_str = "float"; - float_append_buf(val_buf, const_val); - } else { - num_lit_str = "integer"; - bigint_append_buf(val_buf, &const_val->data.x_bigint, 10); - } - - ir_add_error_node(ira, instruction->base.source_node, - buf_sprintf("%s value %s cannot be coerced to type '%s'", - num_lit_str, - buf_ptr(val_buf), - buf_ptr(&other_type->name))); - return false; -} - -static bool is_tagged_union(ZigType *type) { - if (type->id != ZigTypeIdUnion) - return false; - return (type->data.unionation.decl_node->data.container_decl.auto_enum || - type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr); -} - -static void populate_error_set_table(ErrorTableEntry **errors, ZigType *set) { - assert(set->id == ZigTypeIdErrorSet); - for (uint32_t i = 0; i < set->data.error_set.err_count; i += 1) { - ErrorTableEntry *error_entry = set->data.error_set.errors[i]; - assert(errors[error_entry->value] == nullptr); - errors[error_entry->value] = error_entry; - } -} - -static ErrorTableEntry *better_documented_error(ErrorTableEntry *preferred, ErrorTableEntry *other) { - if (preferred->decl_node->type == NodeTypeErrorSetField) - return preferred; - if (other->decl_node->type == NodeTypeErrorSetField) - return other; - return preferred; -} - -static ZigType *get_error_set_intersection(IrAnalyze *ira, ZigType *set1, ZigType *set2, - AstNode *source_node) -{ - assert(set1->id == ZigTypeIdErrorSet); - assert(set2->id == ZigTypeIdErrorSet); - - if (!resolve_inferred_error_set(ira->codegen, set1, source_node)) { - return ira->codegen->builtin_types.entry_invalid; - } - if (!resolve_inferred_error_set(ira->codegen, set2, source_node)) { - return ira->codegen->builtin_types.entry_invalid; - } - if (type_is_global_error_set(set1)) { - return set2; - } - if (type_is_global_error_set(set2)) { - return set1; - } - size_t errors_count = ira->codegen->errors_by_index.length; - ErrorTableEntry **errors = heap::c_allocator.allocate(errors_count); - populate_error_set_table(errors, set1); - ZigList intersection_list = {}; - - ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet); - buf_resize(&err_set_type->name, 0); - buf_appendf(&err_set_type->name, "error{"); - - bool need_comma = false; - for (uint32_t i = 0; i < set2->data.error_set.err_count; i += 1) { - ErrorTableEntry *error_entry = set2->data.error_set.errors[i]; - ErrorTableEntry *existing_entry = errors[error_entry->value]; - if (existing_entry != nullptr) { - // prefer the one with docs - const char *comma = need_comma ? "," : ""; - need_comma = true; - ErrorTableEntry *existing_entry_with_docs = better_documented_error(existing_entry, error_entry); - intersection_list.append(existing_entry_with_docs); - buf_appendf(&err_set_type->name, "%s%s", comma, buf_ptr(&existing_entry_with_docs->name)); - } - } - heap::c_allocator.deallocate(errors, errors_count); - - err_set_type->data.error_set.err_count = intersection_list.length; - err_set_type->data.error_set.errors = intersection_list.items; - err_set_type->size_in_bits = ira->codegen->builtin_types.entry_global_error_set->size_in_bits; - err_set_type->abi_align = ira->codegen->builtin_types.entry_global_error_set->abi_align; - err_set_type->abi_size = ira->codegen->builtin_types.entry_global_error_set->abi_size; - - buf_appendf(&err_set_type->name, "}"); - - return err_set_type; -} - -static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted_type, - ZigType *actual_type, AstNode *source_node, bool wanted_is_mutable) -{ - CodeGen *g = ira->codegen; - ConstCastOnly result = {}; - result.id = ConstCastResultIdOk; - - Error err; - - if (wanted_type == actual_type) - return result; - - // If pointers have the same representation in memory, they can be "const-casted". - // `const` attribute can be gained - // `volatile` attribute can be gained - // `allowzero` attribute can be gained (whether from explicit attribute, C pointer, or optional pointer) - // but only if !wanted_is_mutable - // alignment can be decreased - // bit offset attributes must match exactly - // PtrLenSingle/PtrLenUnknown must match exactly, but PtrLenC matches either one - // sentinel-terminated pointers can coerce into PtrLenUnknown - ZigType *wanted_ptr_type = get_src_ptr_type(wanted_type); - ZigType *actual_ptr_type = get_src_ptr_type(actual_type); - bool wanted_allows_zero = ptr_allows_addr_zero(wanted_type); - bool actual_allows_zero = ptr_allows_addr_zero(actual_type); - bool wanted_is_c_ptr = wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.ptr_len == PtrLenC; - bool actual_is_c_ptr = actual_type->id == ZigTypeIdPointer && actual_type->data.pointer.ptr_len == PtrLenC; - bool wanted_opt_or_ptr = wanted_ptr_type != nullptr && wanted_ptr_type->id == ZigTypeIdPointer; - bool actual_opt_or_ptr = actual_ptr_type != nullptr && actual_ptr_type->id == ZigTypeIdPointer; - if (wanted_opt_or_ptr && actual_opt_or_ptr) { - bool ok_null_term_ptrs = - wanted_ptr_type->data.pointer.sentinel == nullptr || - (actual_ptr_type->data.pointer.sentinel != nullptr && - const_values_equal(ira->codegen, wanted_ptr_type->data.pointer.sentinel, - actual_ptr_type->data.pointer.sentinel)) || - actual_ptr_type->data.pointer.ptr_len == PtrLenC; - if (!ok_null_term_ptrs) { - result.id = ConstCastResultIdPtrSentinel; - result.data.bad_ptr_sentinel = heap::c_allocator.allocate_nonzero(1); - result.data.bad_ptr_sentinel->wanted_type = wanted_ptr_type; - result.data.bad_ptr_sentinel->actual_type = actual_ptr_type; - return result; - } - bool ptr_lens_equal = actual_ptr_type->data.pointer.ptr_len == wanted_ptr_type->data.pointer.ptr_len; - if (!(ptr_lens_equal || wanted_is_c_ptr || actual_is_c_ptr)) { - result.id = ConstCastResultIdPtrLens; - return result; - } - - bool ok_cv_qualifiers = - (!actual_ptr_type->data.pointer.is_const || wanted_ptr_type->data.pointer.is_const) && - (!actual_ptr_type->data.pointer.is_volatile || wanted_ptr_type->data.pointer.is_volatile); - if (!ok_cv_qualifiers) { - result.id = ConstCastResultIdCV; - result.data.bad_cv = heap::c_allocator.allocate_nonzero(1); - result.data.bad_cv->wanted_type = wanted_ptr_type; - result.data.bad_cv->actual_type = actual_ptr_type; - return result; - } - - ConstCastOnly child = types_match_const_cast_only(ira, wanted_ptr_type->data.pointer.child_type, - actual_ptr_type->data.pointer.child_type, source_node, !wanted_ptr_type->data.pointer.is_const); - if (child.id == ConstCastResultIdInvalid) - return child; - if (child.id != ConstCastResultIdOk) { - result.id = ConstCastResultIdPointerChild; - result.data.pointer_mismatch = heap::c_allocator.allocate_nonzero(1); - result.data.pointer_mismatch->child = child; - result.data.pointer_mismatch->wanted_child = wanted_ptr_type->data.pointer.child_type; - result.data.pointer_mismatch->actual_child = actual_ptr_type->data.pointer.child_type; - return result; - } - bool ok_allows_zero = (wanted_allows_zero && - (actual_allows_zero || !wanted_is_mutable)) || - (!wanted_allows_zero && !actual_allows_zero); - if (!ok_allows_zero) { - result.id = ConstCastResultIdBadAllowsZero; - result.data.bad_allows_zero = heap::c_allocator.allocate_nonzero(1); - result.data.bad_allows_zero->wanted_type = wanted_type; - result.data.bad_allows_zero->actual_type = actual_type; - return result; - } - if ((err = type_resolve(g, actual_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) { - result.id = ConstCastResultIdInvalid; - return result; - } - if ((err = type_resolve(g, wanted_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) { - result.id = ConstCastResultIdInvalid; - return result; - } - if ((err = type_resolve(g, wanted_type, ResolveStatusZeroBitsKnown))) { - result.id = ConstCastResultIdInvalid; - return result; - } - if ((err = type_resolve(g, actual_type, ResolveStatusZeroBitsKnown))) { - result.id = ConstCastResultIdInvalid; - return result; - } - if (type_has_bits(g, wanted_type) == type_has_bits(g, actual_type) && - actual_ptr_type->data.pointer.bit_offset_in_host == wanted_ptr_type->data.pointer.bit_offset_in_host && - actual_ptr_type->data.pointer.host_int_bytes == wanted_ptr_type->data.pointer.host_int_bytes && - get_ptr_align(ira->codegen, actual_ptr_type) >= get_ptr_align(ira->codegen, wanted_ptr_type)) - { - return result; - } - } - - // arrays - if (wanted_type->id == ZigTypeIdArray && actual_type->id == ZigTypeIdArray && - wanted_type->data.array.len == actual_type->data.array.len) - { - ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.array.child_type, - actual_type->data.array.child_type, source_node, wanted_is_mutable); - if (child.id == ConstCastResultIdInvalid) - return child; - if (child.id != ConstCastResultIdOk) { - result.id = ConstCastResultIdArrayChild; - result.data.array_mismatch = heap::c_allocator.allocate_nonzero(1); - result.data.array_mismatch->child = child; - result.data.array_mismatch->wanted_child = wanted_type->data.array.child_type; - result.data.array_mismatch->actual_child = actual_type->data.array.child_type; - return result; - } - bool ok_null_terminated = (wanted_type->data.array.sentinel == nullptr) || - (actual_type->data.array.sentinel != nullptr && - const_values_equal(ira->codegen, wanted_type->data.array.sentinel, actual_type->data.array.sentinel)); - if (!ok_null_terminated) { - result.id = ConstCastResultIdSentinelArrays; - result.data.sentinel_arrays = heap::c_allocator.allocate_nonzero(1); - result.data.sentinel_arrays->child = child; - result.data.sentinel_arrays->wanted_type = wanted_type; - result.data.sentinel_arrays->actual_type = actual_type; - return result; - } - return result; - } - - // slice const - if (is_slice(wanted_type) && is_slice(actual_type)) { - ZigType *actual_ptr_type = actual_type->data.structure.fields[slice_ptr_index]->type_entry; - ZigType *wanted_ptr_type = wanted_type->data.structure.fields[slice_ptr_index]->type_entry; - if ((err = type_resolve(g, actual_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) { - result.id = ConstCastResultIdInvalid; - return result; - } - if ((err = type_resolve(g, wanted_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) { - result.id = ConstCastResultIdInvalid; - return result; - } - bool ok_sentinels = - wanted_ptr_type->data.pointer.sentinel == nullptr || - (actual_ptr_type->data.pointer.sentinel != nullptr && - const_values_equal(ira->codegen, wanted_ptr_type->data.pointer.sentinel, - actual_ptr_type->data.pointer.sentinel)); - if (!ok_sentinels) { - result.id = ConstCastResultIdPtrSentinel; - result.data.bad_ptr_sentinel = heap::c_allocator.allocate_nonzero(1); - result.data.bad_ptr_sentinel->wanted_type = wanted_ptr_type; - result.data.bad_ptr_sentinel->actual_type = actual_ptr_type; - return result; - } - if ((!actual_ptr_type->data.pointer.is_const || wanted_ptr_type->data.pointer.is_const) && - (!actual_ptr_type->data.pointer.is_volatile || wanted_ptr_type->data.pointer.is_volatile) && - actual_ptr_type->data.pointer.bit_offset_in_host == wanted_ptr_type->data.pointer.bit_offset_in_host && - actual_ptr_type->data.pointer.host_int_bytes == wanted_ptr_type->data.pointer.host_int_bytes && - get_ptr_align(g, actual_ptr_type) >= get_ptr_align(g, wanted_ptr_type)) - { - ConstCastOnly child = types_match_const_cast_only(ira, wanted_ptr_type->data.pointer.child_type, - actual_ptr_type->data.pointer.child_type, source_node, !wanted_ptr_type->data.pointer.is_const); - if (child.id == ConstCastResultIdInvalid) - return child; - if (child.id != ConstCastResultIdOk) { - result.id = ConstCastResultIdSliceChild; - result.data.slice_mismatch = heap::c_allocator.allocate_nonzero(1); - result.data.slice_mismatch->child = child; - result.data.slice_mismatch->actual_child = actual_ptr_type->data.pointer.child_type; - result.data.slice_mismatch->wanted_child = wanted_ptr_type->data.pointer.child_type; - } - return result; - } - } - - // optional types - if (wanted_type->id == ZigTypeIdOptional && actual_type->id == ZigTypeIdOptional) { - // Consider the case where the wanted type is ??[*]T and the actual one - // is ?[*]T, we cannot turn the former into the latter even though the - // child types are compatible (?[*]T and [*]T are both represented as a - // pointer). The extra level of indirection in ??[*]T means it's - // represented as a regular, fat, optional type and, as a consequence, - // has a different shape than the one of ?[*]T. - if ((wanted_ptr_type != nullptr) != (actual_ptr_type != nullptr)) { - // The use of type_mismatch is intentional - result.id = ConstCastResultIdOptionalShape; - result.data.type_mismatch = heap::c_allocator.allocate_nonzero(1); - result.data.type_mismatch->wanted_type = wanted_type; - result.data.type_mismatch->actual_type = actual_type; - return result; - } - ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.maybe.child_type, - actual_type->data.maybe.child_type, source_node, wanted_is_mutable); - if (child.id == ConstCastResultIdInvalid) - return child; - if (child.id != ConstCastResultIdOk) { - result.id = ConstCastResultIdOptionalChild; - result.data.optional = heap::c_allocator.allocate_nonzero(1); - result.data.optional->child = child; - result.data.optional->wanted_child = wanted_type->data.maybe.child_type; - result.data.optional->actual_child = actual_type->data.maybe.child_type; - } - return result; - } - - // error union - if (wanted_type->id == ZigTypeIdErrorUnion && actual_type->id == ZigTypeIdErrorUnion) { - ConstCastOnly payload_child = types_match_const_cast_only(ira, wanted_type->data.error_union.payload_type, - actual_type->data.error_union.payload_type, source_node, wanted_is_mutable); - if (payload_child.id == ConstCastResultIdInvalid) - return payload_child; - if (payload_child.id != ConstCastResultIdOk) { - result.id = ConstCastResultIdErrorUnionPayload; - result.data.error_union_payload = heap::c_allocator.allocate_nonzero(1); - result.data.error_union_payload->child = payload_child; - result.data.error_union_payload->wanted_payload = wanted_type->data.error_union.payload_type; - result.data.error_union_payload->actual_payload = actual_type->data.error_union.payload_type; - return result; - } - ConstCastOnly error_set_child = types_match_const_cast_only(ira, wanted_type->data.error_union.err_set_type, - actual_type->data.error_union.err_set_type, source_node, wanted_is_mutable); - if (error_set_child.id == ConstCastResultIdInvalid) - return error_set_child; - if (error_set_child.id != ConstCastResultIdOk) { - result.id = ConstCastResultIdErrorUnionErrorSet; - result.data.error_union_error_set = heap::c_allocator.allocate_nonzero(1); - result.data.error_union_error_set->child = error_set_child; - result.data.error_union_error_set->wanted_err_set = wanted_type->data.error_union.err_set_type; - result.data.error_union_error_set->actual_err_set = actual_type->data.error_union.err_set_type; - return result; - } - return result; - } - - // error set - if (wanted_type->id == ZigTypeIdErrorSet && actual_type->id == ZigTypeIdErrorSet) { - ZigType *contained_set = actual_type; - ZigType *container_set = wanted_type; - - // if the container set is inferred, then this will always work. - if (container_set->data.error_set.infer_fn != nullptr && container_set->data.error_set.incomplete) { - return result; - } - // if the container set is the global one, it will always work. - if (type_is_global_error_set(container_set)) { - return result; - } - - if (!resolve_inferred_error_set(ira->codegen, contained_set, source_node)) { - result.id = ConstCastResultIdUnresolvedInferredErrSet; - return result; - } - - if (type_is_global_error_set(contained_set)) { - result.id = ConstCastResultIdErrSetGlobal; - return result; - } - - size_t errors_count = g->errors_by_index.length; - ErrorTableEntry **errors = heap::c_allocator.allocate(errors_count); - for (uint32_t i = 0; i < container_set->data.error_set.err_count; i += 1) { - ErrorTableEntry *error_entry = container_set->data.error_set.errors[i]; - assert(errors[error_entry->value] == nullptr); - errors[error_entry->value] = error_entry; - } - for (uint32_t i = 0; i < contained_set->data.error_set.err_count; i += 1) { - ErrorTableEntry *contained_error_entry = contained_set->data.error_set.errors[i]; - ErrorTableEntry *error_entry = errors[contained_error_entry->value]; - if (error_entry == nullptr) { - if (result.id == ConstCastResultIdOk) { - result.id = ConstCastResultIdErrSet; - result.data.error_set_mismatch = heap::c_allocator.create(); - } - result.data.error_set_mismatch->missing_errors.append(contained_error_entry); - } - } - heap::c_allocator.deallocate(errors, errors_count); - return result; - } - - // fn - if (wanted_type->id == ZigTypeIdFn && - actual_type->id == ZigTypeIdFn) - { - if (wanted_type->data.fn.fn_type_id.alignment > actual_type->data.fn.fn_type_id.alignment) { - result.id = ConstCastResultIdFnAlign; - return result; - } - if (wanted_type->data.fn.fn_type_id.is_var_args != actual_type->data.fn.fn_type_id.is_var_args) { - result.id = ConstCastResultIdFnVarArgs; - return result; - } - if (wanted_type->data.fn.is_generic != actual_type->data.fn.is_generic) { - result.id = ConstCastResultIdFnIsGeneric; - return result; - } - if (!wanted_type->data.fn.is_generic && - actual_type->data.fn.fn_type_id.return_type->id != ZigTypeIdUnreachable) - { - ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.fn.fn_type_id.return_type, - actual_type->data.fn.fn_type_id.return_type, source_node, false); - if (child.id == ConstCastResultIdInvalid) - return child; - if (child.id != ConstCastResultIdOk) { - result.id = ConstCastResultIdFnReturnType; - result.data.return_type = heap::c_allocator.allocate_nonzero(1); - *result.data.return_type = child; - return result; - } - } - if (wanted_type->data.fn.fn_type_id.param_count != actual_type->data.fn.fn_type_id.param_count) { - result.id = ConstCastResultIdFnArgCount; - return result; - } - if (wanted_type->data.fn.fn_type_id.next_param_index != actual_type->data.fn.fn_type_id.next_param_index) { - result.id = ConstCastResultIdFnGenericArgCount; - return result; - } - assert(wanted_type->data.fn.is_generic || - wanted_type->data.fn.fn_type_id.next_param_index == wanted_type->data.fn.fn_type_id.param_count); - for (size_t i = 0; i < wanted_type->data.fn.fn_type_id.param_count; i += 1) { - // note it's reversed for parameters - FnTypeParamInfo *actual_param_info = &actual_type->data.fn.fn_type_id.param_info[i]; - FnTypeParamInfo *expected_param_info = &wanted_type->data.fn.fn_type_id.param_info[i]; - - ConstCastOnly arg_child = types_match_const_cast_only(ira, actual_param_info->type, - expected_param_info->type, source_node, false); - if (arg_child.id == ConstCastResultIdInvalid) - return arg_child; - if (arg_child.id != ConstCastResultIdOk) { - result.id = ConstCastResultIdFnArg; - result.data.fn_arg.arg_index = i; - result.data.fn_arg.actual_param_type = actual_param_info->type; - result.data.fn_arg.expected_param_type = expected_param_info->type; - result.data.fn_arg.child = heap::c_allocator.allocate_nonzero(1); - *result.data.fn_arg.child = arg_child; - return result; - } - - if (expected_param_info->is_noalias != actual_param_info->is_noalias) { - result.id = ConstCastResultIdFnArgNoAlias; - result.data.arg_no_alias.arg_index = i; - return result; - } - } - if (wanted_type->data.fn.fn_type_id.cc != actual_type->data.fn.fn_type_id.cc) { - // ConstCastResultIdFnCC is guaranteed to be the last one reported, meaning everything else is ok. - result.id = ConstCastResultIdFnCC; - return result; - } - return result; - } - - if (wanted_type->id == ZigTypeIdInt && actual_type->id == ZigTypeIdInt) { - if (wanted_type->data.integral.is_signed != actual_type->data.integral.is_signed || - wanted_type->data.integral.bit_count != actual_type->data.integral.bit_count) - { - result.id = ConstCastResultIdIntShorten; - result.data.int_shorten = heap::c_allocator.allocate_nonzero(1); - result.data.int_shorten->wanted_type = wanted_type; - result.data.int_shorten->actual_type = actual_type; - return result; - } - return result; - } - - result.id = ConstCastResultIdType; - result.data.type_mismatch = heap::c_allocator.allocate_nonzero(1); - result.data.type_mismatch->wanted_type = wanted_type; - result.data.type_mismatch->actual_type = actual_type; - return result; -} - -static void update_errors_helper(CodeGen *g, ErrorTableEntry ***errors, size_t *errors_count) { - size_t old_errors_count = *errors_count; - *errors_count = g->errors_by_index.length; - *errors = heap::c_allocator.reallocate(*errors, old_errors_count, *errors_count); -} - -static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigType *expected_type, - IrInstGen **instructions, size_t instruction_count) -{ - Error err; - assert(instruction_count >= 1); - IrInstGen *prev_inst; - size_t i = 0; - for (;;) { - prev_inst = instructions[i]; - if (type_is_invalid(prev_inst->value->type)) { - return ira->codegen->builtin_types.entry_invalid; - } - if (prev_inst->value->type->id == ZigTypeIdUnreachable) { - i += 1; - if (i == instruction_count) { - return prev_inst->value->type; - } - continue; - } - break; - } - ErrorTableEntry **errors = nullptr; - size_t errors_count = 0; - ZigType *err_set_type = nullptr; - if (prev_inst->value->type->id == ZigTypeIdErrorSet) { - if (!resolve_inferred_error_set(ira->codegen, prev_inst->value->type, prev_inst->base.source_node)) { - return ira->codegen->builtin_types.entry_invalid; - } - if (type_is_global_error_set(prev_inst->value->type)) { - err_set_type = ira->codegen->builtin_types.entry_global_error_set; - } else { - err_set_type = prev_inst->value->type; - update_errors_helper(ira->codegen, &errors, &errors_count); - - for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { - ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i]; - assert(errors[error_entry->value] == nullptr); - errors[error_entry->value] = error_entry; - } - } - } - - bool any_are_null = (prev_inst->value->type->id == ZigTypeIdNull); - bool convert_to_const_slice = false; - bool make_the_slice_const = false; - bool make_the_pointer_const = false; - for (; i < instruction_count; i += 1) { - IrInstGen *cur_inst = instructions[i]; - ZigType *cur_type = cur_inst->value->type; - ZigType *prev_type = prev_inst->value->type; - - if (type_is_invalid(cur_type)) { - return cur_type; - } - - if (prev_type == cur_type) { - continue; - } - - if (prev_type->id == ZigTypeIdUnreachable) { - prev_inst = cur_inst; - continue; - } - - if (cur_type->id == ZigTypeIdUnreachable) { - continue; - } - - if (prev_type->id == ZigTypeIdErrorSet) { - ir_assert_gen(err_set_type != nullptr, prev_inst); - if (cur_type->id == ZigTypeIdErrorSet) { - if (type_is_global_error_set(err_set_type)) { - continue; - } - bool allow_infer = cur_type->data.error_set.infer_fn != nullptr && - cur_type->data.error_set.infer_fn == ira->new_irb.exec->fn_entry; - if (!allow_infer && !resolve_inferred_error_set(ira->codegen, cur_type, cur_inst->base.source_node)) { - return ira->codegen->builtin_types.entry_invalid; - } - if (!allow_infer && type_is_global_error_set(cur_type)) { - err_set_type = ira->codegen->builtin_types.entry_global_error_set; - prev_inst = cur_inst; - continue; - } - - // number of declared errors might have increased now - update_errors_helper(ira->codegen, &errors, &errors_count); - - // if err_set_type is a superset of cur_type, keep err_set_type. - // if cur_type is a superset of err_set_type, switch err_set_type to cur_type - bool prev_is_superset = true; - for (uint32_t i = 0; i < cur_type->data.error_set.err_count; i += 1) { - ErrorTableEntry *contained_error_entry = cur_type->data.error_set.errors[i]; - ErrorTableEntry *error_entry = errors[contained_error_entry->value]; - if (error_entry == nullptr) { - prev_is_superset = false; - break; - } - } - if (prev_is_superset) { - continue; - } - - // unset everything in errors - for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { - ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i]; - errors[error_entry->value] = nullptr; - } - for (uint32_t i = 0, count = ira->codegen->errors_by_index.length; i < count; i += 1) { - assert(errors[i] == nullptr); - } - for (uint32_t i = 0; i < cur_type->data.error_set.err_count; i += 1) { - ErrorTableEntry *error_entry = cur_type->data.error_set.errors[i]; - assert(errors[error_entry->value] == nullptr); - errors[error_entry->value] = error_entry; - } - bool cur_is_superset = true; - for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { - ErrorTableEntry *contained_error_entry = err_set_type->data.error_set.errors[i]; - ErrorTableEntry *error_entry = errors[contained_error_entry->value]; - if (error_entry == nullptr) { - cur_is_superset = false; - break; - } - } - if (cur_is_superset) { - err_set_type = cur_type; - prev_inst = cur_inst; - assert(errors != nullptr); - continue; - } - - // neither of them are supersets. so we invent a new error set type that is a union of both of them - err_set_type = get_error_set_union(ira->codegen, errors, cur_type, err_set_type, nullptr); - assert(errors != nullptr); - continue; - } else if (cur_type->id == ZigTypeIdErrorUnion) { - if (type_is_global_error_set(err_set_type)) { - prev_inst = cur_inst; - continue; - } - ZigType *cur_err_set_type = cur_type->data.error_union.err_set_type; - bool allow_infer = cur_err_set_type->data.error_set.infer_fn != nullptr && - cur_err_set_type->data.error_set.infer_fn == ira->new_irb.exec->fn_entry; - if (!allow_infer && !resolve_inferred_error_set(ira->codegen, cur_err_set_type, cur_inst->base.source_node)) { - return ira->codegen->builtin_types.entry_invalid; - } - if (!allow_infer && type_is_global_error_set(cur_err_set_type)) { - err_set_type = ira->codegen->builtin_types.entry_global_error_set; - prev_inst = cur_inst; - continue; - } - - update_errors_helper(ira->codegen, &errors, &errors_count); - - // test if err_set_type is a subset of cur_type's error set - // unset everything in errors - for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { - ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i]; - errors[error_entry->value] = nullptr; - } - for (uint32_t i = 0, count = ira->codegen->errors_by_index.length; i < count; i += 1) { - assert(errors[i] == nullptr); - } - for (uint32_t i = 0; i < cur_err_set_type->data.error_set.err_count; i += 1) { - ErrorTableEntry *error_entry = cur_err_set_type->data.error_set.errors[i]; - assert(errors[error_entry->value] == nullptr); - errors[error_entry->value] = error_entry; - } - bool cur_is_superset = true; - for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { - ErrorTableEntry *contained_error_entry = err_set_type->data.error_set.errors[i]; - ErrorTableEntry *error_entry = errors[contained_error_entry->value]; - if (error_entry == nullptr) { - cur_is_superset = false; - break; - } - } - if (cur_is_superset) { - err_set_type = cur_err_set_type; - prev_inst = cur_inst; - assert(errors != nullptr); - continue; - } - - // not a subset. invent new error set type, union of both of them - err_set_type = get_error_set_union(ira->codegen, errors, cur_err_set_type, err_set_type, nullptr); - prev_inst = cur_inst; - assert(errors != nullptr); - continue; - } else { - prev_inst = cur_inst; - continue; - } - } - - if (cur_type->id == ZigTypeIdErrorSet) { - bool allow_infer = cur_type->data.error_set.infer_fn != nullptr && - cur_type->data.error_set.infer_fn == ira->new_irb.exec->fn_entry; - if (!allow_infer && !resolve_inferred_error_set(ira->codegen, cur_type, cur_inst->base.source_node)) { - return ira->codegen->builtin_types.entry_invalid; - } - if (!allow_infer && type_is_global_error_set(cur_type)) { - err_set_type = ira->codegen->builtin_types.entry_global_error_set; - continue; - } - if (err_set_type != nullptr && type_is_global_error_set(err_set_type)) { - continue; - } - - update_errors_helper(ira->codegen, &errors, &errors_count); - - if (err_set_type == nullptr) { - bool allow_infer = false; - if (prev_type->id == ZigTypeIdErrorUnion) { - err_set_type = prev_type->data.error_union.err_set_type; - allow_infer = err_set_type->data.error_set.infer_fn != nullptr && - err_set_type->data.error_set.infer_fn == ira->new_irb.exec->fn_entry; - } else { - err_set_type = cur_type; - } - - if (!allow_infer && !resolve_inferred_error_set(ira->codegen, err_set_type, cur_inst->base.source_node)) { - return ira->codegen->builtin_types.entry_invalid; - } - - if (!allow_infer && type_is_global_error_set(err_set_type)) { - err_set_type = ira->codegen->builtin_types.entry_global_error_set; - continue; - } - - update_errors_helper(ira->codegen, &errors, &errors_count); - - for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { - ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i]; - assert(errors[error_entry->value] == nullptr); - errors[error_entry->value] = error_entry; - } - if (err_set_type == cur_type) { - continue; - } - } - // check if the cur type error set is a subset - bool prev_is_superset = true; - for (uint32_t i = 0; i < cur_type->data.error_set.err_count; i += 1) { - ErrorTableEntry *contained_error_entry = cur_type->data.error_set.errors[i]; - ErrorTableEntry *error_entry = errors[contained_error_entry->value]; - if (error_entry == nullptr) { - prev_is_superset = false; - break; - } - } - if (prev_is_superset) { - continue; - } - // not a subset. invent new error set type, union of both of them - err_set_type = get_error_set_union(ira->codegen, errors, err_set_type, cur_type, nullptr); - assert(errors != nullptr); - continue; - } - - if (prev_type->id == ZigTypeIdErrorUnion && cur_type->id == ZigTypeIdErrorUnion) { - ZigType *prev_payload_type = prev_type->data.error_union.payload_type; - ZigType *cur_payload_type = cur_type->data.error_union.payload_type; - - bool const_cast_prev = types_match_const_cast_only(ira, prev_payload_type, cur_payload_type, - source_node, false).id == ConstCastResultIdOk; - bool const_cast_cur = types_match_const_cast_only(ira, cur_payload_type, prev_payload_type, - source_node, false).id == ConstCastResultIdOk; - - if (const_cast_prev || const_cast_cur) { - if (const_cast_cur) { - prev_inst = cur_inst; - } - - ZigType *prev_err_set_type = (err_set_type == nullptr) ? prev_type->data.error_union.err_set_type : err_set_type; - ZigType *cur_err_set_type = cur_type->data.error_union.err_set_type; - if (prev_err_set_type == cur_err_set_type) - continue; - - bool allow_infer_prev = prev_err_set_type->data.error_set.infer_fn != nullptr && - prev_err_set_type->data.error_set.infer_fn == ira->new_irb.exec->fn_entry; - bool allow_infer_cur = cur_err_set_type->data.error_set.infer_fn != nullptr && - cur_err_set_type->data.error_set.infer_fn == ira->new_irb.exec->fn_entry; - - if (!allow_infer_prev && !resolve_inferred_error_set(ira->codegen, prev_err_set_type, cur_inst->base.source_node)) { - return ira->codegen->builtin_types.entry_invalid; - } - - if (!allow_infer_cur && !resolve_inferred_error_set(ira->codegen, cur_err_set_type, cur_inst->base.source_node)) { - return ira->codegen->builtin_types.entry_invalid; - } - - if ((!allow_infer_prev && type_is_global_error_set(prev_err_set_type)) || - (!allow_infer_cur && type_is_global_error_set(cur_err_set_type))) - { - err_set_type = ira->codegen->builtin_types.entry_global_error_set; - continue; - } - - update_errors_helper(ira->codegen, &errors, &errors_count); - - if (err_set_type == nullptr) { - err_set_type = prev_err_set_type; - for (uint32_t i = 0; i < prev_err_set_type->data.error_set.err_count; i += 1) { - ErrorTableEntry *error_entry = prev_err_set_type->data.error_set.errors[i]; - assert(errors[error_entry->value] == nullptr); - errors[error_entry->value] = error_entry; - } - } - bool prev_is_superset = true; - for (uint32_t i = 0; i < cur_err_set_type->data.error_set.err_count; i += 1) { - ErrorTableEntry *contained_error_entry = cur_err_set_type->data.error_set.errors[i]; - ErrorTableEntry *error_entry = errors[contained_error_entry->value]; - if (error_entry == nullptr) { - prev_is_superset = false; - break; - } - } - if (prev_is_superset) { - continue; - } - // unset all the errors - for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { - ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i]; - errors[error_entry->value] = nullptr; - } - for (uint32_t i = 0, count = ira->codegen->errors_by_index.length; i < count; i += 1) { - assert(errors[i] == nullptr); - } - for (uint32_t i = 0; i < cur_err_set_type->data.error_set.err_count; i += 1) { - ErrorTableEntry *error_entry = cur_err_set_type->data.error_set.errors[i]; - assert(errors[error_entry->value] == nullptr); - errors[error_entry->value] = error_entry; - } - bool cur_is_superset = true; - for (uint32_t i = 0; i < prev_err_set_type->data.error_set.err_count; i += 1) { - ErrorTableEntry *contained_error_entry = prev_err_set_type->data.error_set.errors[i]; - ErrorTableEntry *error_entry = errors[contained_error_entry->value]; - if (error_entry == nullptr) { - cur_is_superset = false; - break; - } - } - if (cur_is_superset) { - err_set_type = cur_err_set_type; - continue; - } - - err_set_type = get_error_set_union(ira->codegen, errors, cur_err_set_type, prev_err_set_type, nullptr); - continue; - } - } - - if (prev_type->id == ZigTypeIdNull) { - prev_inst = cur_inst; - any_are_null = true; - continue; - } - - if (cur_type->id == ZigTypeIdNull) { - any_are_null = true; - continue; - } - - if (prev_type->id == ZigTypeIdEnum && cur_type->id == ZigTypeIdEnumLiteral) { - TypeEnumField *field = find_enum_type_field(prev_type, cur_inst->value->data.x_enum_literal); - if (field != nullptr) { - continue; - } - } - if (is_tagged_union(prev_type) && cur_type->id == ZigTypeIdEnumLiteral) { - TypeUnionField *field = find_union_type_field(prev_type, cur_inst->value->data.x_enum_literal); - if (field != nullptr) { - continue; - } - } - - if (cur_type->id == ZigTypeIdEnum && prev_type->id == ZigTypeIdEnumLiteral) { - TypeEnumField *field = find_enum_type_field(cur_type, prev_inst->value->data.x_enum_literal); - if (field != nullptr) { - prev_inst = cur_inst; - continue; - } - } - - if (is_tagged_union(cur_type) && prev_type->id == ZigTypeIdEnumLiteral) { - TypeUnionField *field = find_union_type_field(cur_type, prev_inst->value->data.x_enum_literal); - if (field != nullptr) { - prev_inst = cur_inst; - continue; - } - } - - if (prev_type->id == ZigTypeIdPointer && prev_type->data.pointer.ptr_len == PtrLenC && - (cur_type->id == ZigTypeIdComptimeInt || cur_type->id == ZigTypeIdInt)) - { - continue; - } - - if (cur_type->id == ZigTypeIdPointer && cur_type->data.pointer.ptr_len == PtrLenC && - (prev_type->id == ZigTypeIdComptimeInt || prev_type->id == ZigTypeIdInt)) - { - prev_inst = cur_inst; - continue; - } - - if (prev_type->id == ZigTypeIdPointer && cur_type->id == ZigTypeIdPointer) { - if (prev_type->data.pointer.ptr_len == PtrLenC && - types_match_const_cast_only(ira, prev_type->data.pointer.child_type, - cur_type->data.pointer.child_type, source_node, - !prev_type->data.pointer.is_const).id == ConstCastResultIdOk) - { - continue; - } - if (cur_type->data.pointer.ptr_len == PtrLenC && - types_match_const_cast_only(ira, cur_type->data.pointer.child_type, - prev_type->data.pointer.child_type, source_node, - !cur_type->data.pointer.is_const).id == ConstCastResultIdOk) - { - prev_inst = cur_inst; - continue; - } - } - - if (types_match_const_cast_only(ira, prev_type, cur_type, source_node, false).id == ConstCastResultIdOk) { - continue; - } - - if (types_match_const_cast_only(ira, cur_type, prev_type, source_node, false).id == ConstCastResultIdOk) { - prev_inst = cur_inst; - continue; - } - - if (prev_type->id == ZigTypeIdInt && - cur_type->id == ZigTypeIdInt && - prev_type->data.integral.is_signed == cur_type->data.integral.is_signed) - { - if (cur_type->data.integral.bit_count > prev_type->data.integral.bit_count) { - prev_inst = cur_inst; - } - continue; - } - - if (prev_type->id == ZigTypeIdFloat && cur_type->id == ZigTypeIdFloat) { - if (cur_type->data.floating.bit_count > prev_type->data.floating.bit_count) { - prev_inst = cur_inst; - } - continue; - } - - if (prev_type->id == ZigTypeIdErrorUnion && - types_match_const_cast_only(ira, prev_type->data.error_union.payload_type, cur_type, - source_node, false).id == ConstCastResultIdOk) - { - continue; - } - - if (cur_type->id == ZigTypeIdErrorUnion && - types_match_const_cast_only(ira, cur_type->data.error_union.payload_type, prev_type, - source_node, false).id == ConstCastResultIdOk) - { - if (err_set_type != nullptr) { - ZigType *cur_err_set_type = cur_type->data.error_union.err_set_type; - bool allow_infer = cur_err_set_type->data.error_set.infer_fn != nullptr && - cur_err_set_type->data.error_set.infer_fn == ira->new_irb.exec->fn_entry; - if (!allow_infer && !resolve_inferred_error_set(ira->codegen, cur_err_set_type, cur_inst->base.source_node)) { - return ira->codegen->builtin_types.entry_invalid; - } - if ((!allow_infer && type_is_global_error_set(cur_err_set_type)) || - type_is_global_error_set(err_set_type)) - { - err_set_type = ira->codegen->builtin_types.entry_global_error_set; - prev_inst = cur_inst; - continue; - } - - update_errors_helper(ira->codegen, &errors, &errors_count); - - err_set_type = get_error_set_union(ira->codegen, errors, err_set_type, cur_err_set_type, nullptr); - } - prev_inst = cur_inst; - continue; - } - - if (prev_type->id == ZigTypeIdOptional && - types_match_const_cast_only(ira, prev_type->data.maybe.child_type, cur_type, - source_node, false).id == ConstCastResultIdOk) - { - continue; - } - - if (cur_type->id == ZigTypeIdOptional && - types_match_const_cast_only(ira, cur_type->data.maybe.child_type, prev_type, - source_node, false).id == ConstCastResultIdOk) - { - prev_inst = cur_inst; - continue; - } - - if (prev_type->id == ZigTypeIdOptional && - types_match_const_cast_only(ira, cur_type, prev_type->data.maybe.child_type, - source_node, false).id == ConstCastResultIdOk) - { - prev_inst = cur_inst; - any_are_null = true; - continue; - } - - if (cur_type->id == ZigTypeIdOptional && - types_match_const_cast_only(ira, prev_type, cur_type->data.maybe.child_type, - source_node, false).id == ConstCastResultIdOk) - { - any_are_null = true; - continue; - } - - if (cur_type->id == ZigTypeIdUndefined) { - continue; - } - - if (prev_type->id == ZigTypeIdUndefined) { - prev_inst = cur_inst; - continue; - } - - if (prev_type->id == ZigTypeIdComptimeInt || - prev_type->id == ZigTypeIdComptimeFloat) - { - if (ir_num_lit_fits_in_other_type(ira, prev_inst, cur_type, false)) { - prev_inst = cur_inst; - continue; - } else { - return ira->codegen->builtin_types.entry_invalid; - } - } - - if (cur_type->id == ZigTypeIdComptimeInt || - cur_type->id == ZigTypeIdComptimeFloat) - { - if (ir_num_lit_fits_in_other_type(ira, cur_inst, prev_type, false)) { - continue; - } else { - return ira->codegen->builtin_types.entry_invalid; - } - } - - // *[N]T to [*]T - if (prev_type->id == ZigTypeIdPointer && - prev_type->data.pointer.ptr_len == PtrLenSingle && - prev_type->data.pointer.child_type->id == ZigTypeIdArray && - ((cur_type->id == ZigTypeIdPointer && cur_type->data.pointer.ptr_len == PtrLenUnknown))) - { - convert_to_const_slice = false; - prev_inst = cur_inst; - - if (prev_type->data.pointer.is_const && !cur_type->data.pointer.is_const) { - // const array pointer and non-const unknown pointer - make_the_pointer_const = true; - } - continue; - } - - // *[N]T to [*]T - if (cur_type->id == ZigTypeIdPointer && - cur_type->data.pointer.ptr_len == PtrLenSingle && - cur_type->data.pointer.child_type->id == ZigTypeIdArray && - ((prev_type->id == ZigTypeIdPointer && prev_type->data.pointer.ptr_len == PtrLenUnknown))) - { - if (cur_type->data.pointer.is_const && !prev_type->data.pointer.is_const) { - // const array pointer and non-const unknown pointer - make_the_pointer_const = true; - } - continue; - } - - // *[N]T to []T - // *[N]T to E![]T - if (cur_type->id == ZigTypeIdPointer && - cur_type->data.pointer.ptr_len == PtrLenSingle && - cur_type->data.pointer.child_type->id == ZigTypeIdArray && - ((prev_type->id == ZigTypeIdErrorUnion && is_slice(prev_type->data.error_union.payload_type)) || - is_slice(prev_type))) - { - ZigType *array_type = cur_type->data.pointer.child_type; - ZigType *slice_type = (prev_type->id == ZigTypeIdErrorUnion) ? - prev_type->data.error_union.payload_type : prev_type; - ZigType *slice_ptr_type = slice_type->data.structure.fields[slice_ptr_index]->type_entry; - if (types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type, - array_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk) - { - bool const_ok = (slice_ptr_type->data.pointer.is_const || array_type->data.array.len == 0 || - !cur_type->data.pointer.is_const); - if (!const_ok) make_the_slice_const = true; - convert_to_const_slice = false; - continue; - } - } - - // *[N]T to []T - // *[N]T to E![]T - if (prev_type->id == ZigTypeIdPointer && - prev_type->data.pointer.child_type->id == ZigTypeIdArray && - prev_type->data.pointer.ptr_len == PtrLenSingle && - ((cur_type->id == ZigTypeIdErrorUnion && is_slice(cur_type->data.error_union.payload_type)) || - (cur_type->id == ZigTypeIdOptional && is_slice(cur_type->data.maybe.child_type)) || - is_slice(cur_type))) - { - ZigType *array_type = prev_type->data.pointer.child_type; - ZigType *slice_type; - switch (cur_type->id) { - case ZigTypeIdErrorUnion: - slice_type = cur_type->data.error_union.payload_type; - break; - case ZigTypeIdOptional: - slice_type = cur_type->data.maybe.child_type; - break; - default: - slice_type = cur_type; - break; - } - ZigType *slice_ptr_type = slice_type->data.structure.fields[slice_ptr_index]->type_entry; - if (types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type, - array_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk) - { - bool const_ok = (slice_ptr_type->data.pointer.is_const || array_type->data.array.len == 0 || - !prev_type->data.pointer.is_const); - if (!const_ok) make_the_slice_const = true; - prev_inst = cur_inst; - convert_to_const_slice = false; - continue; - } - } - - // *[N]T and *[M]T - if (cur_type->id == ZigTypeIdPointer && cur_type->data.pointer.ptr_len == PtrLenSingle && - cur_type->data.pointer.child_type->id == ZigTypeIdArray && - prev_type->id == ZigTypeIdPointer && prev_type->data.pointer.ptr_len == PtrLenSingle && - prev_type->data.pointer.child_type->id == ZigTypeIdArray && - ( - prev_type->data.pointer.child_type->data.array.sentinel == nullptr || - (cur_type->data.pointer.child_type->data.array.sentinel != nullptr && - const_values_equal(ira->codegen, prev_type->data.pointer.child_type->data.array.sentinel, - cur_type->data.pointer.child_type->data.array.sentinel)) - ) && - types_match_const_cast_only(ira, - cur_type->data.pointer.child_type->data.array.child_type, - prev_type->data.pointer.child_type->data.array.child_type, - source_node, !cur_type->data.pointer.is_const).id == ConstCastResultIdOk) - { - bool const_ok = (cur_type->data.pointer.is_const || !prev_type->data.pointer.is_const || - prev_type->data.pointer.child_type->data.array.len == 0); - if (!const_ok) make_the_slice_const = true; - prev_inst = cur_inst; - convert_to_const_slice = true; - continue; - } - if (prev_type->id == ZigTypeIdPointer && prev_type->data.pointer.ptr_len == PtrLenSingle && - prev_type->data.pointer.child_type->id == ZigTypeIdArray && - cur_type->id == ZigTypeIdPointer && cur_type->data.pointer.ptr_len == PtrLenSingle && - cur_type->data.pointer.child_type->id == ZigTypeIdArray && - ( - cur_type->data.pointer.child_type->data.array.sentinel == nullptr || - (prev_type->data.pointer.child_type->data.array.sentinel != nullptr && - const_values_equal(ira->codegen, cur_type->data.pointer.child_type->data.array.sentinel, - prev_type->data.pointer.child_type->data.array.sentinel)) - ) && - types_match_const_cast_only(ira, - prev_type->data.pointer.child_type->data.array.child_type, - cur_type->data.pointer.child_type->data.array.child_type, - source_node, !prev_type->data.pointer.is_const).id == ConstCastResultIdOk) - { - bool const_ok = (prev_type->data.pointer.is_const || !cur_type->data.pointer.is_const || - cur_type->data.pointer.child_type->data.array.len == 0); - if (!const_ok) make_the_slice_const = true; - convert_to_const_slice = true; - continue; - } - - if (prev_type->id == ZigTypeIdEnum && cur_type->id == ZigTypeIdUnion && - (cur_type->data.unionation.decl_node->data.container_decl.auto_enum || cur_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr)) - { - if ((err = type_resolve(ira->codegen, cur_type, ResolveStatusZeroBitsKnown))) - return ira->codegen->builtin_types.entry_invalid; - if (cur_type->data.unionation.tag_type == prev_type) { - continue; - } - } - - if (cur_type->id == ZigTypeIdEnum && prev_type->id == ZigTypeIdUnion && - (prev_type->data.unionation.decl_node->data.container_decl.auto_enum || prev_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr)) - { - if ((err = type_resolve(ira->codegen, prev_type, ResolveStatusZeroBitsKnown))) - return ira->codegen->builtin_types.entry_invalid; - if (prev_type->data.unionation.tag_type == cur_type) { - prev_inst = cur_inst; - continue; - } - } - - ErrorMsg *msg = ir_add_error_node(ira, source_node, - buf_sprintf("incompatible types: '%s' and '%s'", - buf_ptr(&prev_type->name), buf_ptr(&cur_type->name))); - add_error_note(ira->codegen, msg, prev_inst->base.source_node, - buf_sprintf("type '%s' here", buf_ptr(&prev_type->name))); - add_error_note(ira->codegen, msg, cur_inst->base.source_node, - buf_sprintf("type '%s' here", buf_ptr(&cur_type->name))); - - return ira->codegen->builtin_types.entry_invalid; - } - - heap::c_allocator.deallocate(errors, errors_count); - - if (convert_to_const_slice) { - if (prev_inst->value->type->id == ZigTypeIdPointer) { - ZigType *array_type = prev_inst->value->type->data.pointer.child_type; - src_assert(array_type->id == ZigTypeIdArray, source_node); - ZigType *ptr_type = get_pointer_to_type_extra2( - ira->codegen, array_type->data.array.child_type, - prev_inst->value->type->data.pointer.is_const || make_the_slice_const, false, - PtrLenUnknown, - 0, 0, 0, false, - VECTOR_INDEX_NONE, nullptr, array_type->data.array.sentinel); - ZigType *slice_type = get_slice_type(ira->codegen, ptr_type); - if (err_set_type != nullptr) { - return get_error_union_type(ira->codegen, err_set_type, slice_type); - } else { - return slice_type; - } - } else { - zig_unreachable(); - } - } else if (err_set_type != nullptr) { - if (prev_inst->value->type->id == ZigTypeIdErrorSet) { - return err_set_type; - } else if (prev_inst->value->type->id == ZigTypeIdErrorUnion) { - ZigType *payload_type = prev_inst->value->type->data.error_union.payload_type; - if ((err = type_resolve(ira->codegen, payload_type, ResolveStatusSizeKnown))) - return ira->codegen->builtin_types.entry_invalid; - return get_error_union_type(ira->codegen, err_set_type, payload_type); - } else if (expected_type != nullptr && expected_type->id == ZigTypeIdErrorUnion) { - ZigType *payload_type = expected_type->data.error_union.payload_type; - if ((err = type_resolve(ira->codegen, payload_type, ResolveStatusSizeKnown))) - return ira->codegen->builtin_types.entry_invalid; - return get_error_union_type(ira->codegen, err_set_type, payload_type); - } else { - if (prev_inst->value->type->id == ZigTypeIdComptimeInt || - prev_inst->value->type->id == ZigTypeIdComptimeFloat) - { - ir_add_error_node(ira, source_node, - buf_sprintf("unable to make error union out of number literal")); - return ira->codegen->builtin_types.entry_invalid; - } else if (prev_inst->value->type->id == ZigTypeIdNull) { - ir_add_error_node(ira, source_node, - buf_sprintf("unable to make error union out of null literal")); - return ira->codegen->builtin_types.entry_invalid; - } else { - if ((err = type_resolve(ira->codegen, prev_inst->value->type, ResolveStatusSizeKnown))) - return ira->codegen->builtin_types.entry_invalid; - return get_error_union_type(ira->codegen, err_set_type, prev_inst->value->type); - } - } - } else if (any_are_null && prev_inst->value->type->id != ZigTypeIdNull) { - if (prev_inst->value->type->id == ZigTypeIdOptional) { - return prev_inst->value->type; - } else { - if ((err = type_resolve(ira->codegen, prev_inst->value->type, ResolveStatusSizeKnown))) - return ira->codegen->builtin_types.entry_invalid; - return get_optional_type(ira->codegen, prev_inst->value->type); - } - } else if (make_the_slice_const) { - ZigType *slice_type; - if (prev_inst->value->type->id == ZigTypeIdErrorUnion) { - slice_type = prev_inst->value->type->data.error_union.payload_type; - } else if (is_slice(prev_inst->value->type)) { - slice_type = prev_inst->value->type; - } else { - zig_unreachable(); - } - ZigType *slice_ptr_type = slice_type->data.structure.fields[slice_ptr_index]->type_entry; - ZigType *adjusted_ptr_type = adjust_ptr_const(ira->codegen, slice_ptr_type, make_the_slice_const); - ZigType *adjusted_slice_type = get_slice_type(ira->codegen, adjusted_ptr_type); - if (prev_inst->value->type->id == ZigTypeIdErrorUnion) { - return get_error_union_type(ira->codegen, prev_inst->value->type->data.error_union.err_set_type, - adjusted_slice_type); - } else if (is_slice(prev_inst->value->type)) { - return adjusted_slice_type; - } else { - zig_unreachable(); - } - } else if (make_the_pointer_const) { - return adjust_ptr_const(ira->codegen, prev_inst->value->type, make_the_pointer_const); - } else { - return prev_inst->value->type; - } -} - -static bool eval_const_expr_implicit_cast(IrAnalyze *ira, IrInst *source_instr, - CastOp cast_op, - ZigValue *other_val, ZigType *other_type, - ZigValue *const_val, ZigType *new_type) -{ - const_val->special = other_val->special; - - assert(other_val != const_val); - switch (cast_op) { - case CastOpNoCast: - zig_unreachable(); - case CastOpErrSet: - case CastOpBitCast: - zig_panic("TODO"); - case CastOpNoop: { - copy_const_val(ira->codegen, const_val, other_val); - const_val->type = new_type; - break; - } - case CastOpNumLitToConcrete: - if (other_val->type->id == ZigTypeIdComptimeFloat) { - assert(new_type->id == ZigTypeIdFloat); - switch (new_type->data.floating.bit_count) { - case 16: - const_val->data.x_f16 = bigfloat_to_f16(&other_val->data.x_bigfloat); - break; - case 32: - const_val->data.x_f32 = bigfloat_to_f32(&other_val->data.x_bigfloat); - break; - case 64: - const_val->data.x_f64 = bigfloat_to_f64(&other_val->data.x_bigfloat); - break; - case 80: - zig_panic("TODO"); - case 128: - const_val->data.x_f128 = bigfloat_to_f128(&other_val->data.x_bigfloat); - break; - default: - zig_unreachable(); - } - } else if (other_val->type->id == ZigTypeIdComptimeInt) { - bigint_init_bigint(&const_val->data.x_bigint, &other_val->data.x_bigint); - } else { - zig_unreachable(); - } - const_val->type = new_type; - break; - case CastOpIntToFloat: - if (new_type->id == ZigTypeIdFloat) { - BigFloat bigfloat; - bigfloat_init_bigint(&bigfloat, &other_val->data.x_bigint); - switch (new_type->data.floating.bit_count) { - case 16: - const_val->data.x_f16 = bigfloat_to_f16(&bigfloat); - break; - case 32: - const_val->data.x_f32 = bigfloat_to_f32(&bigfloat); - break; - case 64: - const_val->data.x_f64 = bigfloat_to_f64(&bigfloat); - break; - case 80: - zig_panic("TODO"); - case 128: - const_val->data.x_f128 = bigfloat_to_f128(&bigfloat); - break; - default: - zig_unreachable(); - } - } else if (new_type->id == ZigTypeIdComptimeFloat) { - bigfloat_init_bigint(&const_val->data.x_bigfloat, &other_val->data.x_bigint); - } else { - zig_unreachable(); - } - const_val->special = ConstValSpecialStatic; - break; - case CastOpFloatToInt: - float_init_bigint(&const_val->data.x_bigint, other_val); - if (new_type->id == ZigTypeIdInt) { - if (!bigint_fits_in_bits(&const_val->data.x_bigint, new_type->data.integral.bit_count, - new_type->data.integral.is_signed)) - { - Buf *int_buf = buf_alloc(); - bigint_append_buf(int_buf, &const_val->data.x_bigint, 10); - - ir_add_error(ira, source_instr, - buf_sprintf("integer value '%s' cannot be stored in type '%s'", - buf_ptr(int_buf), buf_ptr(&new_type->name))); - return false; - } - } - - const_val->special = ConstValSpecialStatic; - break; - case CastOpBoolToInt: - bigint_init_unsigned(&const_val->data.x_bigint, other_val->data.x_bool ? 1 : 0); - const_val->special = ConstValSpecialStatic; - break; - } - return true; -} - -static IrInstGen *ir_const(IrAnalyze *ira, IrInst *inst, ZigType *ty) { - IrInstGenConst *const_instruction = ir_create_inst_gen(&ira->new_irb, - inst->scope, inst->source_node); - IrInstGen *new_instruction = &const_instruction->base; - new_instruction->value->type = ty; - new_instruction->value->special = ConstValSpecialStatic; - ira->new_irb.constants.append(&heap::c_allocator, const_instruction); - return new_instruction; -} - -static IrInstGen *ir_const_noval(IrAnalyze *ira, IrInst *old_instruction) { - IrInstGenConst *const_instruction = ir_create_inst_noval(&ira->new_irb, - old_instruction->scope, old_instruction->source_node); - ira->new_irb.constants.append(&heap::c_allocator, const_instruction); - return &const_instruction->base; -} - -// This function initializes the new IrInstGen with the provided ZigValue, -// rather than creating a new one. -static IrInstGen *ir_const_move(IrAnalyze *ira, IrInst *old_instruction, ZigValue *val) { - IrInstGen *result = ir_const_noval(ira, old_instruction); - result->value = val; - return result; -} - -static IrInstGen *ir_resolve_cast(IrAnalyze *ira, IrInst *source_instr, IrInstGen *value, - ZigType *wanted_type, CastOp cast_op) -{ - if (instr_is_comptime(value) || !type_has_bits(ira->codegen, wanted_type)) { - IrInstGen *result = ir_const(ira, source_instr, wanted_type); - ZigValue *val = ir_resolve_const(ira, value, UndefBad); - if (val == nullptr) - return ira->codegen->invalid_inst_gen; - - if (!eval_const_expr_implicit_cast(ira, source_instr, cast_op, val, val->type, - result->value, wanted_type)) - { - return ira->codegen->invalid_inst_gen; - } - return result; - } else { - return ir_build_cast(ira, source_instr, wanted_type, value, cast_op); - } -} - -static IrInstGen *ir_resolve_ptr_of_array_to_unknown_len_ptr(IrAnalyze *ira, IrInst* source_instr, - IrInstGen *value, ZigType *wanted_type) -{ - ir_assert(value->value->type->id == ZigTypeIdPointer, source_instr); - - Error err; - - if ((err = type_resolve(ira->codegen, value->value->type->data.pointer.child_type, - ResolveStatusAlignmentKnown))) - { - return ira->codegen->invalid_inst_gen; - } - - wanted_type = adjust_ptr_align(ira->codegen, wanted_type, get_ptr_align(ira->codegen, value->value->type)); - - if (instr_is_comptime(value)) { - ZigValue *val = ir_resolve_const(ira, value, UndefOk); - if (val == nullptr) - return ira->codegen->invalid_inst_gen; - if (val->special == ConstValSpecialUndef) - return ir_const_undef(ira, source_instr, wanted_type); - - ZigValue *pointee = const_ptr_pointee(ira, ira->codegen, val, source_instr->source_node); - if (pointee == nullptr) - return ira->codegen->invalid_inst_gen; - if (pointee->special != ConstValSpecialRuntime) { - IrInstGen *result = ir_const(ira, source_instr, wanted_type); - result->value->data.x_ptr.special = ConstPtrSpecialBaseArray; - result->value->data.x_ptr.mut = val->data.x_ptr.mut; - result->value->data.x_ptr.data.base_array.array_val = pointee; - result->value->data.x_ptr.data.base_array.elem_index = 0; - return result; - } - } - - return ir_build_cast(ira, source_instr, wanted_type, value, CastOpBitCast); -} - -static IrInstGen *ir_resolve_ptr_of_array_to_slice(IrAnalyze *ira, IrInst* source_instr, - IrInstGen *array_ptr, ZigType *wanted_type, ResultLoc *result_loc) -{ - Error err; - - assert(array_ptr->value->type->id == ZigTypeIdPointer); - assert(array_ptr->value->type->data.pointer.child_type->id == ZigTypeIdArray); - - ZigType *array_type = array_ptr->value->type->data.pointer.child_type; - size_t array_len = array_type->data.array.len; - - // A zero-sized array can be casted regardless of the destination alignment, or - // whether the pointer is undefined, and the result is always comptime known. - // TODO However, this is exposing a result location bug that I failed to solve on the first try. - // If you want to try to fix the bug, uncomment this block and get the tests passing. - //if (array_len == 0 && array_type->data.array.sentinel == nullptr) { - // ZigValue *undef_array = ira->codegen->pass1_arena->create(); - // undef_array->special = ConstValSpecialUndef; - // undef_array->type = array_type; - - // IrInstGen *result = ir_const(ira, source_instr, wanted_type); - // init_const_slice(ira->codegen, result->value, undef_array, 0, 0, false); - // result->value->data.x_struct.fields[slice_ptr_index]->data.x_ptr.mut = ConstPtrMutComptimeConst; - // result->value->type = wanted_type; - // return result; - //} - - if ((err = type_resolve(ira->codegen, array_ptr->value->type, ResolveStatusAlignmentKnown))) { - return ira->codegen->invalid_inst_gen; - } - - if (array_len != 0) { - wanted_type = adjust_slice_align(ira->codegen, wanted_type, - get_ptr_align(ira->codegen, array_ptr->value->type)); - } - - if (instr_is_comptime(array_ptr)) { - UndefAllowed undef_allowed = (array_len == 0) ? UndefOk : UndefBad; - ZigValue *array_ptr_val = ir_resolve_const(ira, array_ptr, undef_allowed); - if (array_ptr_val == nullptr) - return ira->codegen->invalid_inst_gen; - ir_assert(is_slice(wanted_type), source_instr); - if (array_ptr_val->special == ConstValSpecialUndef) { - ZigValue *undef_array = ira->codegen->pass1_arena->create(); - undef_array->special = ConstValSpecialUndef; - undef_array->type = array_type; - - IrInstGen *result = ir_const(ira, source_instr, wanted_type); - init_const_slice(ira->codegen, result->value, undef_array, 0, 0, false); - result->value->data.x_struct.fields[slice_ptr_index]->data.x_ptr.mut = ConstPtrMutComptimeConst; - result->value->type = wanted_type; - return result; - } - bool wanted_const = wanted_type->data.structure.fields[slice_ptr_index]->type_entry->data.pointer.is_const; - // Optimization to avoid creating unnecessary ZigValue in const_ptr_pointee - if (array_ptr_val->data.x_ptr.special == ConstPtrSpecialSubArray) { - ZigValue *array_val = array_ptr_val->data.x_ptr.data.base_array.array_val; - if (array_val->special != ConstValSpecialRuntime) { - IrInstGen *result = ir_const(ira, source_instr, wanted_type); - init_const_slice(ira->codegen, result->value, array_val, - array_ptr_val->data.x_ptr.data.base_array.elem_index, - array_type->data.array.len, wanted_const); - result->value->data.x_struct.fields[slice_ptr_index]->data.x_ptr.mut = array_ptr_val->data.x_ptr.mut; - result->value->type = wanted_type; - return result; - } - } else if (array_ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { - ZigValue *pointee = const_ptr_pointee(ira, ira->codegen, array_ptr_val, source_instr->source_node); - if (pointee == nullptr) - return ira->codegen->invalid_inst_gen; - if (pointee->special != ConstValSpecialRuntime) { - assert(array_ptr_val->type->id == ZigTypeIdPointer); - - IrInstGen *result = ir_const(ira, source_instr, wanted_type); - init_const_slice(ira->codegen, result->value, pointee, 0, array_type->data.array.len, wanted_const); - result->value->data.x_struct.fields[slice_ptr_index]->data.x_ptr.mut = array_ptr_val->data.x_ptr.mut; - result->value->type = wanted_type; - return result; - } - } - } - - if (result_loc == nullptr) result_loc = no_result_loc(); - IrInstGen *result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, wanted_type, nullptr, true, true); - if (type_is_invalid(result_loc_inst->value->type) || - result_loc_inst->value->type->id == ZigTypeIdUnreachable) - { - return result_loc_inst; - } - return ir_build_ptr_of_array_to_slice(ira, source_instr, wanted_type, array_ptr, result_loc_inst); -} - -static IrBasicBlockGen *ir_get_new_bb(IrAnalyze *ira, IrBasicBlockSrc *old_bb, IrInst *ref_old_instruction) { - assert(old_bb); - - if (old_bb->child) { - if (ref_old_instruction == nullptr || old_bb->child->ref_instruction != ref_old_instruction) { - return old_bb->child; - } - } - - IrBasicBlockGen *new_bb = ir_build_bb_from(ira, old_bb); - new_bb->ref_instruction = ref_old_instruction; - - return new_bb; -} - -static IrBasicBlockGen *ir_get_new_bb_runtime(IrAnalyze *ira, IrBasicBlockSrc *old_bb, IrInst *ref_old_instruction) { - assert(ref_old_instruction != nullptr); - IrBasicBlockGen *new_bb = ir_get_new_bb(ira, old_bb, ref_old_instruction); - if (new_bb->must_be_comptime_source_instr) { - ErrorMsg *msg = ir_add_error(ira, ref_old_instruction, - buf_sprintf("control flow attempts to use compile-time variable at runtime")); - add_error_note(ira->codegen, msg, new_bb->must_be_comptime_source_instr->source_node, - buf_sprintf("compile-time variable assigned here")); - return nullptr; - } - return new_bb; -} - -static void ir_start_bb(IrAnalyze *ira, IrBasicBlockSrc *old_bb, IrBasicBlockSrc *const_predecessor_bb) { - ir_assert(!old_bb->suspended, (old_bb->instruction_list.length != 0) ? &old_bb->instruction_list.at(0)->base : nullptr); - ira->instruction_index = 0; - ira->old_irb.current_basic_block = old_bb; - ira->const_predecessor_bb = const_predecessor_bb; - ira->old_bb_index = old_bb->index; -} - -static IrInstGen *ira_suspend(IrAnalyze *ira, IrInst *old_instruction, IrBasicBlockSrc *next_bb, - IrSuspendPosition *suspend_pos) -{ - if (ira->codegen->verbose_ir) { - fprintf(stderr, "suspend %s_%" PRIu32 " %s_%" PRIu32 " #%" PRIu32 " (%zu,%zu)\n", - ira->old_irb.current_basic_block->name_hint, - ira->old_irb.current_basic_block->debug_id, - ira->old_irb.exec->basic_block_list.at(ira->old_bb_index)->name_hint, - ira->old_irb.exec->basic_block_list.at(ira->old_bb_index)->debug_id, - ira->old_irb.current_basic_block->instruction_list.at(ira->instruction_index)->base.debug_id, - ira->old_bb_index, ira->instruction_index); - } - suspend_pos->basic_block_index = ira->old_bb_index; - suspend_pos->instruction_index = ira->instruction_index; - - ira->old_irb.current_basic_block->suspended = true; - - // null next_bb means that the caller plans to call ira_resume before returning - if (next_bb != nullptr) { - ira->old_bb_index = next_bb->index; - ira->old_irb.current_basic_block = ira->old_irb.exec->basic_block_list.at(ira->old_bb_index); - assert(ira->old_irb.current_basic_block == next_bb); - ira->instruction_index = 0; - ira->const_predecessor_bb = nullptr; - next_bb->child = ir_get_new_bb_runtime(ira, next_bb, old_instruction); - ira->new_irb.current_basic_block = next_bb->child; - } - return ira->codegen->unreach_instruction; -} - -static IrInstGen *ira_resume(IrAnalyze *ira) { - IrSuspendPosition pos = ira->resume_stack.pop(); - if (ira->codegen->verbose_ir) { - fprintf(stderr, "resume (%zu,%zu) ", pos.basic_block_index, pos.instruction_index); - } - ira->old_bb_index = pos.basic_block_index; - ira->old_irb.current_basic_block = ira->old_irb.exec->basic_block_list.at(ira->old_bb_index); - assert(ira->old_irb.current_basic_block->in_resume_stack); - ira->old_irb.current_basic_block->in_resume_stack = false; - ira->old_irb.current_basic_block->suspended = false; - ira->instruction_index = pos.instruction_index; - assert(pos.instruction_index < ira->old_irb.current_basic_block->instruction_list.length); - if (ira->codegen->verbose_ir) { - fprintf(stderr, "%s_%" PRIu32 " #%" PRIu32 "\n", ira->old_irb.current_basic_block->name_hint, - ira->old_irb.current_basic_block->debug_id, - ira->old_irb.current_basic_block->instruction_list.at(pos.instruction_index)->base.debug_id); - } - ira->const_predecessor_bb = nullptr; - ira->new_irb.current_basic_block = ira->old_irb.current_basic_block->child; - assert(ira->new_irb.current_basic_block != nullptr); - return ira->codegen->unreach_instruction; -} - -static void ir_start_next_bb(IrAnalyze *ira) { - ira->old_bb_index += 1; - - bool need_repeat = true; - for (;;) { - while (ira->old_bb_index < ira->old_irb.exec->basic_block_list.length) { - IrBasicBlockSrc *old_bb = ira->old_irb.exec->basic_block_list.at(ira->old_bb_index); - if (old_bb->child == nullptr && old_bb->suspend_instruction_ref == nullptr) { - ira->old_bb_index += 1; - continue; - } - // if it's already started, or - // if it's a suspended block, - // then skip it - if (old_bb->suspended || - (old_bb->child != nullptr && old_bb->child->instruction_list.length != 0) || - (old_bb->child != nullptr && old_bb->child->already_appended)) - { - ira->old_bb_index += 1; - continue; - } - - // if there is a resume_stack, pop one from there rather than moving on. - // the last item of the resume stack will be a basic block that will - // move on to the next one below - if (ira->resume_stack.length != 0) { - ira_resume(ira); - return; - } - - if (old_bb->child == nullptr) { - old_bb->child = ir_get_new_bb_runtime(ira, old_bb, old_bb->suspend_instruction_ref); - } - ira->new_irb.current_basic_block = old_bb->child; - ir_start_bb(ira, old_bb, nullptr); - return; - } - if (!need_repeat) { - if (ira->resume_stack.length != 0) { - ira_resume(ira); - } - return; - } - need_repeat = false; - ira->old_bb_index = 0; - continue; - } -} - -static void ir_finish_bb(IrAnalyze *ira) { - if (!ira->new_irb.current_basic_block->already_appended) { - ir_append_basic_block_gen(&ira->new_irb, ira->new_irb.current_basic_block); - if (ira->codegen->verbose_ir) { - fprintf(stderr, "append new bb %s_%" PRIu32 "\n", ira->new_irb.current_basic_block->name_hint, - ira->new_irb.current_basic_block->debug_id); - } - } - ira->instruction_index += 1; - while (ira->instruction_index < ira->old_irb.current_basic_block->instruction_list.length) { - IrInstSrc *next_instruction = ira->old_irb.current_basic_block->instruction_list.at(ira->instruction_index); - if (!next_instruction->is_gen) { - ir_add_error(ira, &next_instruction->base, buf_sprintf("unreachable code")); - break; - } - ira->instruction_index += 1; - } - - ir_start_next_bb(ira); -} - -static IrInstGen *ir_unreach_error(IrAnalyze *ira) { - ira->old_bb_index = SIZE_MAX; - if (ira->new_irb.exec->first_err_trace_msg == nullptr) { - ira->new_irb.exec->first_err_trace_msg = ira->codegen->trace_err; - } - return ira->codegen->unreach_instruction; -} - -static bool ir_emit_backward_branch(IrAnalyze *ira, IrInst* source_instruction) { - size_t *bbc = ira->new_irb.exec->backward_branch_count; - size_t *quota = ira->new_irb.exec->backward_branch_quota; - - // If we're already over quota, we've already given an error message for this. - if (*bbc > *quota) { - assert(ira->codegen->errors.length > 0); - return false; - } - - *bbc += 1; - if (*bbc > *quota) { - ir_add_error(ira, source_instruction, - buf_sprintf("evaluation exceeded %" ZIG_PRI_usize " backwards branches", *quota)); - return false; - } - return true; -} - -static IrInstGen *ir_inline_bb(IrAnalyze *ira, IrInst* source_instruction, IrBasicBlockSrc *old_bb) { - if (old_bb->debug_id <= ira->old_irb.current_basic_block->debug_id) { - if (!ir_emit_backward_branch(ira, source_instruction)) - return ir_unreach_error(ira); - } - - old_bb->child = ira->old_irb.current_basic_block->child; - ir_start_bb(ira, old_bb, ira->old_irb.current_basic_block); - return ira->codegen->unreach_instruction; -} - -static IrInstGen *ir_finish_anal(IrAnalyze *ira, IrInstGen *instruction) { - if (instruction->value->type->id == ZigTypeIdUnreachable) - ir_finish_bb(ira); - return instruction; -} - -static IrInstGen *ir_const_fn(IrAnalyze *ira, IrInst *source_instr, ZigFn *fn_entry) { - IrInstGen *result = ir_const(ira, source_instr, fn_entry->type_entry); - result->value->special = ConstValSpecialStatic; - result->value->data.x_ptr.data.fn.fn_entry = fn_entry; - result->value->data.x_ptr.mut = ConstPtrMutComptimeConst; - result->value->data.x_ptr.special = ConstPtrSpecialFunction; - return result; -} - -static IrInstGen *ir_const_bound_fn(IrAnalyze *ira, IrInst *src_inst, ZigFn *fn_entry, IrInstGen *first_arg, - IrInst *first_arg_src) -{ - // This is unfortunately required to avoid improperly freeing first_arg_src - ira_ref(ira); - - IrInstGen *result = ir_const(ira, src_inst, get_bound_fn_type(ira->codegen, fn_entry)); - result->value->data.x_bound_fn.fn = fn_entry; - result->value->data.x_bound_fn.first_arg = first_arg; - result->value->data.x_bound_fn.first_arg_src = first_arg_src; - return result; -} - -static IrInstGen *ir_const_type(IrAnalyze *ira, IrInst *source_instruction, ZigType *ty) { - IrInstGen *result = ir_const(ira, source_instruction, ira->codegen->builtin_types.entry_type); - result->value->data.x_type = ty; - return result; -} - -static IrInstGen *ir_const_bool(IrAnalyze *ira, IrInst *source_instruction, bool value) { - IrInstGen *result = ir_const(ira, source_instruction, ira->codegen->builtin_types.entry_bool); - result->value->data.x_bool = value; - return result; -} - -static IrInstGen *ir_const_undef(IrAnalyze *ira, IrInst *source_instruction, ZigType *ty) { - IrInstGen *result = ir_const(ira, source_instruction, ty); - result->value->special = ConstValSpecialUndef; - return result; -} - -static IrInstGen *ir_const_unreachable(IrAnalyze *ira, IrInst *source_instruction) { - IrInstGen *result = ir_const_noval(ira, source_instruction); - result->value = ira->codegen->intern.for_unreachable(); - return result; -} - -static IrInstGen *ir_const_void(IrAnalyze *ira, IrInst *source_instruction) { - IrInstGen *result = ir_const_noval(ira, source_instruction); - result->value = ira->codegen->intern.for_void(); - return result; -} - -static IrInstGen *ir_const_unsigned(IrAnalyze *ira, IrInst *source_instruction, uint64_t value) { - IrInstGen *result = ir_const(ira, source_instruction, ira->codegen->builtin_types.entry_num_lit_int); - bigint_init_unsigned(&result->value->data.x_bigint, value); - return result; -} - -static IrInstGen *ir_get_const_ptr(IrAnalyze *ira, IrInst *instruction, - ZigValue *pointee, ZigType *pointee_type, - ConstPtrMut ptr_mut, bool ptr_is_const, bool ptr_is_volatile, uint32_t ptr_align) -{ - ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, pointee_type, - ptr_is_const, ptr_is_volatile, PtrLenSingle, ptr_align, 0, 0, false); - IrInstGen *const_instr = ir_const(ira, instruction, ptr_type); - ZigValue *const_val = const_instr->value; - const_val->data.x_ptr.special = ConstPtrSpecialRef; - const_val->data.x_ptr.mut = ptr_mut; - const_val->data.x_ptr.data.ref.pointee = pointee; - return const_instr; -} - -static Error ir_resolve_const_val(CodeGen *codegen, IrExecutableGen *exec, AstNode *source_node, - ZigValue *val, UndefAllowed undef_allowed) -{ - Error err; - for (;;) { - switch (val->special) { - case ConstValSpecialStatic: - return ErrorNone; - case ConstValSpecialRuntime: - if (!type_has_bits(codegen, val->type)) - return ErrorNone; - - exec_add_error_node_gen(codegen, exec, source_node, - buf_sprintf("unable to evaluate constant expression")); - return ErrorSemanticAnalyzeFail; - case ConstValSpecialUndef: - if (undef_allowed == UndefOk || undef_allowed == LazyOk) - return ErrorNone; - - exec_add_error_node_gen(codegen, exec, source_node, - buf_sprintf("use of undefined value here causes undefined behavior")); - return ErrorSemanticAnalyzeFail; - case ConstValSpecialLazy: - if (undef_allowed == LazyOk || undef_allowed == LazyOkNoUndef) - return ErrorNone; - - if ((err = ir_resolve_lazy(codegen, source_node, val))) - return err; - - continue; - } - } -} - -static ZigValue *ir_resolve_const(IrAnalyze *ira, IrInstGen *value, UndefAllowed undef_allowed) { - Error err; - if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, value->base.source_node, - value->value, undef_allowed))) - { - return nullptr; - } - return value->value; -} - -Error ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node, - ZigValue *return_ptr, size_t *backward_branch_count, size_t *backward_branch_quota, - ZigFn *fn_entry, Buf *c_import_buf, AstNode *source_node, Buf *exec_name, - IrExecutableGen *parent_exec, AstNode *expected_type_source_node, UndefAllowed undef_allowed) -{ - Error err; - - src_assert(return_ptr->type->id == ZigTypeIdPointer, source_node); - - if (type_is_invalid(return_ptr->type)) - return ErrorSemanticAnalyzeFail; - - IrExecutableSrc *ir_executable = heap::c_allocator.create(); - ir_executable->source_node = source_node; - ir_executable->parent_exec = parent_exec; - ir_executable->name = exec_name; - ir_executable->is_inline = true; - ir_executable->fn_entry = fn_entry; - ir_executable->c_import_buf = c_import_buf; - ir_executable->begin_scope = scope; - - if (!ir_gen(codegen, node, scope, ir_executable)) - return ErrorSemanticAnalyzeFail; - - if (ir_executable->first_err_trace_msg != nullptr) { - codegen->trace_err = ir_executable->first_err_trace_msg; - return ErrorSemanticAnalyzeFail; - } - - if (codegen->verbose_ir) { - fprintf(stderr, "\nSource: "); - ast_render(stderr, node, 4); - fprintf(stderr, "\n{ // (IR)\n"); - ir_print_src(codegen, stderr, ir_executable, 2); - fprintf(stderr, "}\n"); - } - IrExecutableGen *analyzed_executable = heap::c_allocator.create(); - analyzed_executable->source_node = source_node; - analyzed_executable->parent_exec = parent_exec; - analyzed_executable->source_exec = ir_executable; - analyzed_executable->name = exec_name; - analyzed_executable->is_inline = true; - analyzed_executable->fn_entry = fn_entry; - analyzed_executable->c_import_buf = c_import_buf; - analyzed_executable->backward_branch_count = backward_branch_count; - analyzed_executable->backward_branch_quota = backward_branch_quota; - analyzed_executable->begin_scope = scope; - ZigType *result_type = ir_analyze(codegen, ir_executable, analyzed_executable, - return_ptr->type->data.pointer.child_type, expected_type_source_node, return_ptr); - if (type_is_invalid(result_type)) { - return ErrorSemanticAnalyzeFail; - } - - if (codegen->verbose_ir) { - fprintf(stderr, "{ // (analyzed)\n"); - ir_print_gen(codegen, stderr, analyzed_executable, 2); - fprintf(stderr, "}\n"); - } - - if ((err = ir_exec_scan_for_side_effects(codegen, analyzed_executable))) - return err; - - ZigValue *result = const_ptr_pointee(nullptr, codegen, return_ptr, source_node); - if (result == nullptr) - return ErrorSemanticAnalyzeFail; - if ((err = ir_resolve_const_val(codegen, analyzed_executable, node, result, undef_allowed))) - return err; - - return ErrorNone; -} - -static ErrorTableEntry *ir_resolve_error(IrAnalyze *ira, IrInstGen *err_value) { - if (type_is_invalid(err_value->value->type)) - return nullptr; - - if (err_value->value->type->id != ZigTypeIdErrorSet) { - ir_add_error_node(ira, err_value->base.source_node, - buf_sprintf("expected error, found '%s'", buf_ptr(&err_value->value->type->name))); - return nullptr; - } - - ZigValue *const_val = ir_resolve_const(ira, err_value, UndefBad); - if (!const_val) - return nullptr; - - assert(const_val->data.x_err_set != nullptr); - return const_val->data.x_err_set; -} - -static ZigType *ir_resolve_const_type(CodeGen *codegen, IrExecutableGen *exec, AstNode *source_node, - ZigValue *val) -{ - Error err; - if ((err = ir_resolve_const_val(codegen, exec, source_node, val, UndefBad))) - return codegen->builtin_types.entry_invalid; - - assert(val->data.x_type != nullptr); - return val->data.x_type; -} - -static ZigValue *ir_resolve_type_lazy(IrAnalyze *ira, IrInstGen *type_value) { - if (type_is_invalid(type_value->value->type)) - return nullptr; - - if (type_value->value->type->id != ZigTypeIdMetaType) { - ir_add_error_node(ira, type_value->base.source_node, - buf_sprintf("expected type 'type', found '%s'", buf_ptr(&type_value->value->type->name))); - return nullptr; - } - - Error err; - if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, type_value->base.source_node, - type_value->value, LazyOk))) - { - return nullptr; - } - - return type_value->value; -} - -static ZigType *ir_resolve_type(IrAnalyze *ira, IrInstGen *type_value) { - ZigValue *val = ir_resolve_type_lazy(ira, type_value); - if (val == nullptr) - return ira->codegen->builtin_types.entry_invalid; - - return ir_resolve_const_type(ira->codegen, ira->new_irb.exec, type_value->base.source_node, val); -} - -static Error ir_validate_vector_elem_type(IrAnalyze *ira, AstNode *source_node, ZigType *elem_type) { - Error err; - bool is_valid; - if ((err = is_valid_vector_elem_type(ira->codegen, elem_type, &is_valid))) - return err; - if (!is_valid) { - ir_add_error_node(ira, source_node, - buf_sprintf("vector element type must be integer, float, bool, or pointer; '%s' is invalid", - buf_ptr(&elem_type->name))); - return ErrorSemanticAnalyzeFail; - } - return ErrorNone; -} - -static ZigType *ir_resolve_vector_elem_type(IrAnalyze *ira, IrInstGen *elem_type_value) { - Error err; - ZigType *elem_type = ir_resolve_type(ira, elem_type_value); - if (type_is_invalid(elem_type)) - return ira->codegen->builtin_types.entry_invalid; - if ((err = ir_validate_vector_elem_type(ira, elem_type_value->base.source_node, elem_type))) - return ira->codegen->builtin_types.entry_invalid; - return elem_type; -} - -static ZigType *ir_resolve_int_type(IrAnalyze *ira, IrInstGen *type_value) { - ZigType *ty = ir_resolve_type(ira, type_value); - if (type_is_invalid(ty)) - return ira->codegen->builtin_types.entry_invalid; - - if (ty->id != ZigTypeIdInt) { - ErrorMsg *msg = ir_add_error_node(ira, type_value->base.source_node, - buf_sprintf("expected integer type, found '%s'", buf_ptr(&ty->name))); - if (ty->id == ZigTypeIdVector && - ty->data.vector.elem_type->id == ZigTypeIdInt) - { - add_error_note(ira->codegen, msg, type_value->base.source_node, - buf_sprintf("represent vectors with their element types, i.e. '%s'", - buf_ptr(&ty->data.vector.elem_type->name))); - } - return ira->codegen->builtin_types.entry_invalid; - } - - return ty; -} - -static ZigType *ir_resolve_error_set_type(IrAnalyze *ira, IrInst *op_source, IrInstGen *type_value) { - if (type_is_invalid(type_value->value->type)) - return ira->codegen->builtin_types.entry_invalid; - - if (type_value->value->type->id != ZigTypeIdMetaType) { - ErrorMsg *msg = ir_add_error_node(ira, type_value->base.source_node, - buf_sprintf("expected error set type, found '%s'", buf_ptr(&type_value->value->type->name))); - add_error_note(ira->codegen, msg, op_source->source_node, - buf_sprintf("`||` merges error sets; `or` performs boolean OR")); - return ira->codegen->builtin_types.entry_invalid; - } - - ZigValue *const_val = ir_resolve_const(ira, type_value, UndefBad); - if (!const_val) - return ira->codegen->builtin_types.entry_invalid; - - assert(const_val->data.x_type != nullptr); - ZigType *result_type = const_val->data.x_type; - if (result_type->id != ZigTypeIdErrorSet) { - ErrorMsg *msg = ir_add_error_node(ira, type_value->base.source_node, - buf_sprintf("expected error set type, found type '%s'", buf_ptr(&result_type->name))); - add_error_note(ira->codegen, msg, op_source->source_node, - buf_sprintf("`||` merges error sets; `or` performs boolean OR")); - return ira->codegen->builtin_types.entry_invalid; - } - return result_type; -} - -static ZigFn *ir_resolve_fn(IrAnalyze *ira, IrInstGen *fn_value) { - if (type_is_invalid(fn_value->value->type)) - return nullptr; - - if (fn_value->value->type->id != ZigTypeIdFn) { - ir_add_error_node(ira, fn_value->base.source_node, - buf_sprintf("expected function type, found '%s'", buf_ptr(&fn_value->value->type->name))); - return nullptr; - } - - ZigValue *const_val = ir_resolve_const(ira, fn_value, UndefBad); - if (!const_val) - return nullptr; - - // May be a ConstPtrSpecialHardCodedAddr - if (const_val->data.x_ptr.special != ConstPtrSpecialFunction) - return nullptr; - - return const_val->data.x_ptr.data.fn.fn_entry; -} - -static IrInstGen *ir_analyze_optional_wrap(IrAnalyze *ira, IrInst* source_instr, - IrInstGen *value, ZigType *wanted_type, ResultLoc *result_loc) -{ - assert(wanted_type->id == ZigTypeIdOptional); - - if (instr_is_comptime(value)) { - ZigType *payload_type = wanted_type->data.maybe.child_type; - IrInstGen *casted_payload = ir_implicit_cast(ira, value, payload_type); - if (type_is_invalid(casted_payload->value->type)) - return ira->codegen->invalid_inst_gen; - - ZigValue *val = ir_resolve_const(ira, casted_payload, UndefOk); - if (!val) - return ira->codegen->invalid_inst_gen; - - IrInstGenConst *const_instruction = ir_create_inst_gen(&ira->new_irb, - source_instr->scope, source_instr->source_node); - const_instruction->base.value->special = ConstValSpecialStatic; - if (types_have_same_zig_comptime_repr(ira->codegen, wanted_type, payload_type)) { - copy_const_val(ira->codegen, const_instruction->base.value, val); - } else { - const_instruction->base.value->data.x_optional = val; - } - const_instruction->base.value->type = wanted_type; - return &const_instruction->base; - } - - if (result_loc == nullptr && handle_is_ptr(ira->codegen, wanted_type)) { - result_loc = no_result_loc(); - } - IrInstGen *result_loc_inst = nullptr; - if (result_loc != nullptr) { - result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, wanted_type, nullptr, true, true); - if (type_is_invalid(result_loc_inst->value->type) || - result_loc_inst->value->type->id == ZigTypeIdUnreachable) - { - return result_loc_inst; - } - } - IrInstGen *result = ir_build_optional_wrap(ira, source_instr, wanted_type, value, result_loc_inst); - result->value->data.rh_maybe = RuntimeHintOptionalNonNull; - return result; -} - -static IrInstGen *ir_analyze_err_wrap_payload(IrAnalyze *ira, IrInst* source_instr, - IrInstGen *value, ZigType *wanted_type, ResultLoc *result_loc) -{ - assert(wanted_type->id == ZigTypeIdErrorUnion); - - ZigType *payload_type = wanted_type->data.error_union.payload_type; - ZigType *err_set_type = wanted_type->data.error_union.err_set_type; - if (instr_is_comptime(value)) { - IrInstGen *casted_payload = ir_implicit_cast(ira, value, payload_type); - if (type_is_invalid(casted_payload->value->type)) - return ira->codegen->invalid_inst_gen; - - ZigValue *val = ir_resolve_const(ira, casted_payload, UndefOk); - if (val == nullptr) - return ira->codegen->invalid_inst_gen; - - ZigValue *err_set_val = ira->codegen->pass1_arena->create(); - err_set_val->type = err_set_type; - err_set_val->special = ConstValSpecialStatic; - err_set_val->data.x_err_set = nullptr; - - IrInstGenConst *const_instruction = ir_create_inst_gen(&ira->new_irb, - source_instr->scope, source_instr->source_node); - const_instruction->base.value->type = wanted_type; - const_instruction->base.value->special = ConstValSpecialStatic; - const_instruction->base.value->data.x_err_union.error_set = err_set_val; - const_instruction->base.value->data.x_err_union.payload = val; - return &const_instruction->base; - } - - IrInstGen *result_loc_inst; - if (handle_is_ptr(ira->codegen, wanted_type)) { - if (result_loc == nullptr) result_loc = no_result_loc(); - result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, wanted_type, nullptr, true, true); - if (type_is_invalid(result_loc_inst->value->type) || - result_loc_inst->value->type->id == ZigTypeIdUnreachable) { - return result_loc_inst; - } - } else { - result_loc_inst = nullptr; - } - - IrInstGen *result = ir_build_err_wrap_payload(ira, source_instr, wanted_type, value, result_loc_inst); - result->value->data.rh_error_union = RuntimeHintErrorUnionNonError; - return result; -} - -static IrInstGen *ir_analyze_err_set_cast(IrAnalyze *ira, IrInst* source_instr, IrInstGen *value, - ZigType *wanted_type) -{ - assert(value->value->type->id == ZigTypeIdErrorSet); - assert(wanted_type->id == ZigTypeIdErrorSet); - - if (instr_is_comptime(value)) { - ZigValue *val = ir_resolve_const(ira, value, UndefBad); - if (!val) - return ira->codegen->invalid_inst_gen; - - if (!resolve_inferred_error_set(ira->codegen, wanted_type, source_instr->source_node)) { - return ira->codegen->invalid_inst_gen; - } - if (!type_is_global_error_set(wanted_type)) { - bool subset = false; - for (uint32_t i = 0, count = wanted_type->data.error_set.err_count; i < count; i += 1) { - if (wanted_type->data.error_set.errors[i]->value == val->data.x_err_set->value) { - subset = true; - break; - } - } - if (!subset) { - ir_add_error(ira, source_instr, - buf_sprintf("error.%s not a member of error set '%s'", - buf_ptr(&val->data.x_err_set->name), buf_ptr(&wanted_type->name))); - return ira->codegen->invalid_inst_gen; - } - } - - IrInstGenConst *const_instruction = ir_create_inst_gen(&ira->new_irb, - source_instr->scope, source_instr->source_node); - const_instruction->base.value->type = wanted_type; - const_instruction->base.value->special = ConstValSpecialStatic; - const_instruction->base.value->data.x_err_set = val->data.x_err_set; - return &const_instruction->base; - } - - return ir_build_cast(ira, source_instr, wanted_type, value, CastOpErrSet); -} - -static IrInstGen *ir_analyze_frame_ptr_to_anyframe(IrAnalyze *ira, IrInst* source_instr, - IrInstGen *frame_ptr, ZigType *wanted_type) -{ - if (instr_is_comptime(frame_ptr)) { - ZigValue *ptr_val = ir_resolve_const(ira, frame_ptr, UndefBad); - if (ptr_val == nullptr) - return ira->codegen->invalid_inst_gen; - - ir_assert(ptr_val->type->id == ZigTypeIdPointer, source_instr); - if (ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar) { - zig_panic("TODO comptime frame pointer"); - } - } - - return ir_build_cast(ira, source_instr, wanted_type, frame_ptr, CastOpBitCast); -} - -static IrInstGen *ir_analyze_anyframe_to_anyframe(IrAnalyze *ira, IrInst* source_instr, - IrInstGen *value, ZigType *wanted_type) -{ - if (instr_is_comptime(value)) { - zig_panic("TODO comptime anyframe->T to anyframe"); - } - - return ir_build_cast(ira, source_instr, wanted_type, value, CastOpBitCast); -} - - -static IrInstGen *ir_analyze_err_wrap_code(IrAnalyze *ira, IrInst* source_instr, IrInstGen *value, - ZigType *wanted_type, ResultLoc *result_loc) -{ - assert(wanted_type->id == ZigTypeIdErrorUnion); - - IrInstGen *casted_value = ir_implicit_cast(ira, value, wanted_type->data.error_union.err_set_type); - - if (instr_is_comptime(casted_value)) { - ZigValue *val = ir_resolve_const(ira, casted_value, UndefBad); - if (!val) - return ira->codegen->invalid_inst_gen; - - ZigValue *err_set_val = ira->codegen->pass1_arena->create(); - err_set_val->special = ConstValSpecialStatic; - err_set_val->type = wanted_type->data.error_union.err_set_type; - err_set_val->data.x_err_set = val->data.x_err_set; - - IrInstGenConst *const_instruction = ir_create_inst_gen(&ira->new_irb, - source_instr->scope, source_instr->source_node); - const_instruction->base.value->type = wanted_type; - const_instruction->base.value->special = ConstValSpecialStatic; - const_instruction->base.value->data.x_err_union.error_set = err_set_val; - const_instruction->base.value->data.x_err_union.payload = nullptr; - return &const_instruction->base; - } - - IrInstGen *result_loc_inst; - if (handle_is_ptr(ira->codegen, wanted_type)) { - if (result_loc == nullptr) result_loc = no_result_loc(); - result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, wanted_type, nullptr, true, true); - if (type_is_invalid(result_loc_inst->value->type) || - result_loc_inst->value->type->id == ZigTypeIdUnreachable) - { - return result_loc_inst; - } - } else { - result_loc_inst = nullptr; - } - - - IrInstGen *result = ir_build_err_wrap_code(ira, source_instr, wanted_type, value, result_loc_inst); - result->value->data.rh_error_union = RuntimeHintErrorUnionError; - return result; -} - -static IrInstGen *ir_analyze_null_to_maybe(IrAnalyze *ira, IrInst *source_instr, IrInstGen *value, ZigType *wanted_type) { - assert(wanted_type->id == ZigTypeIdOptional); - assert(instr_is_comptime(value)); - - ZigValue *val = ir_resolve_const(ira, value, UndefBad); - assert(val != nullptr); - - IrInstGen *result = ir_const(ira, source_instr, wanted_type); - result->value->special = ConstValSpecialStatic; - - if (get_src_ptr_type(wanted_type) != nullptr) { - result->value->data.x_ptr.special = ConstPtrSpecialNull; - } else if (is_opt_err_set(wanted_type)) { - result->value->data.x_err_set = nullptr; - } else { - result->value->data.x_optional = nullptr; - } - return result; -} - -static IrInstGen *ir_analyze_null_to_c_pointer(IrAnalyze *ira, IrInst *source_instr, - IrInstGen *value, ZigType *wanted_type) -{ - assert(wanted_type->id == ZigTypeIdPointer); - assert(wanted_type->data.pointer.ptr_len == PtrLenC); - assert(instr_is_comptime(value)); - - ZigValue *val = ir_resolve_const(ira, value, UndefBad); - assert(val != nullptr); - - IrInstGen *result = ir_const(ira, source_instr, wanted_type); - result->value->data.x_ptr.special = ConstPtrSpecialNull; - result->value->data.x_ptr.mut = ConstPtrMutComptimeConst; - return result; -} - -static IrInstGen *ir_get_ref2(IrAnalyze *ira, IrInst* source_instruction, IrInstGen *value, - ZigType *elem_type, bool is_const, bool is_volatile) -{ - Error err; - - if (type_is_invalid(elem_type)) - return ira->codegen->invalid_inst_gen; - - if (instr_is_comptime(value)) { - ZigValue *val = ir_resolve_const(ira, value, LazyOk); - if (!val) - return ira->codegen->invalid_inst_gen; - return ir_get_const_ptr(ira, source_instruction, val, elem_type, - ConstPtrMutComptimeConst, is_const, is_volatile, 0); - } - - ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, elem_type, - is_const, is_volatile, PtrLenSingle, 0, 0, 0, false); - - if ((err = type_resolve(ira->codegen, ptr_type, ResolveStatusZeroBitsKnown))) - return ira->codegen->invalid_inst_gen; - - IrInstGen *result_loc; - if (type_has_bits(ira->codegen, ptr_type) && !handle_is_ptr(ira->codegen, elem_type)) { - result_loc = ir_resolve_result(ira, source_instruction, no_result_loc(), elem_type, nullptr, true, true); - } else { - result_loc = nullptr; - } - - IrInstGen *new_instruction = ir_build_ref_gen(ira, source_instruction, ptr_type, value, result_loc); - new_instruction->value->data.rh_ptr = RuntimeHintPtrStack; - return new_instruction; -} - -static IrInstGen *ir_get_ref(IrAnalyze *ira, IrInst* source_instruction, IrInstGen *value, - bool is_const, bool is_volatile) -{ - return ir_get_ref2(ira, source_instruction, value, value->value->type, is_const, is_volatile); -} - -static ZigType *ir_resolve_union_tag_type(IrAnalyze *ira, AstNode *source_node, ZigType *union_type) { - assert(union_type->id == ZigTypeIdUnion); - - Error err; - if ((err = type_resolve(ira->codegen, union_type, ResolveStatusSizeKnown))) - return ira->codegen->builtin_types.entry_invalid; - - AstNode *decl_node = union_type->data.unionation.decl_node; - if (decl_node->data.container_decl.auto_enum || decl_node->data.container_decl.init_arg_expr != nullptr) { - assert(union_type->data.unionation.tag_type != nullptr); - return union_type->data.unionation.tag_type; - } else { - ErrorMsg *msg = ir_add_error_node(ira, source_node, buf_sprintf("union '%s' has no tag", - buf_ptr(&union_type->name))); - add_error_note(ira->codegen, msg, decl_node, buf_sprintf("consider 'union(enum)' here")); - return ira->codegen->builtin_types.entry_invalid; - } -} - -static IrInstGen *ir_analyze_enum_to_int(IrAnalyze *ira, IrInst *source_instr, IrInstGen *target) { - Error err; - - IrInstGen *enum_target; - ZigType *enum_type; - if (target->value->type->id == ZigTypeIdUnion) { - enum_type = ir_resolve_union_tag_type(ira, target->base.source_node, target->value->type); - if (type_is_invalid(enum_type)) - return ira->codegen->invalid_inst_gen; - enum_target = ir_implicit_cast(ira, target, enum_type); - if (type_is_invalid(enum_target->value->type)) - return ira->codegen->invalid_inst_gen; - } else if (target->value->type->id == ZigTypeIdEnum) { - enum_target = target; - enum_type = target->value->type; - } else { - ir_add_error_node(ira, target->base.source_node, - buf_sprintf("expected enum, found type '%s'", buf_ptr(&target->value->type->name))); - return ira->codegen->invalid_inst_gen; - } - - if ((err = type_resolve(ira->codegen, enum_type, ResolveStatusSizeKnown))) - return ira->codegen->invalid_inst_gen; - - ZigType *tag_type = enum_type->data.enumeration.tag_int_type; - assert(tag_type->id == ZigTypeIdInt || tag_type->id == ZigTypeIdComptimeInt); - - // If there is only one possible tag, then we know at comptime what it is. - if (enum_type->data.enumeration.layout == ContainerLayoutAuto && - enum_type->data.enumeration.src_field_count == 1 && - !enum_type->data.enumeration.non_exhaustive) - { - IrInstGen *result = ir_const(ira, source_instr, tag_type); - init_const_bigint(result->value, tag_type, - &enum_type->data.enumeration.fields[0].value); - return result; - } - - if (instr_is_comptime(enum_target)) { - ZigValue *val = ir_resolve_const(ira, enum_target, UndefBad); - if (!val) - return ira->codegen->invalid_inst_gen; - IrInstGen *result = ir_const(ira, source_instr, tag_type); - init_const_bigint(result->value, tag_type, &val->data.x_enum_tag); - return result; - } - - return ir_build_widen_or_shorten(ira, source_instr->scope, source_instr->source_node, enum_target, tag_type); -} - -static IrInstGen *ir_analyze_union_to_tag(IrAnalyze *ira, IrInst* source_instr, - IrInstGen *target, ZigType *wanted_type) -{ - assert(target->value->type->id == ZigTypeIdUnion); - assert(wanted_type->id == ZigTypeIdEnum); - assert(wanted_type == target->value->type->data.unionation.tag_type); - - if (instr_is_comptime(target)) { - ZigValue *val = ir_resolve_const(ira, target, UndefBad); - if (!val) - return ira->codegen->invalid_inst_gen; - IrInstGen *result = ir_const(ira, source_instr, wanted_type); - result->value->special = ConstValSpecialStatic; - result->value->type = wanted_type; - bigint_init_bigint(&result->value->data.x_enum_tag, &val->data.x_union.tag); - return result; - } - - // If there is only 1 possible tag, then we know at comptime what it is. - if (wanted_type->data.enumeration.layout == ContainerLayoutAuto && - wanted_type->data.enumeration.src_field_count == 1 && - !wanted_type->data.enumeration.non_exhaustive) - { - IrInstGen *result = ir_const(ira, source_instr, wanted_type); - result->value->special = ConstValSpecialStatic; - result->value->type = wanted_type; - TypeEnumField *enum_field = target->value->type->data.unionation.fields[0].enum_field; - bigint_init_bigint(&result->value->data.x_enum_tag, &enum_field->value); - return result; - } - - return ir_build_union_tag(ira, source_instr, target, wanted_type); -} - -static IrInstGen *ir_analyze_undefined_to_anything(IrAnalyze *ira, IrInst* source_instr, - IrInstGen *target, ZigType *wanted_type) -{ - IrInstGen *result = ir_const(ira, source_instr, wanted_type); - result->value->special = ConstValSpecialUndef; - return result; -} - -static IrInstGen *ir_analyze_enum_to_union(IrAnalyze *ira, IrInst* source_instr, - IrInstGen *uncasted_target, ZigType *wanted_type) -{ - Error err; - assert(wanted_type->id == ZigTypeIdUnion); - - if ((err = type_resolve(ira->codegen, wanted_type, ResolveStatusZeroBitsKnown))) - return ira->codegen->invalid_inst_gen; - - IrInstGen *target = ir_implicit_cast(ira, uncasted_target, wanted_type->data.unionation.tag_type); - if (type_is_invalid(target->value->type)) - return ira->codegen->invalid_inst_gen; - - if (instr_is_comptime(target)) { - ZigValue *val = ir_resolve_const(ira, target, UndefBad); - if (!val) - return ira->codegen->invalid_inst_gen; - TypeUnionField *union_field = find_union_field_by_tag(wanted_type, &val->data.x_enum_tag); - if (union_field == nullptr) { - Buf *int_buf = buf_alloc(); - bigint_append_buf(int_buf, &target->value->data.x_enum_tag, 10); - - ir_add_error(ira, &target->base, - buf_sprintf("no tag by value %s", buf_ptr(int_buf))); - return ira->codegen->invalid_inst_gen; - } - ZigType *field_type = resolve_union_field_type(ira->codegen, union_field); - if (field_type == nullptr) - return ira->codegen->invalid_inst_gen; - if ((err = type_resolve(ira->codegen, field_type, ResolveStatusZeroBitsKnown))) - return ira->codegen->invalid_inst_gen; - - switch (type_has_one_possible_value(ira->codegen, field_type)) { - case OnePossibleValueInvalid: - return ira->codegen->invalid_inst_gen; - case OnePossibleValueNo: { - AstNode *field_node = wanted_type->data.unionation.decl_node->data.container_decl.fields.at( - union_field->enum_field->decl_index); - ErrorMsg *msg = ir_add_error(ira, source_instr, - buf_sprintf("cast to union '%s' must initialize '%s' field '%s'", - buf_ptr(&wanted_type->name), - buf_ptr(&field_type->name), - buf_ptr(union_field->name))); - add_error_note(ira->codegen, msg, field_node, - buf_sprintf("field '%s' declared here", buf_ptr(union_field->name))); - return ira->codegen->invalid_inst_gen; - } - case OnePossibleValueYes: - break; - } - - IrInstGen *result = ir_const(ira, source_instr, wanted_type); - result->value->special = ConstValSpecialStatic; - result->value->type = wanted_type; - bigint_init_bigint(&result->value->data.x_union.tag, &val->data.x_enum_tag); - result->value->data.x_union.payload = ira->codegen->pass1_arena->create(); - result->value->data.x_union.payload->special = ConstValSpecialStatic; - result->value->data.x_union.payload->type = field_type; - return result; - } - - if (target->value->type->data.enumeration.non_exhaustive) { - ir_add_error(ira, source_instr, - buf_sprintf("runtime cast to union '%s' from non-exhustive enum", - buf_ptr(&wanted_type->name))); - return ira->codegen->invalid_inst_gen; - } - - // if the union has all fields 0 bits, we can do it - // and in fact it's a noop cast because the union value is just the enum value - if (wanted_type->data.unionation.gen_field_count == 0) { - return ir_build_cast(ira, &target->base, wanted_type, target, CastOpNoop); - } - - ErrorMsg *msg = ir_add_error(ira, source_instr, - buf_sprintf("runtime cast to union '%s' which has non-void fields", - buf_ptr(&wanted_type->name))); - for (uint32_t i = 0; i < wanted_type->data.unionation.src_field_count; i += 1) { - TypeUnionField *union_field = &wanted_type->data.unionation.fields[i]; - ZigType *field_type = resolve_union_field_type(ira->codegen, union_field); - if (field_type == nullptr) - return ira->codegen->invalid_inst_gen; - bool has_bits; - if ((err = type_has_bits2(ira->codegen, field_type, &has_bits))) - return ira->codegen->invalid_inst_gen; - if (has_bits) { - AstNode *field_node = wanted_type->data.unionation.decl_node->data.container_decl.fields.at(i); - add_error_note(ira->codegen, msg, field_node, - buf_sprintf("field '%s' has type '%s'", - buf_ptr(union_field->name), - buf_ptr(&field_type->name))); - } - } - return ira->codegen->invalid_inst_gen; -} - -static IrInstGen *ir_analyze_widen_or_shorten(IrAnalyze *ira, IrInst* source_instr, - IrInstGen *target, ZigType *wanted_type) -{ - assert(wanted_type->id == ZigTypeIdInt || wanted_type->id == ZigTypeIdFloat); - - if (instr_is_comptime(target)) { - ZigValue *val = ir_resolve_const(ira, target, UndefBad); - if (!val) - return ira->codegen->invalid_inst_gen; - if (wanted_type->id == ZigTypeIdInt) { - if (bigint_cmp_zero(&val->data.x_bigint) == CmpLT && !wanted_type->data.integral.is_signed) { - ir_add_error(ira, source_instr, - buf_sprintf("attempt to cast negative value to unsigned integer")); - return ira->codegen->invalid_inst_gen; - } - if (!bigint_fits_in_bits(&val->data.x_bigint, wanted_type->data.integral.bit_count, - wanted_type->data.integral.is_signed)) - { - ir_add_error(ira, source_instr, - buf_sprintf("cast from '%s' to '%s' truncates bits", - buf_ptr(&target->value->type->name), buf_ptr(&wanted_type->name))); - return ira->codegen->invalid_inst_gen; - } - } - IrInstGen *result = ir_const(ira, source_instr, wanted_type); - result->value->type = wanted_type; - if (wanted_type->id == ZigTypeIdInt) { - bigint_init_bigint(&result->value->data.x_bigint, &val->data.x_bigint); - } else { - float_init_float(result->value, val); - } - return result; - } - - // If the destination integer type has no bits, then we can emit a comptime - // zero. However, we still want to emit a runtime safety check to make sure - // the target is zero. - if (!type_has_bits(ira->codegen, wanted_type)) { - assert(wanted_type->id == ZigTypeIdInt); - assert(type_has_bits(ira->codegen, target->value->type)); - ir_build_assert_zero(ira, source_instr, target); - IrInstGen *result = ir_const_unsigned(ira, source_instr, 0); - result->value->type = wanted_type; - return result; - } - - return ir_build_widen_or_shorten(ira, source_instr->scope, source_instr->source_node, target, wanted_type); -} - -static IrInstGen *ir_analyze_int_to_enum(IrAnalyze *ira, IrInst* source_instr, - IrInstGen *target, ZigType *wanted_type) -{ - Error err; - assert(wanted_type->id == ZigTypeIdEnum); - - ZigType *actual_type = target->value->type; - - if ((err = type_resolve(ira->codegen, wanted_type, ResolveStatusSizeKnown))) - return ira->codegen->invalid_inst_gen; - - if (actual_type != wanted_type->data.enumeration.tag_int_type) { - ir_add_error(ira, source_instr, - buf_sprintf("integer to enum cast from '%s' instead of its tag type, '%s'", - buf_ptr(&actual_type->name), - buf_ptr(&wanted_type->data.enumeration.tag_int_type->name))); - return ira->codegen->invalid_inst_gen; - } - - assert(actual_type->id == ZigTypeIdInt || actual_type->id == ZigTypeIdComptimeInt); - - if (instr_is_comptime(target)) { - ZigValue *val = ir_resolve_const(ira, target, UndefBad); - if (!val) - return ira->codegen->invalid_inst_gen; - - TypeEnumField *field = find_enum_field_by_tag(wanted_type, &val->data.x_bigint); - if (field == nullptr && !wanted_type->data.enumeration.non_exhaustive) { - Buf *val_buf = buf_alloc(); - bigint_append_buf(val_buf, &val->data.x_bigint, 10); - ErrorMsg *msg = ir_add_error(ira, source_instr, - buf_sprintf("enum '%s' has no tag matching integer value %s", - buf_ptr(&wanted_type->name), buf_ptr(val_buf))); - add_error_note(ira->codegen, msg, wanted_type->data.enumeration.decl_node, - buf_sprintf("'%s' declared here", buf_ptr(&wanted_type->name))); - return ira->codegen->invalid_inst_gen; - } - - IrInstGen *result = ir_const(ira, source_instr, wanted_type); - bigint_init_bigint(&result->value->data.x_enum_tag, &val->data.x_bigint); - return result; - } - - return ir_build_int_to_enum_gen(ira, source_instr->scope, source_instr->source_node, wanted_type, target); -} - -static IrInstGen *ir_analyze_number_to_literal(IrAnalyze *ira, IrInst* source_instr, - IrInstGen *target, ZigType *wanted_type) -{ - ZigValue *val = ir_resolve_const(ira, target, UndefBad); - if (!val) - return ira->codegen->invalid_inst_gen; - - IrInstGen *result = ir_const(ira, source_instr, wanted_type); - if (wanted_type->id == ZigTypeIdComptimeFloat) { - float_init_float(result->value, val); - } else if (wanted_type->id == ZigTypeIdComptimeInt) { - bigint_init_bigint(&result->value->data.x_bigint, &val->data.x_bigint); - } else { - zig_unreachable(); - } - return result; -} - -static IrInstGen *ir_analyze_int_to_err(IrAnalyze *ira, IrInst* source_instr, IrInstGen *target, - ZigType *wanted_type) -{ - assert(target->value->type->id == ZigTypeIdInt); - assert(!target->value->type->data.integral.is_signed); - assert(wanted_type->id == ZigTypeIdErrorSet); - - if (instr_is_comptime(target)) { - ZigValue *val = ir_resolve_const(ira, target, UndefBad); - if (!val) - return ira->codegen->invalid_inst_gen; - - IrInstGen *result = ir_const(ira, source_instr, wanted_type); - - if (!resolve_inferred_error_set(ira->codegen, wanted_type, source_instr->source_node)) { - return ira->codegen->invalid_inst_gen; - } - - if (type_is_global_error_set(wanted_type)) { - BigInt err_count; - bigint_init_unsigned(&err_count, ira->codegen->errors_by_index.length); - - if (bigint_cmp_zero(&val->data.x_bigint) == CmpEQ || bigint_cmp(&val->data.x_bigint, &err_count) != CmpLT) { - Buf *val_buf = buf_alloc(); - bigint_append_buf(val_buf, &val->data.x_bigint, 10); - ir_add_error(ira, source_instr, - buf_sprintf("integer value %s represents no error", buf_ptr(val_buf))); - return ira->codegen->invalid_inst_gen; - } - - size_t index = bigint_as_usize(&val->data.x_bigint); - result->value->data.x_err_set = ira->codegen->errors_by_index.at(index); - return result; - } else { - ErrorTableEntry *err = nullptr; - BigInt err_int; - - for (uint32_t i = 0, count = wanted_type->data.error_set.err_count; i < count; i += 1) { - ErrorTableEntry *this_err = wanted_type->data.error_set.errors[i]; - bigint_init_unsigned(&err_int, this_err->value); - if (bigint_cmp(&val->data.x_bigint, &err_int) == CmpEQ) { - err = this_err; - break; - } - } - - if (err == nullptr) { - Buf *val_buf = buf_alloc(); - bigint_append_buf(val_buf, &val->data.x_bigint, 10); - ir_add_error(ira, source_instr, - buf_sprintf("integer value %s represents no error in '%s'", buf_ptr(val_buf), buf_ptr(&wanted_type->name))); - return ira->codegen->invalid_inst_gen; - } - - result->value->data.x_err_set = err; - return result; - } - } - - return ir_build_int_to_err_gen(ira, source_instr->scope, source_instr->source_node, target, wanted_type); -} - -static IrInstGen *ir_analyze_err_to_int(IrAnalyze *ira, IrInst* source_instr, IrInstGen *target, - ZigType *wanted_type) -{ - assert(wanted_type->id == ZigTypeIdInt); - - ZigType *err_type = target->value->type; - - if (instr_is_comptime(target)) { - ZigValue *val = ir_resolve_const(ira, target, UndefBad); - if (!val) - return ira->codegen->invalid_inst_gen; - - IrInstGen *result = ir_const(ira, source_instr, wanted_type); - - ErrorTableEntry *err; - if (err_type->id == ZigTypeIdErrorUnion) { - err = val->data.x_err_union.error_set->data.x_err_set; - } else if (err_type->id == ZigTypeIdErrorSet) { - err = val->data.x_err_set; - } else { - zig_unreachable(); - } - result->value->type = wanted_type; - uint64_t err_value = err ? err->value : 0; - bigint_init_unsigned(&result->value->data.x_bigint, err_value); - - if (!bigint_fits_in_bits(&result->value->data.x_bigint, - wanted_type->data.integral.bit_count, wanted_type->data.integral.is_signed)) - { - ir_add_error_node(ira, source_instr->source_node, - buf_sprintf("error code '%s' does not fit in '%s'", - buf_ptr(&err->name), buf_ptr(&wanted_type->name))); - return ira->codegen->invalid_inst_gen; - } - - return result; - } - - ZigType *err_set_type; - if (err_type->id == ZigTypeIdErrorUnion) { - err_set_type = err_type->data.error_union.err_set_type; - } else if (err_type->id == ZigTypeIdErrorSet) { - err_set_type = err_type; - } else { - zig_unreachable(); - } - if (!type_is_global_error_set(err_set_type)) { - if (!resolve_inferred_error_set(ira->codegen, err_set_type, source_instr->source_node)) { - return ira->codegen->invalid_inst_gen; - } - if (err_set_type->data.error_set.err_count == 0) { - IrInstGen *result = ir_const(ira, source_instr, wanted_type); - bigint_init_unsigned(&result->value->data.x_bigint, 0); - return result; - } else if (err_set_type->data.error_set.err_count == 1) { - IrInstGen *result = ir_const(ira, source_instr, wanted_type); - ErrorTableEntry *err = err_set_type->data.error_set.errors[0]; - bigint_init_unsigned(&result->value->data.x_bigint, err->value); - return result; - } - } - - BigInt bn; - bigint_init_unsigned(&bn, ira->codegen->errors_by_index.length); - if (!bigint_fits_in_bits(&bn, wanted_type->data.integral.bit_count, wanted_type->data.integral.is_signed)) { - ir_add_error_node(ira, source_instr->source_node, - buf_sprintf("too many error values to fit in '%s'", buf_ptr(&wanted_type->name))); - return ira->codegen->invalid_inst_gen; - } - - return ir_build_err_to_int_gen(ira, source_instr->scope, source_instr->source_node, target, wanted_type); -} - -static IrInstGen *ir_analyze_ptr_to_array(IrAnalyze *ira, IrInst* source_instr, IrInstGen *target, - ZigType *wanted_type) -{ - assert(wanted_type->id == ZigTypeIdPointer); - Error err; - if ((err = type_resolve(ira->codegen, target->value->type->data.pointer.child_type, ResolveStatusAlignmentKnown))) - return ira->codegen->invalid_inst_gen; - assert((wanted_type->data.pointer.is_const && target->value->type->data.pointer.is_const) || !target->value->type->data.pointer.is_const); - wanted_type = adjust_ptr_align(ira->codegen, wanted_type, get_ptr_align(ira->codegen, target->value->type)); - ZigType *array_type = wanted_type->data.pointer.child_type; - assert(array_type->id == ZigTypeIdArray); - assert(array_type->data.array.len == 1); - - if (instr_is_comptime(target)) { - ZigValue *val = ir_resolve_const(ira, target, UndefBad); - if (!val) - return ira->codegen->invalid_inst_gen; - - assert(val->type->id == ZigTypeIdPointer); - ZigValue *pointee = const_ptr_pointee(ira, ira->codegen, val, source_instr->source_node); - if (pointee == nullptr) - return ira->codegen->invalid_inst_gen; - if (pointee->special != ConstValSpecialRuntime) { - ZigValue *array_val = ira->codegen->pass1_arena->create(); - array_val->special = ConstValSpecialStatic; - array_val->type = array_type; - array_val->data.x_array.special = ConstArraySpecialNone; - array_val->data.x_array.data.s_none.elements = pointee; - array_val->parent.id = ConstParentIdScalar; - array_val->parent.data.p_scalar.scalar_val = pointee; - - IrInstGenConst *const_instruction = ir_create_inst_gen(&ira->new_irb, - source_instr->scope, source_instr->source_node); - const_instruction->base.value->type = wanted_type; - const_instruction->base.value->special = ConstValSpecialStatic; - const_instruction->base.value->data.x_ptr.special = ConstPtrSpecialRef; - const_instruction->base.value->data.x_ptr.data.ref.pointee = array_val; - const_instruction->base.value->data.x_ptr.mut = val->data.x_ptr.mut; - return &const_instruction->base; - } - } - - // pointer to array and pointer to single item are represented the same way at runtime - return ir_build_cast(ira, &target->base, wanted_type, target, CastOpBitCast); -} - -static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCastOnly *cast_result, - ErrorMsg *parent_msg) -{ - switch (cast_result->id) { - case ConstCastResultIdOk: - zig_unreachable(); - case ConstCastResultIdInvalid: - zig_unreachable(); - case ConstCastResultIdOptionalChild: { - ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node, - buf_sprintf("optional type child '%s' cannot cast into optional type child '%s'", - buf_ptr(&cast_result->data.optional->actual_child->name), - buf_ptr(&cast_result->data.optional->wanted_child->name))); - report_recursive_error(ira, source_node, &cast_result->data.optional->child, msg); - break; - } - case ConstCastResultIdOptionalShape: { - add_error_note(ira->codegen, parent_msg, source_node, - buf_sprintf("optional type child '%s' cannot cast into optional type '%s'", - buf_ptr(&cast_result->data.type_mismatch->actual_type->name), - buf_ptr(&cast_result->data.type_mismatch->wanted_type->name))); - break; - } - case ConstCastResultIdErrorUnionErrorSet: { - ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node, - buf_sprintf("error set '%s' cannot cast into error set '%s'", - buf_ptr(&cast_result->data.error_union_error_set->actual_err_set->name), - buf_ptr(&cast_result->data.error_union_error_set->wanted_err_set->name))); - report_recursive_error(ira, source_node, &cast_result->data.error_union_error_set->child, msg); - break; - } - case ConstCastResultIdErrSet: { - ZigList *missing_errors = &cast_result->data.error_set_mismatch->missing_errors; - for (size_t i = 0; i < missing_errors->length; i += 1) { - ErrorTableEntry *error_entry = missing_errors->at(i); - add_error_note(ira->codegen, parent_msg, ast_field_to_symbol_node(error_entry->decl_node), - buf_sprintf("'error.%s' not a member of destination error set", buf_ptr(&error_entry->name))); - } - break; - } - case ConstCastResultIdErrSetGlobal: { - add_error_note(ira->codegen, parent_msg, source_node, - buf_sprintf("cannot cast global error set into smaller set")); - break; - } - case ConstCastResultIdPointerChild: { - ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node, - buf_sprintf("pointer type child '%s' cannot cast into pointer type child '%s'", - buf_ptr(&cast_result->data.pointer_mismatch->actual_child->name), - buf_ptr(&cast_result->data.pointer_mismatch->wanted_child->name))); - report_recursive_error(ira, source_node, &cast_result->data.pointer_mismatch->child, msg); - break; - } - case ConstCastResultIdSliceChild: { - ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node, - buf_sprintf("slice type child '%s' cannot cast into slice type child '%s'", - buf_ptr(&cast_result->data.slice_mismatch->actual_child->name), - buf_ptr(&cast_result->data.slice_mismatch->wanted_child->name))); - report_recursive_error(ira, source_node, &cast_result->data.slice_mismatch->child, msg); - break; - } - case ConstCastResultIdErrorUnionPayload: { - ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node, - buf_sprintf("error union payload '%s' cannot cast into error union payload '%s'", - buf_ptr(&cast_result->data.error_union_payload->actual_payload->name), - buf_ptr(&cast_result->data.error_union_payload->wanted_payload->name))); - report_recursive_error(ira, source_node, &cast_result->data.error_union_payload->child, msg); - break; - } - case ConstCastResultIdType: { - AstNode *wanted_decl_node = type_decl_node(cast_result->data.type_mismatch->wanted_type); - AstNode *actual_decl_node = type_decl_node(cast_result->data.type_mismatch->actual_type); - if (wanted_decl_node != nullptr) { - add_error_note(ira->codegen, parent_msg, wanted_decl_node, - buf_sprintf("%s declared here", - buf_ptr(&cast_result->data.type_mismatch->wanted_type->name))); - } - if (actual_decl_node != nullptr) { - add_error_note(ira->codegen, parent_msg, actual_decl_node, - buf_sprintf("%s declared here", - buf_ptr(&cast_result->data.type_mismatch->actual_type->name))); - } - break; - } - case ConstCastResultIdFnArg: { - ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node, - buf_sprintf("parameter %" ZIG_PRI_usize ": '%s' cannot cast into '%s'", - cast_result->data.fn_arg.arg_index, - buf_ptr(&cast_result->data.fn_arg.actual_param_type->name), - buf_ptr(&cast_result->data.fn_arg.expected_param_type->name))); - report_recursive_error(ira, source_node, cast_result->data.fn_arg.child, msg); - break; - } - case ConstCastResultIdBadAllowsZero: { - ZigType *wanted_type = cast_result->data.bad_allows_zero->wanted_type; - ZigType *actual_type = cast_result->data.bad_allows_zero->actual_type; - bool wanted_allows_zero = ptr_allows_addr_zero(wanted_type); - bool actual_allows_zero = ptr_allows_addr_zero(actual_type); - if (actual_allows_zero && !wanted_allows_zero) { - add_error_note(ira->codegen, parent_msg, source_node, - buf_sprintf("'%s' could have null values which are illegal in type '%s'", - buf_ptr(&actual_type->name), - buf_ptr(&wanted_type->name))); - } else { - add_error_note(ira->codegen, parent_msg, source_node, - buf_sprintf("mutable '%s' allows illegal null values stored to type '%s'", - buf_ptr(&wanted_type->name), - buf_ptr(&actual_type->name))); - } - break; - } - case ConstCastResultIdPtrLens: { - add_error_note(ira->codegen, parent_msg, source_node, - buf_sprintf("pointer length mismatch")); - break; - } - case ConstCastResultIdPtrSentinel: { - ZigType *actual_type = cast_result->data.bad_ptr_sentinel->actual_type; - ZigType *wanted_type = cast_result->data.bad_ptr_sentinel->wanted_type; - { - Buf *txt_msg = buf_sprintf("destination pointer requires a terminating '"); - render_const_value(ira->codegen, txt_msg, wanted_type->data.pointer.sentinel); - buf_appendf(txt_msg, "' sentinel"); - if (actual_type->data.pointer.sentinel != nullptr) { - buf_appendf(txt_msg, ", but source pointer has a terminating '"); - render_const_value(ira->codegen, txt_msg, actual_type->data.pointer.sentinel); - buf_appendf(txt_msg, "' sentinel"); - } - add_error_note(ira->codegen, parent_msg, source_node, txt_msg); - } - break; - } - case ConstCastResultIdSentinelArrays: { - ZigType *actual_type = cast_result->data.sentinel_arrays->actual_type; - ZigType *wanted_type = cast_result->data.sentinel_arrays->wanted_type; - Buf *txt_msg = buf_sprintf("destination array requires a terminating '"); - render_const_value(ira->codegen, txt_msg, wanted_type->data.array.sentinel); - buf_appendf(txt_msg, "' sentinel"); - if (actual_type->data.array.sentinel != nullptr) { - buf_appendf(txt_msg, ", but source array has a terminating '"); - render_const_value(ira->codegen, txt_msg, actual_type->data.array.sentinel); - buf_appendf(txt_msg, "' sentinel"); - } - add_error_note(ira->codegen, parent_msg, source_node, txt_msg); - break; - } - case ConstCastResultIdCV: { - ZigType *wanted_type = cast_result->data.bad_cv->wanted_type; - ZigType *actual_type = cast_result->data.bad_cv->actual_type; - bool ok_const = !actual_type->data.pointer.is_const || wanted_type->data.pointer.is_const; - bool ok_volatile = !actual_type->data.pointer.is_volatile || wanted_type->data.pointer.is_volatile; - if (!ok_const) { - add_error_note(ira->codegen, parent_msg, source_node, buf_sprintf("cast discards const qualifier")); - } else if (!ok_volatile) { - add_error_note(ira->codegen, parent_msg, source_node, buf_sprintf("cast discards volatile qualifier")); - } else { - zig_unreachable(); - } - break; - } - case ConstCastResultIdFnIsGeneric: - add_error_note(ira->codegen, parent_msg, source_node, - buf_sprintf("only one of the functions is generic")); - break; - case ConstCastResultIdFnCC: - add_error_note(ira->codegen, parent_msg, source_node, - buf_sprintf("calling convention mismatch")); - break; - case ConstCastResultIdIntShorten: { - ZigType *wanted_type = cast_result->data.int_shorten->wanted_type; - ZigType *actual_type = cast_result->data.int_shorten->actual_type; - const char *wanted_signed = wanted_type->data.integral.is_signed ? "signed" : "unsigned"; - const char *actual_signed = actual_type->data.integral.is_signed ? "signed" : "unsigned"; - add_error_note(ira->codegen, parent_msg, source_node, - buf_sprintf("%s %" PRIu32 "-bit int cannot represent all possible %s %" PRIu32 "-bit values", - wanted_signed, wanted_type->data.integral.bit_count, - actual_signed, actual_type->data.integral.bit_count)); - break; - } - case ConstCastResultIdFnAlign: // TODO - case ConstCastResultIdFnVarArgs: // TODO - case ConstCastResultIdFnReturnType: // TODO - case ConstCastResultIdFnArgCount: // TODO - case ConstCastResultIdFnGenericArgCount: // TODO - case ConstCastResultIdFnArgNoAlias: // TODO - case ConstCastResultIdUnresolvedInferredErrSet: // TODO - case ConstCastResultIdAsyncAllocatorType: // TODO - case ConstCastResultIdArrayChild: // TODO - break; - } -} - -static IrInstGen *ir_analyze_array_to_vector(IrAnalyze *ira, IrInst* source_instr, - IrInstGen *array, ZigType *vector_type) -{ - if (instr_is_comptime(array)) { - // arrays and vectors have the same ZigValue representation - IrInstGen *result = ir_const(ira, source_instr, vector_type); - copy_const_val(ira->codegen, result->value, array->value); - result->value->type = vector_type; - return result; - } - return ir_build_array_to_vector(ira, source_instr, array, vector_type); -} - -static IrInstGen *ir_analyze_vector_to_array(IrAnalyze *ira, IrInst* source_instr, - IrInstGen *vector, ZigType *array_type, ResultLoc *result_loc) -{ - if (instr_is_comptime(vector)) { - // arrays and vectors have the same ZigValue representation - IrInstGen *result = ir_const(ira, source_instr, array_type); - copy_const_val(ira->codegen, result->value, vector->value); - result->value->type = array_type; - return result; - } - if (result_loc == nullptr) { - result_loc = no_result_loc(); - } - IrInstGen *result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, array_type, nullptr, true, true); - if (type_is_invalid(result_loc_inst->value->type) || result_loc_inst->value->type->id == ZigTypeIdUnreachable) { - return result_loc_inst; - } - return ir_build_vector_to_array(ira, source_instr, array_type, vector, result_loc_inst); -} - -static IrInstGen *ir_analyze_int_to_c_ptr(IrAnalyze *ira, IrInst* source_instr, - IrInstGen *integer, ZigType *dest_type) -{ - IrInstGen *unsigned_integer; - if (instr_is_comptime(integer)) { - unsigned_integer = integer; - } else { - assert(integer->value->type->id == ZigTypeIdInt); - - if (integer->value->type->data.integral.bit_count > - ira->codegen->builtin_types.entry_usize->data.integral.bit_count) - { - ir_add_error(ira, source_instr, - buf_sprintf("integer type '%s' too big for implicit @intToPtr to type '%s'", - buf_ptr(&integer->value->type->name), - buf_ptr(&dest_type->name))); - return ira->codegen->invalid_inst_gen; - } - - if (integer->value->type->data.integral.is_signed) { - ZigType *unsigned_int_type = get_int_type(ira->codegen, false, - integer->value->type->data.integral.bit_count); - unsigned_integer = ir_analyze_bit_cast(ira, source_instr, integer, unsigned_int_type); - if (type_is_invalid(unsigned_integer->value->type)) - return ira->codegen->invalid_inst_gen; - } else { - unsigned_integer = integer; - } - } - - return ir_analyze_int_to_ptr(ira, source_instr, unsigned_integer, dest_type); -} - -static bool is_pointery_and_elem_is_not_pointery(ZigType *ty) { - if (ty->id == ZigTypeIdPointer) return ty->data.pointer.child_type->id != ZigTypeIdPointer; - if (ty->id == ZigTypeIdFn) return true; - if (ty->id == ZigTypeIdOptional) { - ZigType *ptr_ty = ty->data.maybe.child_type; - if (ptr_ty->id == ZigTypeIdPointer) return ptr_ty->data.pointer.child_type->id != ZigTypeIdPointer; - if (ptr_ty->id == ZigTypeIdFn) return true; - } - return false; -} - -static IrInstGen *ir_analyze_enum_literal(IrAnalyze *ira, IrInst* source_instr, IrInstGen *value, - ZigType *enum_type) -{ - assert(enum_type->id == ZigTypeIdEnum); - - Error err; - if ((err = type_resolve(ira->codegen, enum_type, ResolveStatusZeroBitsKnown))) - return ira->codegen->invalid_inst_gen; - - TypeEnumField *field = find_enum_type_field(enum_type, value->value->data.x_enum_literal); - if (field == nullptr) { - ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("enum '%s' has no field named '%s'", - buf_ptr(&enum_type->name), buf_ptr(value->value->data.x_enum_literal))); - add_error_note(ira->codegen, msg, enum_type->data.enumeration.decl_node, - buf_sprintf("'%s' declared here", buf_ptr(&enum_type->name))); - return ira->codegen->invalid_inst_gen; - } - IrInstGen *result = ir_const(ira, source_instr, enum_type); - bigint_init_bigint(&result->value->data.x_enum_tag, &field->value); - - return result; -} - -static IrInstGen *ir_analyze_struct_literal_to_array(IrAnalyze *ira, IrInst* source_instr, - IrInstGen *value, ZigType *wanted_type) -{ - ir_add_error(ira, source_instr, buf_sprintf("TODO: type coercion of anon list literal to array")); - return ira->codegen->invalid_inst_gen; -} - -static IrInstGen *ir_analyze_struct_literal_to_struct(IrAnalyze *ira, IrInst* source_instr, - IrInstGen *struct_operand, ZigType *wanted_type) -{ - Error err; - - IrInstGen *struct_ptr = ir_get_ref(ira, source_instr, struct_operand, true, false); - if (type_is_invalid(struct_ptr->value->type)) - return ira->codegen->invalid_inst_gen; - - if (wanted_type->data.structure.resolve_status == ResolveStatusBeingInferred) { - ir_add_error(ira, source_instr, buf_sprintf("type coercion of anon struct literal to inferred struct")); - return ira->codegen->invalid_inst_gen; - } - - if ((err = type_resolve(ira->codegen, wanted_type, ResolveStatusSizeKnown))) - return ira->codegen->invalid_inst_gen; - - size_t actual_field_count = wanted_type->data.structure.src_field_count; - size_t instr_field_count = struct_operand->value->type->data.structure.src_field_count; - - bool need_comptime = ir_should_inline(ira->old_irb.exec, source_instr->scope) - || type_requires_comptime(ira->codegen, wanted_type) == ReqCompTimeYes; - bool is_comptime = true; - - // Determine if the struct_operand will be comptime. - // Also emit compile errors for missing fields and duplicate fields. - AstNode **field_assign_nodes = heap::c_allocator.allocate(actual_field_count); - ZigValue **field_values = heap::c_allocator.allocate(actual_field_count); - IrInstGen **casted_fields = heap::c_allocator.allocate(actual_field_count); - IrInstGen *const_result = ir_const(ira, source_instr, wanted_type); - - for (size_t i = 0; i < instr_field_count; i += 1) { - TypeStructField *src_field = struct_operand->value->type->data.structure.fields[i]; - TypeStructField *dst_field = find_struct_type_field(wanted_type, src_field->name); - if (dst_field == nullptr) { - ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("no field named '%s' in struct '%s'", - buf_ptr(src_field->name), buf_ptr(&wanted_type->name))); - if (wanted_type->data.structure.decl_node) { - add_error_note(ira->codegen, msg, wanted_type->data.structure.decl_node, - buf_sprintf("struct '%s' declared here", buf_ptr(&wanted_type->name))); - } - add_error_note(ira->codegen, msg, src_field->decl_node, - buf_sprintf("field '%s' declared here", buf_ptr(src_field->name))); - return ira->codegen->invalid_inst_gen; - } - - ir_assert(src_field->decl_node != nullptr, source_instr); - AstNode *existing_assign_node = field_assign_nodes[dst_field->src_index]; - if (existing_assign_node != nullptr) { - ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("duplicate field")); - add_error_note(ira->codegen, msg, existing_assign_node, buf_sprintf("other field here")); - return ira->codegen->invalid_inst_gen; - } - field_assign_nodes[dst_field->src_index] = src_field->decl_node; - - IrInstGen *field_ptr = ir_analyze_struct_field_ptr(ira, source_instr, src_field, struct_ptr, - struct_operand->value->type, false); - if (type_is_invalid(field_ptr->value->type)) - return ira->codegen->invalid_inst_gen; - IrInstGen *field_value = ir_get_deref(ira, source_instr, field_ptr, nullptr); - if (type_is_invalid(field_value->value->type)) - return ira->codegen->invalid_inst_gen; - IrInstGen *casted_value = ir_implicit_cast(ira, field_value, dst_field->type_entry); - if (type_is_invalid(casted_value->value->type)) - return ira->codegen->invalid_inst_gen; - - casted_fields[dst_field->src_index] = casted_value; - if (need_comptime || instr_is_comptime(casted_value)) { - ZigValue *field_val = ir_resolve_const(ira, casted_value, UndefOk); - if (field_val == nullptr) - return ira->codegen->invalid_inst_gen; - field_val->parent.id = ConstParentIdStruct; - field_val->parent.data.p_struct.struct_val = const_result->value; - field_val->parent.data.p_struct.field_index = dst_field->src_index; - field_values[dst_field->src_index] = field_val; - if (field_val->type->id == ZigTypeIdUndefined && dst_field->type_entry->id != ZigTypeIdUndefined) { - field_values[dst_field->src_index]->special = ConstValSpecialUndef; - } - } else { - is_comptime = false; - } - } - - bool any_missing = false; - for (size_t i = 0; i < actual_field_count; i += 1) { - if (field_assign_nodes[i] != nullptr) continue; - - // look for a default field value - TypeStructField *field = wanted_type->data.structure.fields[i]; - memoize_field_init_val(ira->codegen, wanted_type, field); - if (field->init_val == nullptr) { - ir_add_error(ira, source_instr, - buf_sprintf("missing field: '%s'", buf_ptr(field->name))); - any_missing = true; - continue; - } - if (type_is_invalid(field->init_val->type)) - return ira->codegen->invalid_inst_gen; - ZigValue *init_val_copy = ira->codegen->pass1_arena->create(); - copy_const_val(ira->codegen, init_val_copy, field->init_val); - init_val_copy->parent.id = ConstParentIdStruct; - init_val_copy->parent.data.p_struct.struct_val = const_result->value; - init_val_copy->parent.data.p_struct.field_index = i; - field_values[i] = init_val_copy; - casted_fields[i] = ir_const_move(ira, source_instr, init_val_copy); - } - if (any_missing) - return ira->codegen->invalid_inst_gen; - - if (is_comptime) { - heap::c_allocator.deallocate(field_assign_nodes, actual_field_count); - IrInstGen *const_result = ir_const(ira, source_instr, wanted_type); - const_result->value->data.x_struct.fields = field_values; - return const_result; - } - - IrInstGen *result_loc_inst = ir_resolve_result(ira, source_instr, no_result_loc(), - wanted_type, nullptr, true, true); - if (type_is_invalid(result_loc_inst->value->type) || result_loc_inst->value->type->id == ZigTypeIdUnreachable) { - return ira->codegen->invalid_inst_gen; - } - - for (size_t i = 0; i < actual_field_count; i += 1) { - TypeStructField *field = wanted_type->data.structure.fields[i]; - IrInstGen *field_ptr = ir_analyze_struct_field_ptr(ira, source_instr, field, result_loc_inst, wanted_type, true); - if (type_is_invalid(field_ptr->value->type)) - return ira->codegen->invalid_inst_gen; - IrInstGen *store_ptr_inst = ir_analyze_store_ptr(ira, source_instr, field_ptr, casted_fields[i], true); - if (type_is_invalid(store_ptr_inst->value->type)) - return ira->codegen->invalid_inst_gen; - } - - heap::c_allocator.deallocate(field_assign_nodes, actual_field_count); - heap::c_allocator.deallocate(field_values, actual_field_count); - heap::c_allocator.deallocate(casted_fields, actual_field_count); - - return ir_get_deref(ira, source_instr, result_loc_inst, nullptr); -} - -static IrInstGen *ir_analyze_struct_literal_to_union(IrAnalyze *ira, IrInst* source_instr, - IrInstGen *value, ZigType *union_type) -{ - Error err; - ZigType *struct_type = value->value->type; - - assert(struct_type->id == ZigTypeIdStruct); - assert(union_type->id == ZigTypeIdUnion); - assert(struct_type->data.structure.src_field_count == 1); - - TypeStructField *only_field = struct_type->data.structure.fields[0]; - - if ((err = type_resolve(ira->codegen, union_type, ResolveStatusZeroBitsKnown))) - return ira->codegen->invalid_inst_gen; - - TypeUnionField *union_field = find_union_type_field(union_type, only_field->name); - if (union_field == nullptr) { - ir_add_error_node(ira, only_field->decl_node, - buf_sprintf("no field named '%s' in union '%s'", - buf_ptr(only_field->name), buf_ptr(&union_type->name))); - return ira->codegen->invalid_inst_gen; - } - - ZigType *payload_type = resolve_union_field_type(ira->codegen, union_field); - if (payload_type == nullptr) - return ira->codegen->invalid_inst_gen; - - IrInstGen *field_value = ir_analyze_struct_value_field_value(ira, source_instr, value, only_field); - if (type_is_invalid(field_value->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *casted_value = ir_implicit_cast(ira, field_value, payload_type); - if (type_is_invalid(casted_value->value->type)) - return ira->codegen->invalid_inst_gen; - - if (instr_is_comptime(casted_value)) { - ZigValue *val = ir_resolve_const(ira, casted_value, UndefBad); - if (val == nullptr) - return ira->codegen->invalid_inst_gen; - - IrInstGen *result = ir_const(ira, source_instr, union_type); - bigint_init_bigint(&result->value->data.x_union.tag, &union_field->enum_field->value); - result->value->data.x_union.payload = val; - - val->parent.id = ConstParentIdUnion; - val->parent.data.p_union.union_val = result->value; - - return result; - } - - IrInstGen *result_loc_inst = ir_resolve_result(ira, source_instr, no_result_loc(), - union_type, nullptr, true, true); - if (type_is_invalid(result_loc_inst->value->type) || result_loc_inst->value->type->id == ZigTypeIdUnreachable) { - return ira->codegen->invalid_inst_gen; - } - - IrInstGen *payload_ptr = ir_analyze_container_field_ptr(ira, only_field->name, source_instr, - result_loc_inst, source_instr, union_type, true); - if (type_is_invalid(payload_ptr->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *store_ptr_inst = ir_analyze_store_ptr(ira, source_instr, payload_ptr, casted_value, false); - if (type_is_invalid(store_ptr_inst->value->type)) - return ira->codegen->invalid_inst_gen; - - return ir_get_deref(ira, source_instr, result_loc_inst, nullptr); -} - -// Add a compile error and return ErrorSemanticAnalyzeFail if the pointer alignment does not work, -// otherwise return ErrorNone. Does not emit any instructions. -// Assumes that the pointer types have element types with the same ABI alignment. Avoids resolving the -// pointer types' alignments if both of the pointer types are ABI aligned. -static Error ir_cast_ptr_align(IrAnalyze *ira, IrInst* source_instr, ZigType *dest_ptr_type, - ZigType *src_ptr_type, AstNode *src_source_node) -{ - Error err; - - ir_assert(dest_ptr_type->id == ZigTypeIdPointer, source_instr); - ir_assert(src_ptr_type->id == ZigTypeIdPointer, source_instr); - - if (dest_ptr_type->data.pointer.explicit_alignment == 0 && - src_ptr_type->data.pointer.explicit_alignment == 0) - { - return ErrorNone; - } - - if ((err = type_resolve(ira->codegen, dest_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) - return ErrorSemanticAnalyzeFail; - - if ((err = type_resolve(ira->codegen, src_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) - return ErrorSemanticAnalyzeFail; - - uint32_t wanted_align = get_ptr_align(ira->codegen, dest_ptr_type); - uint32_t actual_align = get_ptr_align(ira->codegen, src_ptr_type); - if (wanted_align > actual_align) { - ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("cast increases pointer alignment")); - add_error_note(ira->codegen, msg, src_source_node, - buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&src_ptr_type->name), actual_align)); - add_error_note(ira->codegen, msg, source_instr->source_node, - buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&dest_ptr_type->name), wanted_align)); - return ErrorSemanticAnalyzeFail; - } - - return ErrorNone; -} - -static IrInstGen *ir_analyze_struct_value_field_value(IrAnalyze *ira, IrInst* source_instr, - IrInstGen *struct_operand, TypeStructField *field) -{ - IrInstGen *struct_ptr = ir_get_ref(ira, source_instr, struct_operand, true, false); - if (type_is_invalid(struct_ptr->value->type)) - return ira->codegen->invalid_inst_gen; - IrInstGen *field_ptr = ir_analyze_struct_field_ptr(ira, source_instr, field, struct_ptr, - struct_operand->value->type, false); - if (type_is_invalid(field_ptr->value->type)) - return ira->codegen->invalid_inst_gen; - return ir_get_deref(ira, source_instr, field_ptr, nullptr); -} - -static IrInstGen *ir_analyze_optional_value_payload_value(IrAnalyze *ira, IrInst* source_instr, - IrInstGen *optional_operand, bool safety_check_on) -{ - IrInstGen *opt_ptr = ir_get_ref(ira, source_instr, optional_operand, true, false); - IrInstGen *payload_ptr = ir_analyze_unwrap_optional_payload(ira, source_instr, opt_ptr, - safety_check_on, false); - return ir_get_deref(ira, source_instr, payload_ptr, nullptr); -} - -static IrInstGen *ir_analyze_cast(IrAnalyze *ira, IrInst *source_instr, - ZigType *wanted_type, IrInstGen *value) -{ - Error err; - ZigType *actual_type = value->value->type; - AstNode *source_node = source_instr->source_node; - - if (type_is_invalid(wanted_type) || type_is_invalid(actual_type)) { - return ira->codegen->invalid_inst_gen; - } - - // This means the wanted type is anything. - if (wanted_type == ira->codegen->builtin_types.entry_anytype) { - return value; - } - - // perfect match or non-const to const - ConstCastOnly const_cast_result = types_match_const_cast_only(ira, wanted_type, actual_type, - source_node, false); - if (const_cast_result.id == ConstCastResultIdInvalid) - return ira->codegen->invalid_inst_gen; - if (const_cast_result.id == ConstCastResultIdOk) { - return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop); - } - - if (const_cast_result.id == ConstCastResultIdFnCC) { - ir_assert(value->value->type->id == ZigTypeIdFn, source_instr); - // ConstCastResultIdFnCC is guaranteed to be the last one reported, meaning everything else is ok. - if (wanted_type->data.fn.fn_type_id.cc == CallingConventionAsync && - actual_type->data.fn.fn_type_id.cc == CallingConventionUnspecified) - { - ir_assert(value->value->data.x_ptr.special == ConstPtrSpecialFunction, source_instr); - ZigFn *fn = value->value->data.x_ptr.data.fn.fn_entry; - if (fn->inferred_async_node == nullptr) { - fn->inferred_async_node = source_instr->source_node; - } - return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop); - } - } - - // cast from T to ?T - // note that the *T to ?*T case is handled via the "ConstCastOnly" mechanism - if (wanted_type->id == ZigTypeIdOptional) { - ZigType *wanted_child_type = wanted_type->data.maybe.child_type; - if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node, - false).id == ConstCastResultIdOk) - { - return ir_analyze_optional_wrap(ira, source_instr, value, wanted_type, nullptr); - } else if (actual_type->id == ZigTypeIdComptimeInt || - actual_type->id == ZigTypeIdComptimeFloat) - { - if (ir_num_lit_fits_in_other_type(ira, value, wanted_child_type, true)) { - return ir_analyze_optional_wrap(ira, source_instr, value, wanted_type, nullptr); - } else { - return ira->codegen->invalid_inst_gen; - } - } else if ( - wanted_child_type->id == ZigTypeIdPointer && - wanted_child_type->data.pointer.ptr_len == PtrLenUnknown && - actual_type->id == ZigTypeIdPointer && - actual_type->data.pointer.ptr_len == PtrLenSingle && - actual_type->data.pointer.child_type->id == ZigTypeIdArray) - { - if ((err = type_resolve(ira->codegen, actual_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) - return ira->codegen->invalid_inst_gen; - if ((err = type_resolve(ira->codegen, wanted_child_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) - return ira->codegen->invalid_inst_gen; - if (get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, wanted_child_type) && - types_match_const_cast_only(ira, wanted_child_type->data.pointer.child_type, - actual_type->data.pointer.child_type->data.array.child_type, source_node, - !wanted_child_type->data.pointer.is_const).id == ConstCastResultIdOk) - { - IrInstGen *cast1 = ir_resolve_ptr_of_array_to_unknown_len_ptr(ira, source_instr, value, - wanted_child_type); - if (type_is_invalid(cast1->value->type)) - return ira->codegen->invalid_inst_gen; - return ir_analyze_optional_wrap(ira, source_instr, cast1, wanted_type, nullptr); - } - } - } - - // T to E!T - if (wanted_type->id == ZigTypeIdErrorUnion) { - if (types_match_const_cast_only(ira, wanted_type->data.error_union.payload_type, actual_type, - source_node, false).id == ConstCastResultIdOk) - { - return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type, nullptr); - } else if (actual_type->id == ZigTypeIdComptimeInt || - actual_type->id == ZigTypeIdComptimeFloat) - { - if (ir_num_lit_fits_in_other_type(ira, value, wanted_type->data.error_union.payload_type, true)) { - return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type, nullptr); - } else { - return ira->codegen->invalid_inst_gen; - } - } - } - - // cast from T to E!?T - if (wanted_type->id == ZigTypeIdErrorUnion && - wanted_type->data.error_union.payload_type->id == ZigTypeIdOptional && - actual_type->id != ZigTypeIdOptional) - { - ZigType *wanted_child_type = wanted_type->data.error_union.payload_type->data.maybe.child_type; - if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node, false).id == ConstCastResultIdOk || - actual_type->id == ZigTypeIdNull || - actual_type->id == ZigTypeIdComptimeInt || - actual_type->id == ZigTypeIdComptimeFloat) - { - IrInstGen *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.error_union.payload_type, value); - if (type_is_invalid(cast1->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1); - if (type_is_invalid(cast2->value->type)) - return ira->codegen->invalid_inst_gen; - - return cast2; - } - } - - - // cast from comptime-known number to another number type - if (instr_is_comptime(value) && - (actual_type->id == ZigTypeIdInt || actual_type->id == ZigTypeIdComptimeInt || - actual_type->id == ZigTypeIdFloat || actual_type->id == ZigTypeIdComptimeFloat) && - (wanted_type->id == ZigTypeIdInt || wanted_type->id == ZigTypeIdComptimeInt || - wanted_type->id == ZigTypeIdFloat || wanted_type->id == ZigTypeIdComptimeFloat)) - { - if (value->value->special == ConstValSpecialUndef) { - IrInstGen *result = ir_const(ira, source_instr, wanted_type); - result->value->special = ConstValSpecialUndef; - return result; - } - if (ir_num_lit_fits_in_other_type(ira, value, wanted_type, true)) { - if (wanted_type->id == ZigTypeIdComptimeInt || wanted_type->id == ZigTypeIdInt) { - IrInstGen *result = ir_const(ira, source_instr, wanted_type); - if (actual_type->id == ZigTypeIdComptimeInt || actual_type->id == ZigTypeIdInt) { - copy_const_val(ira->codegen, result->value, value->value); - result->value->type = wanted_type; - } else { - float_init_bigint(&result->value->data.x_bigint, value->value); - } - return result; - } else if (wanted_type->id == ZigTypeIdComptimeFloat || wanted_type->id == ZigTypeIdFloat) { - IrInstGen *result = ir_const(ira, source_instr, wanted_type); - if (actual_type->id == ZigTypeIdComptimeInt || actual_type->id == ZigTypeIdInt) { - BigFloat bf; - bigfloat_init_bigint(&bf, &value->value->data.x_bigint); - float_init_bigfloat(result->value, &bf); - } else { - float_init_float(result->value, value->value); - } - return result; - } - zig_unreachable(); - } else { - return ira->codegen->invalid_inst_gen; - } - } - - // widening conversion - if (wanted_type->id == ZigTypeIdInt && - actual_type->id == ZigTypeIdInt && - wanted_type->data.integral.is_signed == actual_type->data.integral.is_signed && - wanted_type->data.integral.bit_count >= actual_type->data.integral.bit_count) - { - return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type); - } - - // small enough unsigned ints can get casted to large enough signed ints - if (wanted_type->id == ZigTypeIdInt && wanted_type->data.integral.is_signed && - actual_type->id == ZigTypeIdInt && !actual_type->data.integral.is_signed && - wanted_type->data.integral.bit_count > actual_type->data.integral.bit_count) - { - return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type); - } - - // float widening conversion - if (wanted_type->id == ZigTypeIdFloat && - actual_type->id == ZigTypeIdFloat && - wanted_type->data.floating.bit_count >= actual_type->data.floating.bit_count) - { - return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type); - } - - // *[N]T to ?[]T - if (wanted_type->id == ZigTypeIdOptional && - is_slice(wanted_type->data.maybe.child_type) && - actual_type->id == ZigTypeIdPointer && - actual_type->data.pointer.ptr_len == PtrLenSingle && - actual_type->data.pointer.child_type->id == ZigTypeIdArray) - { - IrInstGen *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.maybe.child_type, value); - if (type_is_invalid(cast1->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1); - if (type_is_invalid(cast2->value->type)) - return ira->codegen->invalid_inst_gen; - - return cast2; - } - - // *[N]T to [*]T and [*c]T - if (wanted_type->id == ZigTypeIdPointer && - (wanted_type->data.pointer.ptr_len == PtrLenUnknown || wanted_type->data.pointer.ptr_len == PtrLenC) && - actual_type->id == ZigTypeIdPointer && - actual_type->data.pointer.ptr_len == PtrLenSingle && - actual_type->data.pointer.child_type->id == ZigTypeIdArray && - (!actual_type->data.pointer.is_const || wanted_type->data.pointer.is_const) && - (!actual_type->data.pointer.is_volatile || wanted_type->data.pointer.is_volatile)) - { - ZigType *actual_array_type = actual_type->data.pointer.child_type; - if (wanted_type->data.pointer.sentinel == nullptr || - (actual_array_type->data.array.sentinel != nullptr && - const_values_equal(ira->codegen, wanted_type->data.pointer.sentinel, - actual_array_type->data.array.sentinel))) - { - if ((err = type_resolve(ira->codegen, actual_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) - return ira->codegen->invalid_inst_gen; - if ((err = type_resolve(ira->codegen, wanted_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) - return ira->codegen->invalid_inst_gen; - if (get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, wanted_type) && - types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, - actual_type->data.pointer.child_type->data.array.child_type, source_node, - !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk) - { - return ir_resolve_ptr_of_array_to_unknown_len_ptr(ira, source_instr, value, wanted_type); - } - } - } - - // *[N]T to []T - // *[N]T to E![]T - if ((is_slice(wanted_type) || - (wanted_type->id == ZigTypeIdErrorUnion && - is_slice(wanted_type->data.error_union.payload_type))) && - actual_type->id == ZigTypeIdPointer && - actual_type->data.pointer.ptr_len == PtrLenSingle && - actual_type->data.pointer.child_type->id == ZigTypeIdArray) - { - ZigType *slice_type = (wanted_type->id == ZigTypeIdErrorUnion) ? - wanted_type->data.error_union.payload_type : wanted_type; - ZigType *slice_ptr_type = slice_type->data.structure.fields[slice_ptr_index]->type_entry; - assert(slice_ptr_type->id == ZigTypeIdPointer); - ZigType *array_type = actual_type->data.pointer.child_type; - bool const_ok = (slice_ptr_type->data.pointer.is_const || array_type->data.array.len == 0 - || !actual_type->data.pointer.is_const); - - if (const_ok && types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type, - array_type->data.array.child_type, source_node, - !slice_ptr_type->data.pointer.is_const).id == ConstCastResultIdOk && - (slice_ptr_type->data.pointer.sentinel == nullptr || - (array_type->data.array.sentinel != nullptr && - const_values_equal(ira->codegen, array_type->data.array.sentinel, - slice_ptr_type->data.pointer.sentinel)))) - { - // If the pointers both have ABI align, it works. - // Or if the array length is 0, alignment doesn't matter. - bool ok_align = array_type->data.array.len == 0 || - (slice_ptr_type->data.pointer.explicit_alignment == 0 && - actual_type->data.pointer.explicit_alignment == 0); - if (!ok_align) { - // If either one has non ABI align, we have to resolve them both - if ((err = type_resolve(ira->codegen, actual_type->data.pointer.child_type, - ResolveStatusAlignmentKnown))) - { - return ira->codegen->invalid_inst_gen; - } - if ((err = type_resolve(ira->codegen, slice_ptr_type->data.pointer.child_type, - ResolveStatusAlignmentKnown))) - { - return ira->codegen->invalid_inst_gen; - } - ok_align = get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, slice_ptr_type); - } - if (ok_align) { - if (wanted_type->id == ZigTypeIdErrorUnion) { - IrInstGen *cast1 = ir_analyze_cast(ira, source_instr, slice_type, value); - if (type_is_invalid(cast1->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1); - if (type_is_invalid(cast2->value->type)) - return ira->codegen->invalid_inst_gen; - - return cast2; - } else { - return ir_resolve_ptr_of_array_to_slice(ira, source_instr, value, slice_type, nullptr); - } - } - } - } - - // @Vector(N,T1) to @Vector(N,T2) - if (actual_type->id == ZigTypeIdVector && wanted_type->id == ZigTypeIdVector) { - if (actual_type->data.vector.len == wanted_type->data.vector.len && - types_match_const_cast_only(ira, wanted_type->data.vector.elem_type, - actual_type->data.vector.elem_type, source_node, false).id == ConstCastResultIdOk) - { - return ir_analyze_bit_cast(ira, source_instr, value, wanted_type); - } - } - - // *@Frame(func) to anyframe->T or anyframe - // *@Frame(func) to ?anyframe->T or ?anyframe - // *@Frame(func) to E!anyframe->T or E!anyframe - if (actual_type->id == ZigTypeIdPointer && actual_type->data.pointer.ptr_len == PtrLenSingle && - !actual_type->data.pointer.is_const && - actual_type->data.pointer.child_type->id == ZigTypeIdFnFrame) - { - ZigType *anyframe_type; - if (wanted_type->id == ZigTypeIdAnyFrame) { - anyframe_type = wanted_type; - } else if (wanted_type->id == ZigTypeIdOptional && - wanted_type->data.maybe.child_type->id == ZigTypeIdAnyFrame) - { - anyframe_type = wanted_type->data.maybe.child_type; - } else if (wanted_type->id == ZigTypeIdErrorUnion && - wanted_type->data.error_union.payload_type->id == ZigTypeIdAnyFrame) - { - anyframe_type = wanted_type->data.error_union.payload_type; - } else { - anyframe_type = nullptr; - } - if (anyframe_type != nullptr) { - bool ok = true; - if (anyframe_type->data.any_frame.result_type != nullptr) { - ZigFn *fn = actual_type->data.pointer.child_type->data.frame.fn; - ZigType *fn_return_type = fn->type_entry->data.fn.fn_type_id.return_type; - if (anyframe_type->data.any_frame.result_type != fn_return_type) { - ok = false; - } - } - if (ok) { - IrInstGen *cast1 = ir_analyze_frame_ptr_to_anyframe(ira, source_instr, value, anyframe_type); - if (anyframe_type == wanted_type) - return cast1; - return ir_analyze_cast(ira, source_instr, wanted_type, cast1); - } - } - } - - // anyframe->T to anyframe - if (actual_type->id == ZigTypeIdAnyFrame && actual_type->data.any_frame.result_type != nullptr && - wanted_type->id == ZigTypeIdAnyFrame && wanted_type->data.any_frame.result_type == nullptr) - { - return ir_analyze_anyframe_to_anyframe(ira, source_instr, value, wanted_type); - } - - // cast from null literal to maybe type - if (wanted_type->id == ZigTypeIdOptional && - actual_type->id == ZigTypeIdNull) - { - return ir_analyze_null_to_maybe(ira, source_instr, value, wanted_type); - } - - // cast from null literal to C pointer - if (wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.ptr_len == PtrLenC && - actual_type->id == ZigTypeIdNull) - { - return ir_analyze_null_to_c_pointer(ira, source_instr, value, wanted_type); - } - - // cast from E to E!T - if (wanted_type->id == ZigTypeIdErrorUnion && - actual_type->id == ZigTypeIdErrorSet) - { - return ir_analyze_err_wrap_code(ira, source_instr, value, wanted_type, nullptr); - } - - // cast from typed number to integer or float literal. - // works when the number is known at compile time - if (instr_is_comptime(value) && - ((actual_type->id == ZigTypeIdInt && wanted_type->id == ZigTypeIdComptimeInt) || - (actual_type->id == ZigTypeIdFloat && wanted_type->id == ZigTypeIdComptimeFloat))) - { - return ir_analyze_number_to_literal(ira, source_instr, value, wanted_type); - } - - // cast from enum literal to enum with matching field name - if (actual_type->id == ZigTypeIdEnumLiteral && wanted_type->id == ZigTypeIdEnum) - { - return ir_analyze_enum_literal(ira, source_instr, value, wanted_type); - } - - // cast from enum literal to optional enum - if (actual_type->id == ZigTypeIdEnumLiteral && - (wanted_type->id == ZigTypeIdOptional && wanted_type->data.maybe.child_type->id == ZigTypeIdEnum)) - { - IrInstGen *result = ir_analyze_enum_literal(ira, source_instr, value, wanted_type->data.maybe.child_type); - if (type_is_invalid(result->value->type)) - return result; - - return ir_analyze_optional_wrap(ira, source_instr, value, wanted_type, nullptr); - } - - // cast from enum literal to error union when payload is an enum - if (actual_type->id == ZigTypeIdEnumLiteral && - (wanted_type->id == ZigTypeIdErrorUnion && wanted_type->data.error_union.payload_type->id == ZigTypeIdEnum)) - { - IrInstGen *result = ir_analyze_enum_literal(ira, source_instr, value, wanted_type->data.error_union.payload_type); - if (type_is_invalid(result->value->type)) - return result; - - return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type, nullptr); - } - - // cast from union to the enum type of the union - if (actual_type->id == ZigTypeIdUnion && wanted_type->id == ZigTypeIdEnum) { - if ((err = type_resolve(ira->codegen, actual_type, ResolveStatusZeroBitsKnown))) - return ira->codegen->invalid_inst_gen; - - if (actual_type->data.unionation.tag_type == wanted_type) { - return ir_analyze_union_to_tag(ira, source_instr, value, wanted_type); - } - } - - // enum to union which has the enum as the tag type, or - // enum literal to union which has a matching enum as the tag type - if (is_tagged_union(wanted_type) && (actual_type->id == ZigTypeIdEnum || - actual_type->id == ZigTypeIdEnumLiteral)) - { - return ir_analyze_enum_to_union(ira, source_instr, value, wanted_type); - } - - // cast from *T to *[1]T - if (wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.ptr_len == PtrLenSingle && - actual_type->id == ZigTypeIdPointer && actual_type->data.pointer.ptr_len == PtrLenSingle) - { - ZigType *array_type = wanted_type->data.pointer.child_type; - if (array_type->id == ZigTypeIdArray && array_type->data.array.len == 1 && - types_match_const_cast_only(ira, array_type->data.array.child_type, - actual_type->data.pointer.child_type, source_node, - !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk && - // `types_match_const_cast_only` only gets info for child_types - (!actual_type->data.pointer.is_const || wanted_type->data.pointer.is_const) && - (!actual_type->data.pointer.is_volatile || wanted_type->data.pointer.is_volatile)) - { - if ((err = ir_cast_ptr_align(ira, source_instr, wanted_type, actual_type, value->base.source_node))) - return ira->codegen->invalid_inst_gen; - - return ir_analyze_ptr_to_array(ira, source_instr, value, wanted_type); - } - } - - // [:x]T to [*:x]T - // [:x]T to [*c]T - if (wanted_type->id == ZigTypeIdPointer && is_slice(actual_type) && - ((wanted_type->data.pointer.ptr_len == PtrLenUnknown && wanted_type->data.pointer.sentinel != nullptr) || - wanted_type->data.pointer.ptr_len == PtrLenC)) - { - ZigType *slice_ptr_type = resolve_struct_field_type(ira->codegen, - actual_type->data.structure.fields[slice_ptr_index]); - if (types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, - slice_ptr_type->data.pointer.child_type, source_node, - !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk && - (slice_ptr_type->data.pointer.sentinel != nullptr && - (wanted_type->data.pointer.ptr_len == PtrLenC || - const_values_equal(ira->codegen, wanted_type->data.pointer.sentinel, - slice_ptr_type->data.pointer.sentinel)))) - { - TypeStructField *ptr_field = actual_type->data.structure.fields[slice_ptr_index]; - IrInstGen *slice_ptr = ir_analyze_struct_value_field_value(ira, source_instr, value, ptr_field); - return ir_implicit_cast2(ira, source_instr, slice_ptr, wanted_type); - } - } - - // cast from *T and [*]T to *c_void and ?*c_void - // but don't do it if the actual type is a double pointer - if (is_pointery_and_elem_is_not_pointery(actual_type)) { - ZigType *dest_ptr_type = nullptr; - if (wanted_type->id == ZigTypeIdPointer && - actual_type->id != ZigTypeIdOptional && - wanted_type->data.pointer.child_type == ira->codegen->builtin_types.entry_c_void) - { - dest_ptr_type = wanted_type; - } else if (wanted_type->id == ZigTypeIdOptional && - wanted_type->data.maybe.child_type->id == ZigTypeIdPointer && - wanted_type->data.maybe.child_type->data.pointer.child_type == ira->codegen->builtin_types.entry_c_void) - { - dest_ptr_type = wanted_type->data.maybe.child_type; - } - if (dest_ptr_type != nullptr) { - return ir_analyze_ptr_cast(ira, source_instr, value, source_instr, wanted_type, source_instr, true, - false); - } - } - - // cast from T to *T where T is zero bits - if (wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.ptr_len == PtrLenSingle && - types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, - actual_type, source_node, !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk) - { - bool has_bits; - if ((err = type_has_bits2(ira->codegen, actual_type, &has_bits))) - return ira->codegen->invalid_inst_gen; - if (!has_bits) { - return ir_get_ref(ira, source_instr, value, false, false); - } - } - - // cast from @Vector(N, T) to [N]T - if (wanted_type->id == ZigTypeIdArray && actual_type->id == ZigTypeIdVector && - wanted_type->data.array.len == actual_type->data.vector.len && - types_match_const_cast_only(ira, wanted_type->data.array.child_type, - actual_type->data.vector.elem_type, source_node, false).id == ConstCastResultIdOk) - { - return ir_analyze_vector_to_array(ira, source_instr, value, wanted_type, nullptr); - } - - // cast from [N]T to @Vector(N, T) - if (actual_type->id == ZigTypeIdArray && wanted_type->id == ZigTypeIdVector && - actual_type->data.array.len == wanted_type->data.vector.len && - types_match_const_cast_only(ira, actual_type->data.array.child_type, - wanted_type->data.vector.elem_type, source_node, false).id == ConstCastResultIdOk) - { - return ir_analyze_array_to_vector(ira, source_instr, value, wanted_type); - } - - // casting between C pointers and normal pointers - if (wanted_type->id == ZigTypeIdPointer && actual_type->id == ZigTypeIdPointer && - (wanted_type->data.pointer.ptr_len == PtrLenC || actual_type->data.pointer.ptr_len == PtrLenC) && - types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, - actual_type->data.pointer.child_type, source_node, - !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk) - { - return ir_analyze_ptr_cast(ira, source_instr, value, source_instr, wanted_type, source_instr, true, false); - } - - // cast from integer to C pointer - if (wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.ptr_len == PtrLenC && - (actual_type->id == ZigTypeIdInt || actual_type->id == ZigTypeIdComptimeInt)) - { - return ir_analyze_int_to_c_ptr(ira, source_instr, value, wanted_type); - } - - // cast from inferred struct type to array, union, or struct - if (is_anon_container(actual_type)) { - const bool is_array_init = - actual_type->data.structure.special == StructSpecialInferredTuple; - const uint32_t field_count = actual_type->data.structure.src_field_count; - - if (wanted_type->id == ZigTypeIdArray && (is_array_init || field_count == 0) && - wanted_type->data.array.len == field_count) - { - return ir_analyze_struct_literal_to_array(ira, source_instr, value, wanted_type); - } else if (wanted_type->id == ZigTypeIdStruct && !is_slice(wanted_type) && - (!is_array_init || field_count == 0)) - { - return ir_analyze_struct_literal_to_struct(ira, source_instr, value, wanted_type); - } else if (wanted_type->id == ZigTypeIdUnion && !is_array_init && field_count == 1) { - return ir_analyze_struct_literal_to_union(ira, source_instr, value, wanted_type); - } - } - - // cast from undefined to anything - if (actual_type->id == ZigTypeIdUndefined) { - return ir_analyze_undefined_to_anything(ira, source_instr, value, wanted_type); - } - - // T to ?U, where T implicitly casts to U - if (wanted_type->id == ZigTypeIdOptional && actual_type->id != ZigTypeIdOptional) { - IrInstGen *cast1 = ir_implicit_cast2(ira, source_instr, value, wanted_type->data.maybe.child_type); - if (type_is_invalid(cast1->value->type)) - return ira->codegen->invalid_inst_gen; - return ir_implicit_cast2(ira, source_instr, cast1, wanted_type); - } - - // T to E!U, where T implicitly casts to U - if (wanted_type->id == ZigTypeIdErrorUnion && actual_type->id != ZigTypeIdErrorUnion && - actual_type->id != ZigTypeIdErrorSet) - { - IrInstGen *cast1 = ir_implicit_cast2(ira, source_instr, value, wanted_type->data.error_union.payload_type); - if (type_is_invalid(cast1->value->type)) - return ira->codegen->invalid_inst_gen; - return ir_implicit_cast2(ira, source_instr, cast1, wanted_type); - } - - ErrorMsg *parent_msg = ir_add_error_node(ira, source_instr->source_node, - buf_sprintf("expected type '%s', found '%s'", - buf_ptr(&wanted_type->name), - buf_ptr(&actual_type->name))); - report_recursive_error(ira, source_instr->source_node, &const_cast_result, parent_msg); - return ira->codegen->invalid_inst_gen; -} - -static IrInstGen *ir_implicit_cast2(IrAnalyze *ira, IrInst *value_source_instr, - IrInstGen *value, ZigType *expected_type) -{ - assert(value); - assert(!expected_type || !type_is_invalid(expected_type)); - assert(value->value->type); - assert(!type_is_invalid(value->value->type)); - if (expected_type == nullptr) - return value; // anything will do - if (expected_type == value->value->type) - return value; // match - if (value->value->type->id == ZigTypeIdUnreachable) - return value; - - return ir_analyze_cast(ira, value_source_instr, expected_type, value); -} - -static IrInstGen *ir_implicit_cast(IrAnalyze *ira, IrInstGen *value, ZigType *expected_type) { - return ir_implicit_cast2(ira, &value->base, value, expected_type); -} - -static ZigType *get_ptr_elem_type(CodeGen *g, IrInstGen *ptr) { - ir_assert_gen(ptr->value->type->id == ZigTypeIdPointer, ptr); - ZigType *elem_type = ptr->value->type->data.pointer.child_type; - if (elem_type != g->builtin_types.entry_anytype) - return elem_type; - - if (ir_resolve_lazy(g, ptr->base.source_node, ptr->value)) - return g->builtin_types.entry_invalid; - - assert(value_is_comptime(ptr->value)); - ZigValue *pointee = const_ptr_pointee_unchecked(g, ptr->value); - return pointee->type; -} - -static IrInstGen *ir_get_deref(IrAnalyze *ira, IrInst* source_instruction, IrInstGen *ptr, - ResultLoc *result_loc) -{ - Error err; - ZigType *ptr_type = ptr->value->type; - if (type_is_invalid(ptr_type)) - return ira->codegen->invalid_inst_gen; - - if (ptr_type->id != ZigTypeIdPointer) { - ir_add_error_node(ira, source_instruction->source_node, - buf_sprintf("attempt to dereference non-pointer type '%s'", - buf_ptr(&ptr_type->name))); - return ira->codegen->invalid_inst_gen; - } - - ZigType *child_type = ptr_type->data.pointer.child_type; - if (type_is_invalid(child_type)) - return ira->codegen->invalid_inst_gen; - // if the child type has one possible value, the deref is comptime - switch (type_has_one_possible_value(ira->codegen, child_type)) { - case OnePossibleValueInvalid: - return ira->codegen->invalid_inst_gen; - case OnePossibleValueYes: - return ir_const_move(ira, source_instruction, - get_the_one_possible_value(ira->codegen, child_type)); - case OnePossibleValueNo: - break; - } - if (instr_is_comptime(ptr)) { - if (ptr->value->special == ConstValSpecialUndef) { - // If we are in a TypeOf call, we return an undefined value instead of erroring - // since we know the type. - if (get_scope_typeof(source_instruction->scope)) { - return ir_const_undef(ira, source_instruction, child_type); - } - - ir_add_error(ira, &ptr->base, buf_sprintf("attempt to dereference undefined value")); - return ira->codegen->invalid_inst_gen; - } - if (ptr->value->data.x_ptr.mut != ConstPtrMutRuntimeVar) { - ZigValue *pointee = const_ptr_pointee_unchecked(ira->codegen, ptr->value); - if (child_type == ira->codegen->builtin_types.entry_anytype) { - child_type = pointee->type; - } - if (pointee->special != ConstValSpecialRuntime) { - IrInstGen *result = ir_const(ira, source_instruction, child_type); - - if ((err = ir_read_const_ptr(ira, ira->codegen, source_instruction->source_node, result->value, - ptr->value))) - { - return ira->codegen->invalid_inst_gen; - } - result->value->type = child_type; - return result; - } - } - } - - // if the instruction is a const ref instruction we can skip it - if (ptr->id == IrInstGenIdRef) { - IrInstGenRef *ref_inst = reinterpret_cast(ptr); - return ref_inst->operand; - } - - // If the instruction is a element pointer instruction to a vector, we emit - // vector element extract instruction rather than load pointer. If the - // pointer type has non-VECTOR_INDEX_RUNTIME value, it would have been - // possible to implement this in the codegen for IrInstGenLoadPtr. - // However if it has VECTOR_INDEX_RUNTIME then we must emit a compile error - // if the vector index cannot be determined right here, right now, because - // the type information does not contain enough information to actually - // perform a dereference. - if (ptr_type->data.pointer.vector_index == VECTOR_INDEX_RUNTIME) { - if (ptr->id == IrInstGenIdElemPtr) { - IrInstGenElemPtr *elem_ptr = (IrInstGenElemPtr *)ptr; - IrInstGen *vector_loaded = ir_get_deref(ira, &elem_ptr->array_ptr->base, - elem_ptr->array_ptr, nullptr); - IrInstGen *elem_index = elem_ptr->elem_index; - return ir_build_vector_extract_elem(ira, source_instruction, vector_loaded, elem_index); - } - ir_add_error(ira, &ptr->base, - buf_sprintf("unable to determine vector element index of type '%s'", buf_ptr(&ptr_type->name))); - return ira->codegen->invalid_inst_gen; - } - - IrInstGen *result_loc_inst; - if (ptr_type->data.pointer.host_int_bytes != 0 && handle_is_ptr(ira->codegen, child_type)) { - if (result_loc == nullptr) result_loc = no_result_loc(); - result_loc_inst = ir_resolve_result(ira, source_instruction, result_loc, child_type, nullptr, true, true); - if (type_is_invalid(result_loc_inst->value->type) || result_loc_inst->value->type->id == ZigTypeIdUnreachable) { - return result_loc_inst; - } - } else { - result_loc_inst = nullptr; - } - - return ir_build_load_ptr_gen(ira, source_instruction, ptr, child_type, result_loc_inst); -} - -static bool ir_resolve_const_align(CodeGen *codegen, IrExecutableGen *exec, AstNode *source_node, - ZigValue *const_val, uint32_t *out) -{ - Error err; - if ((err = ir_resolve_const_val(codegen, exec, source_node, const_val, UndefBad))) - return false; - - uint32_t align_bytes = bigint_as_u32(&const_val->data.x_bigint); - if (align_bytes == 0) { - exec_add_error_node_gen(codegen, exec, source_node, buf_sprintf("alignment must be >= 1")); - return false; - } - - if (!is_power_of_2(align_bytes)) { - exec_add_error_node_gen(codegen, exec, source_node, - buf_sprintf("alignment value %" PRIu32 " is not a power of 2", align_bytes)); - return false; - } - - *out = align_bytes; - return true; -} - -static bool ir_resolve_align(IrAnalyze *ira, IrInstGen *value, ZigType *elem_type, uint32_t *out) { - if (type_is_invalid(value->value->type)) - return false; - - // Look for this pattern: `*align(@alignOf(T)) T`. - // This can be resolved to be `*out = 0` without resolving any alignment. - if (elem_type != nullptr && value->value->special == ConstValSpecialLazy && - value->value->data.x_lazy->id == LazyValueIdAlignOf) - { - LazyValueAlignOf *lazy_align_of = reinterpret_cast(value->value->data.x_lazy); - - ZigType *lazy_elem_type = ir_resolve_type(lazy_align_of->ira, lazy_align_of->target_type); - if (type_is_invalid(lazy_elem_type)) - return false; - - if (elem_type == lazy_elem_type) { - *out = 0; - return true; - } - } - - IrInstGen *casted_value = ir_implicit_cast(ira, value, get_align_amt_type(ira->codegen)); - if (type_is_invalid(casted_value->value->type)) - return false; - - return ir_resolve_const_align(ira->codegen, ira->new_irb.exec, value->base.source_node, - casted_value->value, out); -} - -static bool ir_resolve_unsigned(IrAnalyze *ira, IrInstGen *value, ZigType *int_type, uint64_t *out) { - if (type_is_invalid(value->value->type)) - return false; - - IrInstGen *casted_value = ir_implicit_cast(ira, value, int_type); - if (type_is_invalid(casted_value->value->type)) - return false; - - ZigValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); - if (!const_val) - return false; - - *out = bigint_as_u64(&const_val->data.x_bigint); - return true; -} - -static bool ir_resolve_usize(IrAnalyze *ira, IrInstGen *value, uint64_t *out) { - return ir_resolve_unsigned(ira, value, ira->codegen->builtin_types.entry_usize, out); -} - -static bool ir_resolve_bool(IrAnalyze *ira, IrInstGen *value, bool *out) { - if (type_is_invalid(value->value->type)) - return false; - - IrInstGen *casted_value = ir_implicit_cast(ira, value, ira->codegen->builtin_types.entry_bool); - if (type_is_invalid(casted_value->value->type)) - return false; - - ZigValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); - if (!const_val) - return false; - - *out = const_val->data.x_bool; - return true; -} - -static bool ir_resolve_comptime(IrAnalyze *ira, IrInstGen *value, bool *out) { - if (!value) { - *out = false; - return true; - } - return ir_resolve_bool(ira, value, out); -} - -static bool ir_resolve_atomic_order(IrAnalyze *ira, IrInstGen *value, AtomicOrder *out) { - if (type_is_invalid(value->value->type)) - return false; - - ZigType *atomic_order_type = get_builtin_type(ira->codegen, "AtomicOrder"); - - IrInstGen *casted_value = ir_implicit_cast(ira, value, atomic_order_type); - if (type_is_invalid(casted_value->value->type)) - return false; - - ZigValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); - if (!const_val) - return false; - - *out = (AtomicOrder)bigint_as_u32(&const_val->data.x_enum_tag); - return true; -} - -static bool ir_resolve_atomic_rmw_op(IrAnalyze *ira, IrInstGen *value, AtomicRmwOp *out) { - if (type_is_invalid(value->value->type)) - return false; - - ZigType *atomic_rmw_op_type = get_builtin_type(ira->codegen, "AtomicRmwOp"); - - IrInstGen *casted_value = ir_implicit_cast(ira, value, atomic_rmw_op_type); - if (type_is_invalid(casted_value->value->type)) - return false; - - ZigValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); - if (!const_val) - return false; - - *out = (AtomicRmwOp)bigint_as_u32(&const_val->data.x_enum_tag); - return true; -} - -static bool ir_resolve_global_linkage(IrAnalyze *ira, IrInstGen *value, GlobalLinkageId *out) { - if (type_is_invalid(value->value->type)) - return false; - - ZigType *global_linkage_type = get_builtin_type(ira->codegen, "GlobalLinkage"); - - IrInstGen *casted_value = ir_implicit_cast(ira, value, global_linkage_type); - if (type_is_invalid(casted_value->value->type)) - return false; - - ZigValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); - if (!const_val) - return false; - - *out = (GlobalLinkageId)bigint_as_u32(&const_val->data.x_enum_tag); - return true; -} - -static bool ir_resolve_float_mode(IrAnalyze *ira, IrInstGen *value, FloatMode *out) { - if (type_is_invalid(value->value->type)) - return false; - - ZigType *float_mode_type = get_builtin_type(ira->codegen, "FloatMode"); - - IrInstGen *casted_value = ir_implicit_cast(ira, value, float_mode_type); - if (type_is_invalid(casted_value->value->type)) - return false; - - ZigValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); - if (!const_val) - return false; - - *out = (FloatMode)bigint_as_u32(&const_val->data.x_enum_tag); - return true; -} - -static Buf *ir_resolve_str(IrAnalyze *ira, IrInstGen *value) { - if (type_is_invalid(value->value->type)) - return nullptr; - - ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, - true, false, PtrLenUnknown, 0, 0, 0, false); - ZigType *str_type = get_slice_type(ira->codegen, ptr_type); - IrInstGen *casted_value = ir_implicit_cast(ira, value, str_type); - if (type_is_invalid(casted_value->value->type)) - return nullptr; - - ZigValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); - if (!const_val) - return nullptr; - - ZigValue *ptr_field = const_val->data.x_struct.fields[slice_ptr_index]; - ZigValue *len_field = const_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; - expand_undef_array(ira->codegen, array_val); - size_t len = bigint_as_usize(&len_field->data.x_bigint); - if (array_val->data.x_array.special == ConstArraySpecialBuf && len == buf_len(array_val->data.x_array.data.s_buf)) { - return array_val->data.x_array.data.s_buf; - } - 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) { - ir_add_error(ira, &casted_value->base, buf_sprintf("use of undefined value")); - return nullptr; - } - 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; - } - return result; -} - -static IrInstGen *ir_analyze_instruction_add_implicit_return_type(IrAnalyze *ira, - IrInstSrcAddImplicitReturnType *instruction) -{ - IrInstGen *value = instruction->value->child; - if (type_is_invalid(value->value->type)) - return ir_unreach_error(ira); - - if (instruction->result_loc_ret == nullptr || !instruction->result_loc_ret->implicit_return_type_done) { - ira->src_implicit_return_type_list.append(value); - } - - return ir_const_void(ira, &instruction->base.base); -} - -static IrInstGen *ir_analyze_instruction_return(IrAnalyze *ira, IrInstSrcReturn *instruction) { - if (instruction->operand == nullptr) { - // result location mechanism took care of it. - IrInstGen *result = ir_build_return_gen(ira, &instruction->base.base, nullptr); - return ir_finish_anal(ira, result); - } - - IrInstGen *operand = instruction->operand->child; - if (type_is_invalid(operand->value->type)) - return ir_unreach_error(ira); - - IrInstGen *casted_operand = ir_implicit_cast(ira, operand, ira->explicit_return_type); - if (type_is_invalid(casted_operand->value->type)) { - AstNode *source_node = ira->explicit_return_type_source_node; - if (source_node != nullptr) { - ErrorMsg *msg = ira->codegen->errors.last(); - add_error_note(ira->codegen, msg, source_node, - buf_sprintf("return type declared here")); - } - return ir_unreach_error(ira); - } - - if (!instr_is_comptime(operand) && ira->explicit_return_type != nullptr && - handle_is_ptr(ira->codegen, ira->explicit_return_type)) - { - // result location mechanism took care of it. - IrInstGen *result = ir_build_return_gen(ira, &instruction->base.base, nullptr); - return ir_finish_anal(ira, result); - } - - if (casted_operand->value->special == ConstValSpecialRuntime && - casted_operand->value->type->id == ZigTypeIdPointer && - casted_operand->value->data.rh_ptr == RuntimeHintPtrStack) - { - ir_add_error(ira, &instruction->operand->base, buf_sprintf("function returns address of local variable")); - return ir_unreach_error(ira); - } - - IrInstGen *result = ir_build_return_gen(ira, &instruction->base.base, casted_operand); - return ir_finish_anal(ira, result); -} - -static IrInstGen *ir_analyze_instruction_const(IrAnalyze *ira, IrInstSrcConst *instruction) { - return ir_const_move(ira, &instruction->base.base, instruction->value); -} - -static IrInstGen *ir_analyze_bin_op_bool(IrAnalyze *ira, IrInstSrcBinOp *bin_op_instruction) { - IrInstGen *op1 = bin_op_instruction->op1->child; - if (type_is_invalid(op1->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *op2 = bin_op_instruction->op2->child; - if (type_is_invalid(op2->value->type)) - return ira->codegen->invalid_inst_gen; - - ZigType *bool_type = ira->codegen->builtin_types.entry_bool; - - IrInstGen *casted_op1 = ir_implicit_cast(ira, op1, bool_type); - if (type_is_invalid(casted_op1->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *casted_op2 = ir_implicit_cast(ira, op2, bool_type); - if (type_is_invalid(casted_op2->value->type)) - return ira->codegen->invalid_inst_gen; - - if (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2)) { - ZigValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad); - if (op1_val == nullptr) - return ira->codegen->invalid_inst_gen; - - ZigValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); - if (op2_val == nullptr) - return ira->codegen->invalid_inst_gen; - - assert(casted_op1->value->type->id == ZigTypeIdBool); - assert(casted_op2->value->type->id == ZigTypeIdBool); - bool result_bool; - if (bin_op_instruction->op_id == IrBinOpBoolOr) { - result_bool = op1_val->data.x_bool || op2_val->data.x_bool; - } else if (bin_op_instruction->op_id == IrBinOpBoolAnd) { - result_bool = op1_val->data.x_bool && op2_val->data.x_bool; - } else { - zig_unreachable(); - } - return ir_const_bool(ira, &bin_op_instruction->base.base, result_bool); - } - - return ir_build_bin_op_gen(ira, &bin_op_instruction->base.base, bool_type, - bin_op_instruction->op_id, casted_op1, casted_op2, bin_op_instruction->safety_check_on); -} - -static bool resolve_cmp_op_id(IrBinOp op_id, Cmp cmp) { - switch (op_id) { - case IrBinOpCmpEq: - return cmp == CmpEQ; - case IrBinOpCmpNotEq: - return cmp != CmpEQ; - case IrBinOpCmpLessThan: - return cmp == CmpLT; - case IrBinOpCmpGreaterThan: - return cmp == CmpGT; - case IrBinOpCmpLessOrEq: - return cmp != CmpGT; - case IrBinOpCmpGreaterOrEq: - return cmp != CmpLT; - default: - zig_unreachable(); - } -} - -static void set_optional_value_to_null(ZigValue *val) { - assert(val->special == ConstValSpecialStatic); - if (val->type->id == ZigTypeIdNull) return; // nothing to do - assert(val->type->id == ZigTypeIdOptional); - if (get_src_ptr_type(val->type) != nullptr) { - val->data.x_ptr.special = ConstPtrSpecialNull; - } else if (is_opt_err_set(val->type)) { - val->data.x_err_set = nullptr; - } else { - val->data.x_optional = nullptr; - } -} - -static void set_optional_payload(ZigValue *opt_val, ZigValue *payload) { - assert(opt_val->special == ConstValSpecialStatic); - assert(opt_val->type->id == ZigTypeIdOptional); - if (payload == nullptr) { - set_optional_value_to_null(opt_val); - } else if (is_opt_err_set(opt_val->type)) { - assert(payload->type->id == ZigTypeIdErrorSet); - opt_val->data.x_err_set = payload->data.x_err_set; - } else { - opt_val->data.x_optional = payload; - } -} - -static IrInstGen *ir_evaluate_bin_op_cmp(IrAnalyze *ira, ZigType *resolved_type, - ZigValue *op1_val, ZigValue *op2_val, IrInst *source_instr, IrBinOp op_id, - bool one_possible_value) -{ - if (op1_val->special == ConstValSpecialUndef || - op2_val->special == ConstValSpecialUndef) - return ir_const_undef(ira, source_instr, resolved_type); - if (resolved_type->id == ZigTypeIdPointer && op_id != IrBinOpCmpEq && op_id != IrBinOpCmpNotEq) { - if ((op1_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr || - op1_val->data.x_ptr.special == ConstPtrSpecialNull) && - (op2_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr || - op2_val->data.x_ptr.special == ConstPtrSpecialNull)) - { - uint64_t op1_addr = op1_val->data.x_ptr.special == ConstPtrSpecialNull ? - 0 : op1_val->data.x_ptr.data.hard_coded_addr.addr; - uint64_t op2_addr = op2_val->data.x_ptr.special == ConstPtrSpecialNull ? - 0 : op2_val->data.x_ptr.data.hard_coded_addr.addr; - Cmp cmp_result; - if (op1_addr > op2_addr) { - cmp_result = CmpGT; - } else if (op1_addr < op2_addr) { - cmp_result = CmpLT; - } else { - cmp_result = CmpEQ; - } - bool answer = resolve_cmp_op_id(op_id, cmp_result); - return ir_const_bool(ira, source_instr, answer); - } - } else { - bool are_equal = one_possible_value || const_values_equal(ira->codegen, op1_val, op2_val); - bool answer; - if (op_id == IrBinOpCmpEq) { - answer = are_equal; - } else if (op_id == IrBinOpCmpNotEq) { - answer = !are_equal; - } else { - zig_unreachable(); - } - return ir_const_bool(ira, source_instr, answer); - } - zig_unreachable(); -} - -static IrInstGen *ir_try_evaluate_bin_op_cmp_const(IrAnalyze *ira, IrInst *source_instr, IrInstGen *op1, IrInstGen *op2, - ZigType *resolved_type, IrBinOp op_id) -{ - assert(op1->value->type == resolved_type && op2->value->type == resolved_type); - bool one_possible_value; - switch (type_has_one_possible_value(ira->codegen, resolved_type)) { - case OnePossibleValueInvalid: - return ira->codegen->invalid_inst_gen; - case OnePossibleValueYes: - one_possible_value = true; - break; - case OnePossibleValueNo: - one_possible_value = false; - break; - } - - if (one_possible_value || (instr_is_comptime(op1) && instr_is_comptime(op2))) { - ZigValue *op1_val = one_possible_value ? op1->value : ir_resolve_const(ira, op1, UndefBad); - if (op1_val == nullptr) - return ira->codegen->invalid_inst_gen; - ZigValue *op2_val = one_possible_value ? op2->value : ir_resolve_const(ira, op2, UndefBad); - if (op2_val == nullptr) - return ira->codegen->invalid_inst_gen; - if (resolved_type->id != ZigTypeIdVector) - return ir_evaluate_bin_op_cmp(ira, resolved_type, op1_val, op2_val, source_instr, op_id, one_possible_value); - IrInstGen *result = ir_const(ira, source_instr, - get_vector_type(ira->codegen, resolved_type->data.vector.len, ira->codegen->builtin_types.entry_bool)); - result->value->data.x_array.data.s_none.elements = - ira->codegen->pass1_arena->allocate(resolved_type->data.vector.len); - - expand_undef_array(ira->codegen, result->value); - for (size_t i = 0;i < resolved_type->data.vector.len;i++) { - IrInstGen *cur_res = ir_evaluate_bin_op_cmp(ira, resolved_type->data.vector.elem_type, - &op1_val->data.x_array.data.s_none.elements[i], - &op2_val->data.x_array.data.s_none.elements[i], - source_instr, op_id, one_possible_value); - copy_const_val(ira->codegen, &result->value->data.x_array.data.s_none.elements[i], cur_res->value); - } - return result; - } else { - return nullptr; - } -} - -// Returns ErrorNotLazy when the value cannot be determined -static Error lazy_cmp_zero(CodeGen *codegen, AstNode *source_node, ZigValue *val, Cmp *result) { - Error err; - - switch (type_has_one_possible_value(codegen, val->type)) { - case OnePossibleValueInvalid: - return ErrorSemanticAnalyzeFail; - case OnePossibleValueNo: - break; - case OnePossibleValueYes: - switch (val->type->id) { - case ZigTypeIdInt: - src_assert(val->type->data.integral.bit_count == 0, source_node); - *result = CmpEQ; - return ErrorNone; - case ZigTypeIdUndefined: - return ErrorNotLazy; - default: - zig_unreachable(); - } - } - - switch (val->special) { - case ConstValSpecialRuntime: - case ConstValSpecialUndef: - return ErrorNotLazy; - case ConstValSpecialStatic: - switch (val->type->id) { - case ZigTypeIdComptimeInt: - case ZigTypeIdInt: - *result = bigint_cmp_zero(&val->data.x_bigint); - return ErrorNone; - case ZigTypeIdComptimeFloat: - case ZigTypeIdFloat: - if (float_is_nan(val)) - return ErrorNotLazy; - *result = float_cmp_zero(val); - return ErrorNone; - default: - return ErrorNotLazy; - } - case ConstValSpecialLazy: - switch (val->data.x_lazy->id) { - case LazyValueIdInvalid: - zig_unreachable(); - case LazyValueIdAlignOf: { - LazyValueAlignOf *lazy_align_of = reinterpret_cast(val->data.x_lazy); - IrAnalyze *ira = lazy_align_of->ira; - - bool is_zero_bits; - if ((err = type_val_resolve_zero_bits(ira->codegen, lazy_align_of->target_type->value, - nullptr, nullptr, &is_zero_bits))) - { - return err; - } - - *result = is_zero_bits ? CmpEQ : CmpGT; - return ErrorNone; - } - case LazyValueIdSizeOf: { - LazyValueSizeOf *lazy_size_of = reinterpret_cast(val->data.x_lazy); - IrAnalyze *ira = lazy_size_of->ira; - bool is_zero_bits; - if ((err = type_val_resolve_zero_bits(ira->codegen, lazy_size_of->target_type->value, - nullptr, nullptr, &is_zero_bits))) - { - return err; - } - *result = is_zero_bits ? CmpEQ : CmpGT; - return ErrorNone; - } - default: - return ErrorNotLazy; - } - } - zig_unreachable(); -} - -static ErrorMsg *ir_eval_bin_op_cmp_scalar(IrAnalyze *ira, IrInst* source_instr, - ZigValue *op1_val, IrBinOp op_id, ZigValue *op2_val, ZigValue *out_val) -{ - Error err; - { - // Before resolving the values, we special case comparisons against zero. These can often - // be done without resolving lazy values, preventing potential dependency loops. - Cmp op1_cmp_zero; - if ((err = lazy_cmp_zero(ira->codegen, source_instr->source_node, op1_val, &op1_cmp_zero))) { - if (err == ErrorNotLazy) goto never_mind_just_calculate_it_normally; - return ira->codegen->trace_err; - } - Cmp op2_cmp_zero; - if ((err = lazy_cmp_zero(ira->codegen, source_instr->source_node, op2_val, &op2_cmp_zero))) { - if (err == ErrorNotLazy) goto never_mind_just_calculate_it_normally; - return ira->codegen->trace_err; - } - bool can_cmp_zero = false; - Cmp cmp_result; - if (op1_cmp_zero == CmpEQ && op2_cmp_zero == CmpEQ) { - can_cmp_zero = true; - cmp_result = CmpEQ; - } else if (op1_cmp_zero == CmpGT && op2_cmp_zero == CmpEQ) { - can_cmp_zero = true; - cmp_result = CmpGT; - } else if (op1_cmp_zero == CmpEQ && op2_cmp_zero == CmpGT) { - can_cmp_zero = true; - cmp_result = CmpLT; - } else if (op1_cmp_zero == CmpLT && op2_cmp_zero == CmpEQ) { - can_cmp_zero = true; - cmp_result = CmpLT; - } else if (op1_cmp_zero == CmpEQ && op2_cmp_zero == CmpLT) { - can_cmp_zero = true; - cmp_result = CmpGT; - } else if (op1_cmp_zero == CmpLT && op2_cmp_zero == CmpGT) { - can_cmp_zero = true; - cmp_result = CmpLT; - } else if (op1_cmp_zero == CmpGT && op2_cmp_zero == CmpLT) { - can_cmp_zero = true; - cmp_result = CmpGT; - } - if (can_cmp_zero) { - bool answer = resolve_cmp_op_id(op_id, cmp_result); - out_val->special = ConstValSpecialStatic; - out_val->data.x_bool = answer; - return nullptr; - } - } -never_mind_just_calculate_it_normally: - - if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, source_instr->source_node, - op1_val, UndefOk))) - { - return ira->codegen->trace_err; - } - if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, source_instr->source_node, - op2_val, UndefOk))) - { - return ira->codegen->trace_err; - } - - - if (op1_val->special == ConstValSpecialUndef || op2_val->special == ConstValSpecialUndef || - op1_val->type->id == ZigTypeIdUndefined || op2_val->type->id == ZigTypeIdUndefined) - { - out_val->special = ConstValSpecialUndef; - return nullptr; - } - - bool op1_is_float = op1_val->type->id == ZigTypeIdFloat || op1_val->type->id == ZigTypeIdComptimeFloat; - bool op2_is_float = op2_val->type->id == ZigTypeIdFloat || op2_val->type->id == ZigTypeIdComptimeFloat; - if (op1_is_float && op2_is_float) { - if (float_is_nan(op1_val) || float_is_nan(op2_val)) { - out_val->special = ConstValSpecialStatic; - out_val->data.x_bool = op_id == IrBinOpCmpNotEq; - return nullptr; - } - if (op1_val->type->id == ZigTypeIdComptimeFloat) { - IrInstGen *tmp = ir_const_noval(ira, source_instr); - tmp->value = op1_val; - IrInstGen *casted = ir_implicit_cast(ira, tmp, op2_val->type); - op1_val = casted->value; - } else if (op2_val->type->id == ZigTypeIdComptimeFloat) { - IrInstGen *tmp = ir_const_noval(ira, source_instr); - tmp->value = op2_val; - IrInstGen *casted = ir_implicit_cast(ira, tmp, op1_val->type); - op2_val = casted->value; - } - Cmp cmp_result = float_cmp(op1_val, op2_val); - out_val->special = ConstValSpecialStatic; - out_val->data.x_bool = resolve_cmp_op_id(op_id, cmp_result); - return nullptr; - } - - bool op1_is_int = op1_val->type->id == ZigTypeIdInt || op1_val->type->id == ZigTypeIdComptimeInt; - bool op2_is_int = op2_val->type->id == ZigTypeIdInt || op2_val->type->id == ZigTypeIdComptimeInt; - - if (op1_is_int && op2_is_int) { - Cmp cmp_result = bigint_cmp(&op1_val->data.x_bigint, &op2_val->data.x_bigint); - out_val->special = ConstValSpecialStatic; - out_val->data.x_bool = resolve_cmp_op_id(op_id, cmp_result); - - return nullptr; - } - - // Handle the case where one of the two operands is a fp value and the other - // is an integer value - ZigValue *float_val; - if (op1_is_int && op2_is_float) { - float_val = op2_val; - } else if (op1_is_float && op2_is_int) { - float_val = op1_val; - } else { - zig_unreachable(); - } - - // They can never be equal if the fp value has a non-zero decimal part - if (op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq) { - if (float_has_fraction(float_val)) { - out_val->special = ConstValSpecialStatic; - out_val->data.x_bool = op_id == IrBinOpCmpNotEq; - return nullptr; - } - } - - // Cast the integer operand into a fp value to perform the comparison - BigFloat op1_bigfloat; - BigFloat op2_bigfloat; - value_to_bigfloat(&op1_bigfloat, op1_val); - value_to_bigfloat(&op2_bigfloat, op2_val); - - Cmp cmp_result = bigfloat_cmp(&op1_bigfloat, &op2_bigfloat); - out_val->special = ConstValSpecialStatic; - out_val->data.x_bool = resolve_cmp_op_id(op_id, cmp_result); - - return nullptr; -} - -static IrInstGen *ir_analyze_bin_op_cmp_numeric(IrAnalyze *ira, IrInst *source_instr, - IrInstGen *op1, IrInstGen *op2, IrBinOp op_id) -{ - Error err; - - ZigType *scalar_result_type = ira->codegen->builtin_types.entry_bool; - ZigType *result_type = scalar_result_type; - ZigType *op1_scalar_type = op1->value->type; - ZigType *op2_scalar_type = op2->value->type; - if (op1->value->type->id == ZigTypeIdVector && op2->value->type->id == ZigTypeIdVector) { - if (op1->value->type->data.vector.len != op2->value->type->data.vector.len) { - ir_add_error(ira, source_instr, - buf_sprintf("vector length mismatch: %" PRIu64 " and %" PRIu64, - op1->value->type->data.vector.len, op2->value->type->data.vector.len)); - return ira->codegen->invalid_inst_gen; - } - result_type = get_vector_type(ira->codegen, op1->value->type->data.vector.len, scalar_result_type); - op1_scalar_type = op1->value->type->data.vector.elem_type; - op2_scalar_type = op2->value->type->data.vector.elem_type; - } else if (op1->value->type->id == ZigTypeIdVector || op2->value->type->id == ZigTypeIdVector) { - ir_add_error(ira, source_instr, - buf_sprintf("mixed scalar and vector operands to comparison operator: '%s' and '%s'", - buf_ptr(&op1->value->type->name), buf_ptr(&op2->value->type->name))); - return ira->codegen->invalid_inst_gen; - } - - bool opv_op1; - switch (type_has_one_possible_value(ira->codegen, op1->value->type)) { - case OnePossibleValueInvalid: - return ira->codegen->invalid_inst_gen; - case OnePossibleValueYes: - opv_op1 = true; - break; - case OnePossibleValueNo: - opv_op1 = false; - break; - } - bool opv_op2; - switch (type_has_one_possible_value(ira->codegen, op2->value->type)) { - case OnePossibleValueInvalid: - return ira->codegen->invalid_inst_gen; - case OnePossibleValueYes: - opv_op2 = true; - break; - case OnePossibleValueNo: - opv_op2 = false; - break; - } - Cmp op1_cmp_zero; - bool have_op1_cmp_zero = false; - if ((err = lazy_cmp_zero(ira->codegen, source_instr->source_node, op1->value, &op1_cmp_zero))) { - if (err != ErrorNotLazy) return ira->codegen->invalid_inst_gen; - } else { - have_op1_cmp_zero = true; - } - Cmp op2_cmp_zero; - bool have_op2_cmp_zero = false; - if ((err = lazy_cmp_zero(ira->codegen, source_instr->source_node, op2->value, &op2_cmp_zero))) { - if (err != ErrorNotLazy) return ira->codegen->invalid_inst_gen; - } else { - have_op2_cmp_zero = true; - } - if (((opv_op1 || instr_is_comptime(op1)) && (opv_op2 || instr_is_comptime(op2))) || - (have_op1_cmp_zero && have_op2_cmp_zero)) - { - IrInstGen *result_instruction = ir_const(ira, source_instr, result_type); - ZigValue *out_val = result_instruction->value; - if (result_type->id == ZigTypeIdVector) { - size_t len = result_type->data.vector.len; - expand_undef_array(ira->codegen, op1->value); - expand_undef_array(ira->codegen, op2->value); - out_val->special = ConstValSpecialUndef; - expand_undef_array(ira->codegen, out_val); - for (size_t i = 0; i < len; i += 1) { - ZigValue *scalar_op1_val = &op1->value->data.x_array.data.s_none.elements[i]; - ZigValue *scalar_op2_val = &op2->value->data.x_array.data.s_none.elements[i]; - ZigValue *scalar_out_val = &out_val->data.x_array.data.s_none.elements[i]; - assert(scalar_out_val->type == scalar_result_type); - ErrorMsg *msg = ir_eval_bin_op_cmp_scalar(ira, source_instr, - scalar_op1_val, op_id, scalar_op2_val, scalar_out_val); - if (msg != nullptr) { - add_error_note(ira->codegen, msg, source_instr->source_node, - buf_sprintf("when computing vector element at index %" ZIG_PRI_usize, i)); - return ira->codegen->invalid_inst_gen; - } - } - out_val->type = result_type; - out_val->special = ConstValSpecialStatic; - } else { - if (ir_eval_bin_op_cmp_scalar(ira, source_instr, op1->value, op_id, - op2->value, out_val) != nullptr) - { - return ira->codegen->invalid_inst_gen; - } - } - return result_instruction; - } - - // If one operand has a comptime-known comparison with 0, and the other operand is unsigned, we might - // know the answer, depending on the operator. - // TODO make this work with vectors - if (have_op1_cmp_zero && op2_scalar_type->id == ZigTypeIdInt && !op2_scalar_type->data.integral.is_signed) { - if (op1_cmp_zero == CmpEQ) { - // 0 <= unsigned_x // true - // 0 > unsigned_x // false - switch (op_id) { - case IrBinOpCmpLessOrEq: - return ir_const_bool(ira, source_instr, true); - case IrBinOpCmpGreaterThan: - return ir_const_bool(ira, source_instr, false); - default: - break; - } - } else if (op1_cmp_zero == CmpLT) { - // -1 != unsigned_x // true - // -1 <= unsigned_x // true - // -1 < unsigned_x // true - // -1 == unsigned_x // false - // -1 >= unsigned_x // false - // -1 > unsigned_x // false - switch (op_id) { - case IrBinOpCmpNotEq: - case IrBinOpCmpLessOrEq: - case IrBinOpCmpLessThan: - return ir_const_bool(ira, source_instr, true); - case IrBinOpCmpEq: - case IrBinOpCmpGreaterOrEq: - case IrBinOpCmpGreaterThan: - return ir_const_bool(ira, source_instr, false); - default: - break; - } - } - } - if (have_op2_cmp_zero && op1_scalar_type->id == ZigTypeIdInt && !op1_scalar_type->data.integral.is_signed) { - if (op2_cmp_zero == CmpEQ) { - // unsigned_x < 0 // false - // unsigned_x >= 0 // true - switch (op_id) { - case IrBinOpCmpLessThan: - return ir_const_bool(ira, source_instr, false); - case IrBinOpCmpGreaterOrEq: - return ir_const_bool(ira, source_instr, true); - default: - break; - } - } else if (op2_cmp_zero == CmpLT) { - // unsigned_x != -1 // true - // unsigned_x >= -1 // true - // unsigned_x > -1 // true - // unsigned_x == -1 // false - // unsigned_x < -1 // false - // unsigned_x <= -1 // false - switch (op_id) { - case IrBinOpCmpNotEq: - case IrBinOpCmpGreaterOrEq: - case IrBinOpCmpGreaterThan: - return ir_const_bool(ira, source_instr, true); - case IrBinOpCmpEq: - case IrBinOpCmpLessThan: - case IrBinOpCmpLessOrEq: - return ir_const_bool(ira, source_instr, false); - default: - break; - } - } - } - - // It must be a runtime comparison. - // For floats, emit a float comparison instruction. - bool op1_is_float = op1_scalar_type->id == ZigTypeIdFloat || op1_scalar_type->id == ZigTypeIdComptimeFloat; - bool op2_is_float = op2_scalar_type->id == ZigTypeIdFloat || op2_scalar_type->id == ZigTypeIdComptimeFloat; - if (op1_is_float && op2_is_float) { - // Implicit cast the smaller one to the larger one. - ZigType *dest_scalar_type; - if (op1_scalar_type->id == ZigTypeIdComptimeFloat) { - dest_scalar_type = op2_scalar_type; - } else if (op2_scalar_type->id == ZigTypeIdComptimeFloat) { - dest_scalar_type = op1_scalar_type; - } else if (op1_scalar_type->data.floating.bit_count >= op2_scalar_type->data.floating.bit_count) { - dest_scalar_type = op1_scalar_type; - } else { - dest_scalar_type = op2_scalar_type; - } - ZigType *dest_type = (result_type->id == ZigTypeIdVector) ? - get_vector_type(ira->codegen, result_type->data.vector.len, dest_scalar_type) : dest_scalar_type; - IrInstGen *casted_op1 = ir_implicit_cast(ira, op1, dest_type); - IrInstGen *casted_op2 = ir_implicit_cast(ira, op2, dest_type); - if (type_is_invalid(casted_op1->value->type) || type_is_invalid(casted_op2->value->type)) - return ira->codegen->invalid_inst_gen; - return ir_build_bin_op_gen(ira, source_instr, result_type, op_id, casted_op1, casted_op2, true); - } - - // 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. - bool dest_int_is_signed = false; - if (have_op1_cmp_zero) { - if (op1_cmp_zero == CmpLT) dest_int_is_signed = true; - } else if (op1_is_float) { - dest_int_is_signed = true; - } else if (op1_scalar_type->id == ZigTypeIdInt && op1_scalar_type->data.integral.is_signed) { - dest_int_is_signed = true; - } - if (have_op2_cmp_zero) { - if (op2_cmp_zero == CmpLT) dest_int_is_signed = true; - } else if (op2_is_float) { - dest_int_is_signed = true; - } else if (op2->value->type->id == ZigTypeIdInt && op2->value->type->data.integral.is_signed) { - dest_int_is_signed = true; - } - ZigType *dest_float_type = nullptr; - uint32_t op1_bits; - if (instr_is_comptime(op1) && result_type->id != ZigTypeIdVector) { - ZigValue *op1_val = ir_resolve_const(ira, op1, UndefOk); - if (op1_val == nullptr) - return ira->codegen->invalid_inst_gen; - if (op1_val->special == ConstValSpecialUndef) - return ir_const_undef(ira, source_instr, ira->codegen->builtin_types.entry_bool); - bool is_unsigned; - if (op1_is_float) { - BigInt bigint = {}; - float_init_bigint(&bigint, op1_val); - Cmp zcmp = float_cmp_zero(op1_val); - if (float_has_fraction(op1_val)) { - if (op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq) { - return ir_const_bool(ira, source_instr, op_id == IrBinOpCmpNotEq); - } - if (zcmp == CmpLT) { - bigint_decr(&bigint); - } else { - bigint_incr(&bigint); - } - } - op1_bits = bigint_bits_needed(&bigint); - is_unsigned = zcmp != CmpLT; - } else { - op1_bits = bigint_bits_needed(&op1_val->data.x_bigint); - is_unsigned = bigint_cmp_zero(&op1_val->data.x_bigint) != CmpLT; - } - if (is_unsigned && dest_int_is_signed) { - op1_bits += 1; - } - } else if (op1_is_float) { - ir_assert(op1_scalar_type->id == ZigTypeIdFloat, source_instr); - dest_float_type = op1_scalar_type; - } else { - ir_assert(op1_scalar_type->id == ZigTypeIdInt, source_instr); - op1_bits = op1_scalar_type->data.integral.bit_count; - if (!op1_scalar_type->data.integral.is_signed && dest_int_is_signed) { - op1_bits += 1; - } - } - uint32_t op2_bits; - if (instr_is_comptime(op2) && result_type->id != ZigTypeIdVector) { - ZigValue *op2_val = ir_resolve_const(ira, op2, UndefOk); - if (op2_val == nullptr) - return ira->codegen->invalid_inst_gen; - if (op2_val->special == ConstValSpecialUndef) - return ir_const_undef(ira, source_instr, ira->codegen->builtin_types.entry_bool); - bool is_unsigned; - if (op2_is_float) { - BigInt bigint = {}; - float_init_bigint(&bigint, op2_val); - Cmp zcmp = float_cmp_zero(op2_val); - if (float_has_fraction(op2_val)) { - if (op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq) { - return ir_const_bool(ira, source_instr, op_id == IrBinOpCmpNotEq); - } - if (zcmp == CmpLT) { - bigint_decr(&bigint); - } else { - bigint_incr(&bigint); - } - } - op2_bits = bigint_bits_needed(&bigint); - is_unsigned = zcmp != CmpLT; - } else { - op2_bits = bigint_bits_needed(&op2_val->data.x_bigint); - is_unsigned = bigint_cmp_zero(&op2_val->data.x_bigint) != CmpLT; - } - if (is_unsigned && dest_int_is_signed) { - op2_bits += 1; - } - } else if (op2_is_float) { - ir_assert(op2_scalar_type->id == ZigTypeIdFloat, source_instr); - dest_float_type = op2_scalar_type; - } else { - ir_assert(op2_scalar_type->id == ZigTypeIdInt, source_instr); - op2_bits = op2_scalar_type->data.integral.bit_count; - if (!op2_scalar_type->data.integral.is_signed && dest_int_is_signed) { - op2_bits += 1; - } - } - ZigType *dest_scalar_type = (dest_float_type == nullptr) ? - get_int_type(ira->codegen, dest_int_is_signed, (op1_bits > op2_bits) ? op1_bits : op2_bits) : - dest_float_type; - ZigType *dest_type = (result_type->id == ZigTypeIdVector) ? - get_vector_type(ira->codegen, result_type->data.vector.len, dest_scalar_type) : dest_scalar_type; - - IrInstGen *casted_op1 = ir_implicit_cast(ira, op1, dest_type); - if (type_is_invalid(casted_op1->value->type)) - return ira->codegen->invalid_inst_gen; - IrInstGen *casted_op2 = ir_implicit_cast(ira, op2, dest_type); - if (type_is_invalid(casted_op2->value->type)) - return ira->codegen->invalid_inst_gen; - return ir_build_bin_op_gen(ira, source_instr, result_type, op_id, casted_op1, casted_op2, true); -} - -static bool type_is_self_comparable(ZigType *ty, bool is_equality_cmp) { - if (type_is_numeric(ty)) { - return true; - } - switch (ty->id) { - case ZigTypeIdInvalid: - zig_unreachable(); - - case ZigTypeIdComptimeFloat: - case ZigTypeIdComptimeInt: - case ZigTypeIdInt: - case ZigTypeIdFloat: - zig_unreachable(); // handled with the type_is_numeric check above - - case ZigTypeIdVector: - // Not every case is handled by the type_is_numeric check above, - // vectors of bool trigger this code path - case ZigTypeIdBool: - case ZigTypeIdMetaType: - case ZigTypeIdVoid: - case ZigTypeIdErrorSet: - case ZigTypeIdFn: - case ZigTypeIdOpaque: - case ZigTypeIdBoundFn: - case ZigTypeIdEnum: - case ZigTypeIdEnumLiteral: - case ZigTypeIdAnyFrame: - return is_equality_cmp; - - case ZigTypeIdPointer: - return is_equality_cmp || (ty->data.pointer.ptr_len == PtrLenC); - - case ZigTypeIdUnreachable: - case ZigTypeIdArray: - case ZigTypeIdStruct: - case ZigTypeIdUndefined: - case ZigTypeIdNull: - case ZigTypeIdErrorUnion: - case ZigTypeIdUnion: - case ZigTypeIdFnFrame: - return false; - - case ZigTypeIdOptional: - return is_equality_cmp && get_src_ptr_type(ty) != nullptr; - } - zig_unreachable(); -} - -static IrInstGen *ir_try_evaluate_cmp_optional_non_optional_const(IrAnalyze *ira, IrInst *source_instr, ZigType *child_type, - IrInstGen *optional, IrInstGen *non_optional, IrBinOp op_id) -{ - assert(optional->value->type->id == ZigTypeIdOptional); - assert(optional->value->type->data.maybe.child_type == non_optional->value->type); - assert(non_optional->value->type == child_type); - assert(op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq); - - if (instr_is_comptime(optional) && instr_is_comptime(non_optional)) { - ZigValue *optional_val = ir_resolve_const(ira, optional, UndefBad); - if (!optional_val) { - return ira->codegen->invalid_inst_gen; - } - - ZigValue *non_optional_val = ir_resolve_const(ira, non_optional, UndefBad); - if (!non_optional_val) { - return ira->codegen->invalid_inst_gen; - } - - if (!optional_value_is_null(optional_val)) { - IrInstGen *optional_unwrapped = ir_analyze_optional_value_payload_value(ira, source_instr, optional, false); - if (type_is_invalid(optional_unwrapped->value->type)) { - return ira->codegen->invalid_inst_gen; - } - - IrInstGen *ret = ir_try_evaluate_bin_op_cmp_const(ira, source_instr, optional_unwrapped, non_optional, child_type, op_id); - assert(ret != nullptr); - return ret; - } - return ir_const_bool(ira, source_instr, (op_id != IrBinOpCmpEq)); - } else { - return nullptr; - } -} - -static IrInstGen *ir_evaluate_cmp_optional_non_optional(IrAnalyze *ira, IrInst *source_instr, ZigType *child_type, - IrInstGen *optional, IrInstGen *non_optional, IrBinOp op_id) -{ - assert(optional->value->type->id == ZigTypeIdOptional); - assert(optional->value->type->data.maybe.child_type == non_optional->value->type); - assert(non_optional->value->type == child_type); - assert(op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq); - - ZigType *result_type = ira->codegen->builtin_types.entry_bool; - ir_append_basic_block_gen(&ira->new_irb, ira->new_irb.current_basic_block); - - IrBasicBlockGen *null_block = ir_create_basic_block_gen(ira, source_instr->scope, "CmpOptionalNonOptionalOptionalNull"); - IrBasicBlockGen *non_null_block = ir_create_basic_block_gen(ira, source_instr->scope, "CmpOptionalNonOptionalOptionalNotNull"); - IrBasicBlockGen *end_block = ir_create_basic_block_gen(ira, source_instr->scope, "CmpOptionalNonOptionalEnd"); - - IrInstGen *is_non_null = ir_build_test_non_null_gen(ira, source_instr, optional); - ir_build_cond_br_gen(ira, source_instr, is_non_null, non_null_block, null_block); - - ir_set_cursor_at_end_and_append_block_gen(&ira->new_irb, non_null_block); - IrInstGen *optional_unwrapped = ir_analyze_optional_value_payload_value(ira, source_instr, optional, false); - if (type_is_invalid(optional_unwrapped->value->type)) { - return ira->codegen->invalid_inst_gen; - } - IrInstGen *non_null_cmp_result = ir_build_bin_op_gen(ira, source_instr, result_type, op_id, - optional_unwrapped, non_optional, false); // safety check unnecessary for comparison operators - ir_build_br_gen(ira, source_instr, end_block); - - - ir_set_cursor_at_end_and_append_block_gen(&ira->new_irb, null_block); - IrInstGen *null_result = ir_const_bool(ira, source_instr, (op_id != IrBinOpCmpEq)); - ir_build_br_gen(ira, source_instr, end_block); - - ir_set_cursor_at_end_gen(&ira->new_irb, end_block); - int incoming_count = 2; - IrBasicBlockGen **incoming_blocks = heap::c_allocator.allocate_nonzero(incoming_count); - incoming_blocks[0] = null_block; - incoming_blocks[1] = non_null_block; - IrInstGen **incoming_values = heap::c_allocator.allocate_nonzero(incoming_count); - incoming_values[0] = null_result; - incoming_values[1] = non_null_cmp_result; - - return ir_build_phi_gen(ira, source_instr, incoming_count, incoming_blocks, incoming_values, result_type); -} - -static IrInstGen *ir_analyze_cmp_optional_non_optional(IrAnalyze *ira, IrInst *source_instr, - IrInstGen *op1, IrInstGen *op2, IrInstGen *optional, IrBinOp op_id) -{ - assert(op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq); - assert(optional->value->type->id == ZigTypeIdOptional); - assert(get_src_ptr_type(optional->value->type) == nullptr); - - IrInstGen *non_optional; - if (op1 == optional) { - non_optional = op2; - } else if (op2 == optional) { - non_optional = op1; - } else { - zig_unreachable(); - } - - ZigType *child_type = optional->value->type->data.maybe.child_type; - bool child_type_matches = (child_type == non_optional->value->type); - if (!child_type_matches || !type_is_self_comparable(child_type, true)) { - ErrorMsg *msg = ir_add_error_node(ira, source_instr->source_node, buf_sprintf("cannot compare types '%s' and '%s'", - buf_ptr(&op1->value->type->name), - buf_ptr(&op2->value->type->name))); - - if (!child_type_matches) { - if (non_optional->value->type->id == ZigTypeIdOptional) { - add_error_note(ira->codegen, msg, source_instr->source_node, buf_sprintf( - "optional to optional comparison is only supported for optional pointer types")); - } else { - add_error_note(ira->codegen, msg, source_instr->source_node, - buf_sprintf("optional child type '%s' must be the same as non-optional type '%s'", - buf_ptr(&child_type->name), - buf_ptr(&non_optional->value->type->name))); - } - } else { - add_error_note(ira->codegen, msg, source_instr->source_node, - buf_sprintf("operator not supported for type '%s'", - buf_ptr(&child_type->name))); - } - return ira->codegen->invalid_inst_gen; - } - - if (child_type->id == ZigTypeIdVector) { - ir_add_error_node(ira, source_instr->source_node, buf_sprintf("TODO add comparison of optional vector")); - return ira->codegen->invalid_inst_gen; - } - - if (IrInstGen *const_result = ir_try_evaluate_cmp_optional_non_optional_const(ira, source_instr, child_type, - optional, non_optional, op_id)) - { - return const_result; - } - - return ir_evaluate_cmp_optional_non_optional(ira, source_instr, child_type, optional, non_optional, op_id); -} - -static IrInstGen *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstSrcBinOp *bin_op_instruction) { - IrInstGen *op1 = bin_op_instruction->op1->child; - if (type_is_invalid(op1->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *op2 = bin_op_instruction->op2->child; - if (type_is_invalid(op2->value->type)) - return ira->codegen->invalid_inst_gen; - - AstNode *source_node = bin_op_instruction->base.base.source_node; - - IrBinOp op_id = bin_op_instruction->op_id; - bool is_equality_cmp = (op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq); - if (is_equality_cmp && op1->value->type->id == ZigTypeIdNull && op2->value->type->id == ZigTypeIdNull) { - return ir_const_bool(ira, &bin_op_instruction->base.base, (op_id == IrBinOpCmpEq)); - } else if (is_equality_cmp && - ((op1->value->type->id == ZigTypeIdNull && op2->value->type->id == ZigTypeIdOptional) || - (op2->value->type->id == ZigTypeIdNull && op1->value->type->id == ZigTypeIdOptional))) - { - IrInstGen *maybe_op; - if (op1->value->type->id == ZigTypeIdNull) { - maybe_op = op2; - } else if (op2->value->type->id == ZigTypeIdNull) { - maybe_op = op1; - } else { - zig_unreachable(); - } - if (instr_is_comptime(maybe_op)) { - ZigValue *maybe_val = ir_resolve_const(ira, maybe_op, UndefBad); - if (!maybe_val) - return ira->codegen->invalid_inst_gen; - bool is_null = optional_value_is_null(maybe_val); - bool bool_result = (op_id == IrBinOpCmpEq) ? is_null : !is_null; - return ir_const_bool(ira, &bin_op_instruction->base.base, bool_result); - } - - IrInstGen *is_non_null = ir_build_test_non_null_gen(ira, &bin_op_instruction->base.base, maybe_op); - - if (op_id == IrBinOpCmpEq) { - return ir_build_bool_not_gen(ira, &bin_op_instruction->base.base, is_non_null); - } else { - return is_non_null; - } - } else if (is_equality_cmp && - ((op1->value->type->id == ZigTypeIdNull && op2->value->type->id == ZigTypeIdPointer && - op2->value->type->data.pointer.ptr_len == PtrLenC) || - (op2->value->type->id == ZigTypeIdNull && op1->value->type->id == ZigTypeIdPointer && - op1->value->type->data.pointer.ptr_len == PtrLenC))) - { - IrInstGen *c_ptr_op; - if (op1->value->type->id == ZigTypeIdNull) { - c_ptr_op = op2; - } else if (op2->value->type->id == ZigTypeIdNull) { - c_ptr_op = op1; - } else { - zig_unreachable(); - } - if (instr_is_comptime(c_ptr_op)) { - ZigValue *c_ptr_val = ir_resolve_const(ira, c_ptr_op, UndefOk); - if (!c_ptr_val) - return ira->codegen->invalid_inst_gen; - if (c_ptr_val->special == ConstValSpecialUndef) - return ir_const_undef(ira, &bin_op_instruction->base.base, ira->codegen->builtin_types.entry_bool); - bool is_null = c_ptr_val->data.x_ptr.special == ConstPtrSpecialNull || - (c_ptr_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr && - c_ptr_val->data.x_ptr.data.hard_coded_addr.addr == 0); - bool bool_result = (op_id == IrBinOpCmpEq) ? is_null : !is_null; - return ir_const_bool(ira, &bin_op_instruction->base.base, bool_result); - } - IrInstGen *is_non_null = ir_build_test_non_null_gen(ira, &bin_op_instruction->base.base, c_ptr_op); - - if (op_id == IrBinOpCmpEq) { - return ir_build_bool_not_gen(ira, &bin_op_instruction->base.base, is_non_null); - } else { - return is_non_null; - } - } else if (is_equality_cmp && - (op1->value->type->id == ZigTypeIdOptional && get_src_ptr_type(op1->value->type) == nullptr)) - { - return ir_analyze_cmp_optional_non_optional(ira, &bin_op_instruction->base.base, op1, op2, op1, op_id); - } else if(is_equality_cmp && - (op2->value->type->id == ZigTypeIdOptional && get_src_ptr_type(op2->value->type) == nullptr)) - { - return ir_analyze_cmp_optional_non_optional(ira, &bin_op_instruction->base.base, op1, op2, op2, op_id); - } else if (op1->value->type->id == ZigTypeIdNull || op2->value->type->id == ZigTypeIdNull) { - ZigType *non_null_type = (op1->value->type->id == ZigTypeIdNull) ? op2->value->type : op1->value->type; - ir_add_error_node(ira, source_node, buf_sprintf("comparison of '%s' with null", - buf_ptr(&non_null_type->name))); - return ira->codegen->invalid_inst_gen; - } else if (is_equality_cmp && ( - (op1->value->type->id == ZigTypeIdEnumLiteral && op2->value->type->id == ZigTypeIdUnion) || - (op2->value->type->id == ZigTypeIdEnumLiteral && op1->value->type->id == ZigTypeIdUnion))) - { - // Support equality comparison between a union's tag value and a enum literal - IrInstGen *union_val = op1->value->type->id == ZigTypeIdUnion ? op1 : op2; - IrInstGen *enum_val = op1->value->type->id == ZigTypeIdUnion ? op2 : op1; - - if (!is_tagged_union(union_val->value->type)) { - ErrorMsg *msg = ir_add_error_node(ira, source_node, - buf_sprintf("comparison of union and enum literal is only valid for tagged union types")); - add_error_note(ira->codegen, msg, union_val->value->type->data.unionation.decl_node, - buf_sprintf("type %s is not a tagged union", - buf_ptr(&union_val->value->type->name))); - return ira->codegen->invalid_inst_gen; - } - - ZigType *tag_type = union_val->value->type->data.unionation.tag_type; - assert(tag_type != nullptr); - - IrInstGen *casted_union = ir_implicit_cast(ira, union_val, tag_type); - if (type_is_invalid(casted_union->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *casted_val = ir_implicit_cast(ira, enum_val, tag_type); - if (type_is_invalid(casted_val->value->type)) - return ira->codegen->invalid_inst_gen; - - if (instr_is_comptime(casted_union)) { - ZigValue *const_union_val = ir_resolve_const(ira, casted_union, UndefBad); - if (!const_union_val) - return ira->codegen->invalid_inst_gen; - - ZigValue *const_enum_val = ir_resolve_const(ira, casted_val, UndefBad); - if (!const_enum_val) - return ira->codegen->invalid_inst_gen; - - Cmp cmp_result = bigint_cmp(&const_union_val->data.x_union.tag, &const_enum_val->data.x_enum_tag); - bool bool_result = (op_id == IrBinOpCmpEq) ? cmp_result == CmpEQ : cmp_result != CmpEQ; - - return ir_const_bool(ira, &bin_op_instruction->base.base, bool_result); - } - - return ir_build_bin_op_gen(ira, &bin_op_instruction->base.base, ira->codegen->builtin_types.entry_bool, - op_id, casted_union, casted_val, bin_op_instruction->safety_check_on); - } - - if (op1->value->type->id == ZigTypeIdErrorSet && op2->value->type->id == ZigTypeIdErrorSet) { - if (!is_equality_cmp) { - ir_add_error_node(ira, source_node, buf_sprintf("operator not allowed for errors")); - return ira->codegen->invalid_inst_gen; - } - ZigType *intersect_type = get_error_set_intersection(ira, op1->value->type, op2->value->type, source_node); - if (type_is_invalid(intersect_type)) { - return ira->codegen->invalid_inst_gen; - } - - if (!resolve_inferred_error_set(ira->codegen, intersect_type, source_node)) { - return ira->codegen->invalid_inst_gen; - } - - // exception if one of the operators has the type of the empty error set, we allow the comparison - // (and make it comptime known) - // this is a function which is evaluated at comptime and returns an inferred error set will have an empty - // error set. - if (op1->value->type->data.error_set.err_count == 0 || op2->value->type->data.error_set.err_count == 0) { - bool are_equal = false; - bool answer; - if (op_id == IrBinOpCmpEq) { - answer = are_equal; - } else if (op_id == IrBinOpCmpNotEq) { - answer = !are_equal; - } else { - zig_unreachable(); - } - return ir_const_bool(ira, &bin_op_instruction->base.base, answer); - } - - if (!type_is_global_error_set(intersect_type)) { - if (intersect_type->data.error_set.err_count == 0) { - ir_add_error_node(ira, source_node, - buf_sprintf("error sets '%s' and '%s' have no common errors", - buf_ptr(&op1->value->type->name), buf_ptr(&op2->value->type->name))); - return ira->codegen->invalid_inst_gen; - } - if (op1->value->type->data.error_set.err_count == 1 && op2->value->type->data.error_set.err_count == 1) { - bool are_equal = true; - bool answer; - if (op_id == IrBinOpCmpEq) { - answer = are_equal; - } else if (op_id == IrBinOpCmpNotEq) { - answer = !are_equal; - } else { - zig_unreachable(); - } - return ir_const_bool(ira, &bin_op_instruction->base.base, answer); - } - } - - if (instr_is_comptime(op1) && instr_is_comptime(op2)) { - ZigValue *op1_val = ir_resolve_const(ira, op1, UndefBad); - if (op1_val == nullptr) - return ira->codegen->invalid_inst_gen; - ZigValue *op2_val = ir_resolve_const(ira, op2, UndefBad); - if (op2_val == nullptr) - return ira->codegen->invalid_inst_gen; - - bool answer; - bool are_equal = op1_val->data.x_err_set->value == op2_val->data.x_err_set->value; - if (op_id == IrBinOpCmpEq) { - answer = are_equal; - } else if (op_id == IrBinOpCmpNotEq) { - answer = !are_equal; - } else { - zig_unreachable(); - } - - return ir_const_bool(ira, &bin_op_instruction->base.base, answer); - } - - return ir_build_bin_op_gen(ira, &bin_op_instruction->base.base, ira->codegen->builtin_types.entry_bool, - op_id, op1, op2, bin_op_instruction->safety_check_on); - } - - if (type_is_numeric(op1->value->type) && type_is_numeric(op2->value->type)) { - // 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 ir_analyze_bin_op_cmp_numeric(ira, &bin_op_instruction->base.base, op1, op2, op_id); - } - - IrInstGen *instructions[] = {op1, op2}; - ZigType *resolved_type = ir_resolve_peer_types(ira, source_node, nullptr, instructions, 2); - if (type_is_invalid(resolved_type)) - return ira->codegen->invalid_inst_gen; - - bool operator_allowed = type_is_self_comparable(resolved_type, is_equality_cmp); - - if (!operator_allowed) { - ir_add_error_node(ira, source_node, - buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name))); - return ira->codegen->invalid_inst_gen; - } - - IrInstGen *casted_op1 = ir_implicit_cast(ira, op1, resolved_type); - if (type_is_invalid(casted_op1->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *casted_op2 = ir_implicit_cast(ira, op2, resolved_type); - if (type_is_invalid(casted_op2->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *resolve_const_result = ir_try_evaluate_bin_op_cmp_const(ira, &bin_op_instruction->base.base, casted_op1, - casted_op2, resolved_type, op_id); - if (resolve_const_result != nullptr) { - return resolve_const_result; - } - - ZigType *res_type = (resolved_type->id == ZigTypeIdVector) ? - get_vector_type(ira->codegen, resolved_type->data.vector.len, ira->codegen->builtin_types.entry_bool) : - ira->codegen->builtin_types.entry_bool; - return ir_build_bin_op_gen(ira, &bin_op_instruction->base.base, res_type, - op_id, casted_op1, casted_op2, bin_op_instruction->safety_check_on); -} - -static ErrorMsg *ir_eval_math_op_scalar(IrAnalyze *ira, IrInst* source_instr, ZigType *type_entry, - ZigValue *op1_val, IrBinOp op_id, ZigValue *op2_val, ZigValue *out_val) -{ - bool is_int; - bool is_float; - Cmp op2_zcmp; - if (type_entry->id == ZigTypeIdInt || type_entry->id == ZigTypeIdComptimeInt) { - is_int = true; - is_float = false; - op2_zcmp = bigint_cmp_zero(&op2_val->data.x_bigint); - } else if (type_entry->id == ZigTypeIdFloat || - type_entry->id == ZigTypeIdComptimeFloat) - { - is_int = false; - is_float = true; - op2_zcmp = float_cmp_zero(op2_val); - } else { - zig_unreachable(); - } - - if ((op_id == IrBinOpDivUnspecified || op_id == IrBinOpRemRem || op_id == IrBinOpRemMod || - op_id == IrBinOpDivTrunc || op_id == IrBinOpDivFloor) && op2_zcmp == CmpEQ) - { - return ir_add_error(ira, source_instr, buf_sprintf("division by zero")); - } - if ((op_id == IrBinOpRemRem || op_id == IrBinOpRemMod) && op2_zcmp == CmpLT) { - return ir_add_error(ira, source_instr, buf_sprintf("negative denominator")); - } - - switch (op_id) { - case IrBinOpInvalid: - case IrBinOpBoolOr: - case IrBinOpBoolAnd: - case IrBinOpCmpEq: - case IrBinOpCmpNotEq: - case IrBinOpCmpLessThan: - case IrBinOpCmpGreaterThan: - case IrBinOpCmpLessOrEq: - case IrBinOpCmpGreaterOrEq: - case IrBinOpArrayCat: - case IrBinOpArrayMult: - case IrBinOpRemUnspecified: - zig_unreachable(); - case IrBinOpBinOr: - assert(is_int); - bigint_or(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); - break; - case IrBinOpBinXor: - assert(is_int); - bigint_xor(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); - break; - case IrBinOpBinAnd: - assert(is_int); - bigint_and(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); - break; - case IrBinOpBitShiftLeftExact: - assert(is_int); - bigint_shl(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); - break; - case IrBinOpBitShiftLeftLossy: - assert(type_entry->id == ZigTypeIdInt); - bigint_shl_trunc(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint, - type_entry->data.integral.bit_count, type_entry->data.integral.is_signed); - break; - case IrBinOpBitShiftRightExact: - { - assert(is_int); - bigint_shr(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); - BigInt orig_bigint; - bigint_shl(&orig_bigint, &out_val->data.x_bigint, &op2_val->data.x_bigint); - if (bigint_cmp(&op1_val->data.x_bigint, &orig_bigint) != CmpEQ) { - return ir_add_error(ira, source_instr, buf_sprintf("exact shift shifted out 1 bits")); - } - break; - } - case IrBinOpBitShiftRightLossy: - assert(is_int); - bigint_shr(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); - break; - case IrBinOpAdd: - if (is_int) { - bigint_add(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); - } else { - float_add(out_val, op1_val, op2_val); - } - break; - case IrBinOpAddWrap: - assert(type_entry->id == ZigTypeIdInt); - bigint_add_wrap(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint, - type_entry->data.integral.bit_count, type_entry->data.integral.is_signed); - break; - case IrBinOpSub: - if (is_int) { - bigint_sub(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); - } else { - float_sub(out_val, op1_val, op2_val); - } - break; - case IrBinOpSubWrap: - assert(type_entry->id == ZigTypeIdInt); - bigint_sub_wrap(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint, - type_entry->data.integral.bit_count, type_entry->data.integral.is_signed); - break; - case IrBinOpMult: - if (is_int) { - bigint_mul(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); - } else { - float_mul(out_val, op1_val, op2_val); - } - break; - case IrBinOpMultWrap: - assert(type_entry->id == ZigTypeIdInt); - bigint_mul_wrap(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint, - type_entry->data.integral.bit_count, type_entry->data.integral.is_signed); - break; - case IrBinOpDivUnspecified: - assert(is_float); - float_div(out_val, op1_val, op2_val); - break; - case IrBinOpDivTrunc: - if (is_int) { - bigint_div_trunc(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); - } else { - float_div_trunc(out_val, op1_val, op2_val); - } - break; - case IrBinOpDivFloor: - if (is_int) { - bigint_div_floor(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); - } else { - float_div_floor(out_val, op1_val, op2_val); - } - break; - case IrBinOpDivExact: - if (is_int) { - bigint_div_trunc(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); - BigInt remainder; - bigint_rem(&remainder, &op1_val->data.x_bigint, &op2_val->data.x_bigint); - if (bigint_cmp_zero(&remainder) != CmpEQ) { - return ir_add_error(ira, source_instr, buf_sprintf("exact division had a remainder")); - } - } else { - float_div_trunc(out_val, op1_val, op2_val); - ZigValue remainder = {}; - float_rem(&remainder, op1_val, op2_val); - if (float_cmp_zero(&remainder) != CmpEQ) { - return ir_add_error(ira, source_instr, buf_sprintf("exact division had a remainder")); - } - } - break; - case IrBinOpRemRem: - if (is_int) { - bigint_rem(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); - } else { - float_rem(out_val, op1_val, op2_val); - } - break; - case IrBinOpRemMod: - if (is_int) { - bigint_mod(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); - } else { - float_mod(out_val, op1_val, op2_val); - } - break; - } - - if (type_entry->id == ZigTypeIdInt) { - if (!bigint_fits_in_bits(&out_val->data.x_bigint, type_entry->data.integral.bit_count, - type_entry->data.integral.is_signed)) - { - return ir_add_error(ira, source_instr, buf_sprintf("operation caused overflow")); - } - } - - out_val->type = type_entry; - out_val->special = ConstValSpecialStatic; - return nullptr; -} - -// This works on operands that have already been checked to be comptime known. -static IrInstGen *ir_analyze_math_op(IrAnalyze *ira, IrInst* source_instr, - ZigType *type_entry, ZigValue *op1_val, IrBinOp op_id, ZigValue *op2_val) -{ - IrInstGen *result_instruction = ir_const(ira, source_instr, type_entry); - ZigValue *out_val = result_instruction->value; - if (type_entry->id == ZigTypeIdVector) { - expand_undef_array(ira->codegen, op1_val); - expand_undef_array(ira->codegen, op2_val); - out_val->special = ConstValSpecialUndef; - expand_undef_array(ira->codegen, out_val); - size_t len = type_entry->data.vector.len; - ZigType *scalar_type = type_entry->data.vector.elem_type; - for (size_t i = 0; i < len; i += 1) { - ZigValue *scalar_op1_val = &op1_val->data.x_array.data.s_none.elements[i]; - ZigValue *scalar_op2_val = &op2_val->data.x_array.data.s_none.elements[i]; - ZigValue *scalar_out_val = &out_val->data.x_array.data.s_none.elements[i]; - assert(scalar_op1_val->type == scalar_type); - assert(scalar_out_val->type == scalar_type); - ErrorMsg *msg = ir_eval_math_op_scalar(ira, source_instr, scalar_type, - scalar_op1_val, op_id, scalar_op2_val, scalar_out_val); - if (msg != nullptr) { - add_error_note(ira->codegen, msg, source_instr->source_node, - buf_sprintf("when computing vector element at index %" ZIG_PRI_usize, i)); - return ira->codegen->invalid_inst_gen; - } - } - out_val->type = type_entry; - out_val->special = ConstValSpecialStatic; - } else { - if (ir_eval_math_op_scalar(ira, source_instr, type_entry, op1_val, op_id, op2_val, out_val) != nullptr) { - return ira->codegen->invalid_inst_gen; - } - } - return ir_implicit_cast(ira, result_instruction, type_entry); -} - -static IrInstGen *ir_analyze_bit_shift(IrAnalyze *ira, IrInstSrcBinOp *bin_op_instruction) { - IrInstGen *op1 = bin_op_instruction->op1->child; - if (type_is_invalid(op1->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *op2 = bin_op_instruction->op2->child; - if (type_is_invalid(op2->value->type)) - return ira->codegen->invalid_inst_gen; - - ZigType *op1_type = op1->value->type; - ZigType *op2_type = op2->value->type; - - if (op1_type->id == ZigTypeIdVector && op2_type->id != ZigTypeIdVector) { - ir_add_error(ira, &bin_op_instruction->op1->base, - buf_sprintf("bit shifting operation expected vector type, found '%s'", - buf_ptr(&op2_type->name))); - return ira->codegen->invalid_inst_gen; - } - - if (op1_type->id != ZigTypeIdVector && op2_type->id == ZigTypeIdVector) { - ir_add_error(ira, &bin_op_instruction->op1->base, - buf_sprintf("bit shifting operation expected vector type, found '%s'", - buf_ptr(&op1_type->name))); - return ira->codegen->invalid_inst_gen; - } - - ZigType *op1_scalar_type = (op1_type->id == ZigTypeIdVector) ? - op1_type->data.vector.elem_type : op1_type; - ZigType *op2_scalar_type = (op2_type->id == ZigTypeIdVector) ? - op2_type->data.vector.elem_type : op2_type; - - if (op1_scalar_type->id != ZigTypeIdInt && op1_scalar_type->id != ZigTypeIdComptimeInt) { - ir_add_error(ira, &bin_op_instruction->op1->base, - buf_sprintf("bit shifting operation expected integer type, found '%s'", - buf_ptr(&op1_scalar_type->name))); - return ira->codegen->invalid_inst_gen; - } - - if (op2_scalar_type->id != ZigTypeIdInt && op2_scalar_type->id != ZigTypeIdComptimeInt) { - ir_add_error(ira, &bin_op_instruction->op2->base, - buf_sprintf("shift amount has to be an integer type, but found '%s'", - buf_ptr(&op2_scalar_type->name))); - return ira->codegen->invalid_inst_gen; - } - - IrInstGen *casted_op2; - IrBinOp op_id = bin_op_instruction->op_id; - if (op1_scalar_type->id == ZigTypeIdComptimeInt) { - // comptime_int has no finite bit width - casted_op2 = op2; - - if (op_id == IrBinOpBitShiftLeftLossy) { - op_id = IrBinOpBitShiftLeftExact; - } - - if (!instr_is_comptime(op2)) { - ir_add_error(ira, &bin_op_instruction->base.base, - buf_sprintf("LHS of shift must be a fixed-width integer type, or RHS must be compile-time known")); - return ira->codegen->invalid_inst_gen; - } - - ZigValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); - if (op2_val == nullptr) - return ira->codegen->invalid_inst_gen; - - if (op2_val->data.x_bigint.is_negative) { - Buf *val_buf = buf_alloc(); - bigint_append_buf(val_buf, &op2_val->data.x_bigint, 10); - ir_add_error(ira, &casted_op2->base, - buf_sprintf("shift by negative value %s", buf_ptr(val_buf))); - return ira->codegen->invalid_inst_gen; - } - } else { - const unsigned bit_count = op1_scalar_type->data.integral.bit_count; - ZigType *shift_amt_type = get_smallest_unsigned_int_type(ira->codegen, - bit_count > 0 ? bit_count - 1 : 0); - - if (op1_type->id == ZigTypeIdVector) { - shift_amt_type = get_vector_type(ira->codegen, op1_type->data.vector.len, - shift_amt_type); - } - - casted_op2 = ir_implicit_cast(ira, op2, shift_amt_type); - if (type_is_invalid(casted_op2->value->type)) - return ira->codegen->invalid_inst_gen; - - // This check is only valid iff op1 has at least one bit - if (bit_count > 0 && instr_is_comptime(casted_op2)) { - ZigValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); - if (op2_val == nullptr) - return ira->codegen->invalid_inst_gen; - - ZigValue bit_count_value = {}; - init_const_usize(ira->codegen, &bit_count_value, bit_count); - - if (!value_cmp_numeric_val_all(op2_val, CmpLT, &bit_count_value)) { - ErrorMsg* msg = ir_add_error(ira, - &bin_op_instruction->base.base, - buf_sprintf("RHS of shift is too large for LHS type")); - add_error_note(ira->codegen, msg, op1->base.source_node, - buf_sprintf("type %s has only %u bits", - buf_ptr(&op1->value->type->name), bit_count)); - - return ira->codegen->invalid_inst_gen; - } - } - } - - // Fast path for zero RHS - if (instr_is_comptime(casted_op2)) { - ZigValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); - if (op2_val == nullptr) - return ira->codegen->invalid_inst_gen; - - if (value_cmp_numeric_val_all(op2_val, CmpEQ, nullptr)) - return ir_analyze_cast(ira, &bin_op_instruction->base.base, op1->value->type, op1); - } - - if (instr_is_comptime(op1) && instr_is_comptime(casted_op2)) { - ZigValue *op1_val = ir_resolve_const(ira, op1, UndefBad); - if (op1_val == nullptr) - return ira->codegen->invalid_inst_gen; - - ZigValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); - if (op2_val == nullptr) - return ira->codegen->invalid_inst_gen; - - return ir_analyze_math_op(ira, &bin_op_instruction->base.base, op1_type, op1_val, op_id, op2_val); - } - - return ir_build_bin_op_gen(ira, &bin_op_instruction->base.base, op1->value->type, - op_id, op1, casted_op2, bin_op_instruction->safety_check_on); -} - -static bool ok_float_op(IrBinOp op) { - switch (op) { - case IrBinOpInvalid: - zig_unreachable(); - case IrBinOpAdd: - case IrBinOpSub: - case IrBinOpMult: - case IrBinOpDivUnspecified: - case IrBinOpDivTrunc: - case IrBinOpDivFloor: - case IrBinOpDivExact: - case IrBinOpRemRem: - case IrBinOpRemMod: - case IrBinOpRemUnspecified: - return true; - - case IrBinOpBoolOr: - case IrBinOpBoolAnd: - case IrBinOpCmpEq: - case IrBinOpCmpNotEq: - case IrBinOpCmpLessThan: - case IrBinOpCmpGreaterThan: - case IrBinOpCmpLessOrEq: - case IrBinOpCmpGreaterOrEq: - case IrBinOpBinOr: - case IrBinOpBinXor: - case IrBinOpBinAnd: - case IrBinOpBitShiftLeftLossy: - case IrBinOpBitShiftLeftExact: - case IrBinOpBitShiftRightLossy: - case IrBinOpBitShiftRightExact: - case IrBinOpAddWrap: - case IrBinOpSubWrap: - case IrBinOpMultWrap: - case IrBinOpArrayCat: - case IrBinOpArrayMult: - return false; - } - zig_unreachable(); -} - -static bool is_pointer_arithmetic_allowed(ZigType *lhs_type, IrBinOp op) { - switch (op) { - case IrBinOpAdd: - case IrBinOpSub: - break; - default: - return false; - } - if (lhs_type->id != ZigTypeIdPointer) - return false; - switch (lhs_type->data.pointer.ptr_len) { - case PtrLenSingle: - return lhs_type->data.pointer.child_type->id == ZigTypeIdArray; - case PtrLenUnknown: - case PtrLenC: - return true; - } - zig_unreachable(); -} - -static bool value_cmp_numeric_val(ZigValue *left, Cmp predicate, ZigValue *right, bool any) { - assert(left->special == ConstValSpecialStatic); - assert(right == nullptr || right->special == ConstValSpecialStatic); - - switch (left->type->id) { - case ZigTypeIdComptimeInt: - case ZigTypeIdInt: { - const Cmp result = right ? - bigint_cmp(&left->data.x_bigint, &right->data.x_bigint) : - bigint_cmp_zero(&left->data.x_bigint); - return result == predicate; - } - case ZigTypeIdComptimeFloat: - case ZigTypeIdFloat: { - if (float_is_nan(left)) - return false; - if (right != nullptr && float_is_nan(right)) - return false; - - const Cmp result = right ? float_cmp(left, right) : float_cmp_zero(left); - return result == predicate; - } - case ZigTypeIdVector: { - for (size_t i = 0; i < left->type->data.vector.len; i++) { - ZigValue *scalar_val = &left->data.x_array.data.s_none.elements[i]; - const bool result = value_cmp_numeric_val(scalar_val, predicate, right, any); - - if (any && result) - return true; // This element satisfies the predicate - else if (!any && !result) - return false; // This element doesn't satisfy the predicate - } - return any ? false : true; - } - default: - zig_unreachable(); - } -} - -static bool value_cmp_numeric_val_any(ZigValue *left, Cmp predicate, ZigValue *right) { - return value_cmp_numeric_val(left, predicate, right, true); -} - -static bool value_cmp_numeric_val_all(ZigValue *left, Cmp predicate, ZigValue *right) { - return value_cmp_numeric_val(left, predicate, right, false); -} - -static IrInstGen *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstSrcBinOp *instruction) { - Error err; - - IrInstGen *op1 = instruction->op1->child; - if (type_is_invalid(op1->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *op2 = instruction->op2->child; - if (type_is_invalid(op2->value->type)) - return ira->codegen->invalid_inst_gen; - - IrBinOp op_id = instruction->op_id; - - // look for pointer math - if (is_pointer_arithmetic_allowed(op1->value->type, op_id)) { - IrInstGen *casted_op2 = ir_implicit_cast(ira, op2, ira->codegen->builtin_types.entry_usize); - if (type_is_invalid(casted_op2->value->type)) - return ira->codegen->invalid_inst_gen; - - // If either operand is undef, result is undef. - ZigValue *op1_val = nullptr; - ZigValue *op2_val = nullptr; - if (instr_is_comptime(op1)) { - op1_val = ir_resolve_const(ira, op1, UndefOk); - if (op1_val == nullptr) - return ira->codegen->invalid_inst_gen; - if (op1_val->special == ConstValSpecialUndef) - return ir_const_undef(ira, &instruction->base.base, op1->value->type); - } - if (instr_is_comptime(casted_op2)) { - op2_val = ir_resolve_const(ira, casted_op2, UndefOk); - if (op2_val == nullptr) - return ira->codegen->invalid_inst_gen; - if (op2_val->special == ConstValSpecialUndef) - return ir_const_undef(ira, &instruction->base.base, op1->value->type); - } - - ZigType *elem_type = op1->value->type->data.pointer.child_type; - if ((err = type_resolve(ira->codegen, elem_type, ResolveStatusSizeKnown))) - return ira->codegen->invalid_inst_gen; - - // NOTE: this variable is meaningful iff op2_val is not null! - uint64_t byte_offset; - if (op2_val != nullptr) { - uint64_t elem_offset; - if (!ir_resolve_usize(ira, casted_op2, &elem_offset)) - return ira->codegen->invalid_inst_gen; - - byte_offset = type_size(ira->codegen, elem_type) * elem_offset; - } - - // Fast path for cases where the RHS is zero - if (op2_val != nullptr && byte_offset == 0) { - return op1; - } - - ZigType *result_type = op1->value->type; - // Calculate the new alignment of the pointer - { - uint32_t align_bytes; - if ((err = resolve_ptr_align(ira, op1->value->type, &align_bytes))) - return ira->codegen->invalid_inst_gen; - - // If the addend is not a comptime-known value we can still count on - // it being a multiple of the type size - uint32_t addend = op2_val ? byte_offset : type_size(ira->codegen, elem_type); - - // The resulting pointer is aligned to the lcd between the - // offset (an arbitrary number) and the alignment factor (always - // a power of two, non zero) - uint32_t new_align = 1 << ctzll(addend | align_bytes); - // Rough guard to prevent overflows - assert(new_align); - result_type = adjust_ptr_align(ira->codegen, result_type, new_align); - } - - if (op2_val != nullptr && op1_val != nullptr && - (op1->value->data.x_ptr.special == ConstPtrSpecialHardCodedAddr || - op1->value->data.x_ptr.special == ConstPtrSpecialNull)) - { - uint64_t start_addr = (op1_val->data.x_ptr.special == ConstPtrSpecialNull) ? - 0 : op1_val->data.x_ptr.data.hard_coded_addr.addr; - uint64_t new_addr; - if (op_id == IrBinOpAdd) { - new_addr = start_addr + byte_offset; - } else if (op_id == IrBinOpSub) { - new_addr = start_addr - byte_offset; - } else { - zig_unreachable(); - } - IrInstGen *result = ir_const(ira, &instruction->base.base, result_type); - result->value->data.x_ptr.special = ConstPtrSpecialHardCodedAddr; - result->value->data.x_ptr.mut = ConstPtrMutRuntimeVar; - result->value->data.x_ptr.data.hard_coded_addr.addr = new_addr; - return result; - } - - return ir_build_bin_op_gen(ira, &instruction->base.base, result_type, op_id, op1, casted_op2, true); - } - - IrInstGen *instructions[] = {op1, op2}; - ZigType *resolved_type = ir_resolve_peer_types(ira, instruction->base.base.source_node, nullptr, instructions, 2); - if (type_is_invalid(resolved_type)) - return ira->codegen->invalid_inst_gen; - - ZigType *scalar_type = (resolved_type->id == ZigTypeIdVector) ? - resolved_type->data.vector.elem_type : resolved_type; - - bool is_int = scalar_type->id == ZigTypeIdInt || scalar_type->id == ZigTypeIdComptimeInt; - bool is_float = scalar_type->id == ZigTypeIdFloat || scalar_type->id == ZigTypeIdComptimeFloat; - - if (!is_int && !(is_float && ok_float_op(op_id))) { - AstNode *source_node = instruction->base.base.source_node; - ir_add_error_node(ira, source_node, - buf_sprintf("invalid operands to binary expression: '%s' and '%s'", - buf_ptr(&op1->value->type->name), - buf_ptr(&op2->value->type->name))); - return ira->codegen->invalid_inst_gen; - } - - IrInstGen *casted_op1 = ir_implicit_cast(ira, op1, resolved_type); - if (type_is_invalid(casted_op1->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *casted_op2 = ir_implicit_cast(ira, op2, resolved_type); - if (type_is_invalid(casted_op2->value->type)) - return ira->codegen->invalid_inst_gen; - - // Comptime integers have no fixed size - if (scalar_type->id == ZigTypeIdComptimeInt) { - if (op_id == IrBinOpAddWrap) { - op_id = IrBinOpAdd; - } else if (op_id == IrBinOpSubWrap) { - op_id = IrBinOpSub; - } else if (op_id == IrBinOpMultWrap) { - op_id = IrBinOpMult; - } - } - - if (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2)) { - ZigValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad); - if (op1_val == nullptr) - return ira->codegen->invalid_inst_gen; - - ZigValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); - if (op2_val == nullptr) - return ira->codegen->invalid_inst_gen; - - // Promote division with negative numbers to signed - bool is_signed_div = value_cmp_numeric_val_any(op1_val, CmpLT, nullptr) || - value_cmp_numeric_val_any(op2_val, CmpLT, nullptr); - - if (op_id == IrBinOpDivUnspecified && is_int) { - // Default to truncating division and check if it's valid for the - // given operands if signed - op_id = IrBinOpDivTrunc; - - if (is_signed_div) { - bool ok = false; - - if (value_cmp_numeric_val_any(op2_val, CmpEQ, nullptr)) { - // the division by zero error will be caught later, but we don't have a - // division function ambiguity problem. - ok = true; - } else { - IrInstGen *trunc_val = ir_analyze_math_op(ira, &instruction->base.base, resolved_type, - op1_val, IrBinOpDivTrunc, op2_val); - if (type_is_invalid(trunc_val->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *floor_val = ir_analyze_math_op(ira, &instruction->base.base, resolved_type, - op1_val, IrBinOpDivFloor, op2_val); - if (type_is_invalid(floor_val->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *cmp_val = ir_analyze_bin_op_cmp_numeric(ira, &instruction->base.base, - trunc_val, floor_val, IrBinOpCmpEq); - if (type_is_invalid(cmp_val->value->type)) - return ira->codegen->invalid_inst_gen; - - // We can "upgrade" the operator only if trunc(a/b) == floor(a/b) - if (!ir_resolve_bool(ira, cmp_val, &ok)) - return ira->codegen->invalid_inst_gen; - } - - if (!ok) { - ir_add_error(ira, &instruction->base.base, - buf_sprintf("division with '%s' and '%s': signed integers must use @divTrunc, @divFloor, or @divExact", - buf_ptr(&op1->value->type->name), - buf_ptr(&op2->value->type->name))); - return ira->codegen->invalid_inst_gen; - } - } - } else if (op_id == IrBinOpRemUnspecified) { - op_id = IrBinOpRemRem; - - if (is_signed_div) { - bool ok = false; - - if (value_cmp_numeric_val_any(op2_val, CmpEQ, nullptr)) { - // the division by zero error will be caught later, but we don't have a - // division function ambiguity problem. - ok = true; - } else { - IrInstGen *rem_val = ir_analyze_math_op(ira, &instruction->base.base, resolved_type, - op1_val, IrBinOpRemRem, op2_val); - if (type_is_invalid(rem_val->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *mod_val = ir_analyze_math_op(ira, &instruction->base.base, resolved_type, - op1_val, IrBinOpRemMod, op2_val); - if (type_is_invalid(mod_val->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *cmp_val = ir_analyze_bin_op_cmp_numeric(ira, &instruction->base.base, - rem_val, mod_val, IrBinOpCmpEq); - if (type_is_invalid(cmp_val->value->type)) - return ira->codegen->invalid_inst_gen; - - // We can "upgrade" the operator only if mod(a,b) == rem(a,b) - if (!ir_resolve_bool(ira, cmp_val, &ok)) - return ira->codegen->invalid_inst_gen; - } - - if (!ok) { - ir_add_error(ira, &instruction->base.base, - buf_sprintf("remainder division with '%s' and '%s': signed integers and floats must use @rem or @mod", - buf_ptr(&op1->value->type->name), - buf_ptr(&op2->value->type->name))); - return ira->codegen->invalid_inst_gen; - } - } - } - - return ir_analyze_math_op(ira, &instruction->base.base, resolved_type, op1_val, op_id, op2_val); - } - - const bool is_signed_div = - (scalar_type->id == ZigTypeIdInt && scalar_type->data.integral.is_signed) || - scalar_type->id == ZigTypeIdFloat; - - // Warn the user to use the proper operators here - if (op_id == IrBinOpDivUnspecified && is_int) { - op_id = IrBinOpDivTrunc; - - if (is_signed_div) { - ir_add_error(ira, &instruction->base.base, - buf_sprintf("division with '%s' and '%s': signed integers must use @divTrunc, @divFloor, or @divExact", - buf_ptr(&op1->value->type->name), - buf_ptr(&op2->value->type->name))); - return ira->codegen->invalid_inst_gen; - } - } else if (op_id == IrBinOpRemUnspecified) { - op_id = IrBinOpRemRem; - - if (is_signed_div) { - ir_add_error(ira, &instruction->base.base, - buf_sprintf("remainder division with '%s' and '%s': signed integers and floats must use @rem or @mod", - buf_ptr(&op1->value->type->name), - buf_ptr(&op2->value->type->name))); - return ira->codegen->invalid_inst_gen; - } - } - - return ir_build_bin_op_gen(ira, &instruction->base.base, resolved_type, - op_id, casted_op1, casted_op2, instruction->safety_check_on); -} - -static IrInstGen *ir_analyze_tuple_cat(IrAnalyze *ira, IrInst* source_instr, - IrInstGen *op1, IrInstGen *op2) -{ - Error err; - ZigType *op1_type = op1->value->type; - ZigType *op2_type = op2->value->type; - - uint32_t op1_field_count = op1_type->data.structure.src_field_count; - uint32_t op2_field_count = op2_type->data.structure.src_field_count; - - Buf *bare_name = buf_alloc(); - Buf *name = get_anon_type_name(ira->codegen, nullptr, container_string(ContainerKindStruct), - source_instr->scope, source_instr->source_node, bare_name); - ZigType *new_type = get_partial_container_type(ira->codegen, source_instr->scope, - ContainerKindStruct, source_instr->source_node, buf_ptr(name), bare_name, ContainerLayoutAuto); - new_type->data.structure.special = StructSpecialInferredTuple; - new_type->data.structure.resolve_status = ResolveStatusBeingInferred; - uint32_t new_field_count = op1_field_count + op2_field_count; - - new_type->data.structure.src_field_count = new_field_count; - new_type->data.structure.fields = realloc_type_struct_fields(new_type->data.structure.fields, - 0, new_field_count); - - IrInstGen *new_struct_ptr = ir_resolve_result(ira, source_instr, no_result_loc(), - new_type, nullptr, false, true); - - for (uint32_t i = 0; i < new_field_count; i += 1) { - TypeStructField *src_field; - if (i < op1_field_count) { - src_field = op1_type->data.structure.fields[i]; - } else { - src_field = op2_type->data.structure.fields[i - op1_field_count]; - } - TypeStructField *new_field = new_type->data.structure.fields[i]; - new_field->name = buf_sprintf("%" PRIu32, i); - new_field->type_entry = src_field->type_entry; - new_field->type_val = src_field->type_val; - new_field->src_index = i; - new_field->decl_node = src_field->decl_node; - new_field->init_val = src_field->init_val; - new_field->is_comptime = src_field->is_comptime; - } - if ((err = type_resolve(ira->codegen, new_type, ResolveStatusZeroBitsKnown))) - return ira->codegen->invalid_inst_gen; - - ZigList const_ptrs = {}; - for (uint32_t i = 0; i < new_field_count; i += 1) { - TypeStructField *dst_field = new_type->data.structure.fields[i]; - IrInstGen *src_struct_op; - TypeStructField *src_field; - if (i < op1_field_count) { - src_field = op1_type->data.structure.fields[i]; - src_struct_op = op1; - } else { - src_field = op2_type->data.structure.fields[i - op1_field_count]; - src_struct_op = op2; - } - IrInstGen *field_value = ir_analyze_struct_value_field_value(ira, source_instr, - src_struct_op, src_field); - if (type_is_invalid(field_value->value->type)) - return ira->codegen->invalid_inst_gen; - IrInstGen *dest_ptr = ir_analyze_struct_field_ptr(ira, source_instr, dst_field, - new_struct_ptr, new_type, true); - if (type_is_invalid(dest_ptr->value->type)) - return ira->codegen->invalid_inst_gen; - if (instr_is_comptime(field_value)) { - const_ptrs.append(dest_ptr); - } - IrInstGen *store_ptr_inst = ir_analyze_store_ptr(ira, source_instr, dest_ptr, field_value, - true); - if (type_is_invalid(store_ptr_inst->value->type)) - return ira->codegen->invalid_inst_gen; - } - if (const_ptrs.length != new_field_count) { - new_struct_ptr->value->special = ConstValSpecialRuntime; - for (size_t i = 0; i < const_ptrs.length; i += 1) { - IrInstGen *elem_result_loc = const_ptrs.at(i); - assert(elem_result_loc->value->special == ConstValSpecialStatic); - if (elem_result_loc->value->type->data.pointer.inferred_struct_field != nullptr) { - // This field will be generated comptime; no need to do this. - continue; - } - IrInstGen *deref = ir_get_deref(ira, &elem_result_loc->base, elem_result_loc, nullptr); - if (!type_requires_comptime(ira->codegen, elem_result_loc->value->type->data.pointer.child_type)) { - elem_result_loc->value->special = ConstValSpecialRuntime; - } - ir_analyze_store_ptr(ira, &elem_result_loc->base, elem_result_loc, deref, true); - } - } - - const_ptrs.deinit(); - - return ir_get_deref(ira, source_instr, new_struct_ptr, nullptr); -} - -static IrInstGen *ir_analyze_array_cat(IrAnalyze *ira, IrInstSrcBinOp *instruction) { - IrInstGen *op1 = instruction->op1->child; - ZigType *op1_type = op1->value->type; - if (type_is_invalid(op1_type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *op2 = instruction->op2->child; - ZigType *op2_type = op2->value->type; - if (type_is_invalid(op2_type)) - return ira->codegen->invalid_inst_gen; - - if (is_tuple(op1_type) && is_tuple(op2_type)) { - return ir_analyze_tuple_cat(ira, &instruction->base.base, op1, op2); - } - - ZigValue *op1_val = ir_resolve_const(ira, op1, UndefBad); - if (!op1_val) - return ira->codegen->invalid_inst_gen; - - ZigValue *op2_val = ir_resolve_const(ira, op2, UndefBad); - if (!op2_val) - return ira->codegen->invalid_inst_gen; - - ZigValue *sentinel1 = nullptr; - ZigValue *op1_array_val; - size_t op1_array_index; - size_t op1_array_end; - ZigType *child_type; - if (op1_type->id == ZigTypeIdArray) { - child_type = op1_type->data.array.child_type; - op1_array_val = op1_val; - op1_array_index = 0; - op1_array_end = op1_type->data.array.len; - sentinel1 = op1_type->data.array.sentinel; - } else if (op1_type->id == ZigTypeIdPointer && - op1_type->data.pointer.child_type == ira->codegen->builtin_types.entry_u8 && - op1_type->data.pointer.sentinel != nullptr && - op1_val->data.x_ptr.special == ConstPtrSpecialBaseArray) - { - child_type = op1_type->data.pointer.child_type; - op1_array_val = op1_val->data.x_ptr.data.base_array.array_val; - op1_array_index = op1_val->data.x_ptr.data.base_array.elem_index; - op1_array_end = op1_array_val->type->data.array.len; - sentinel1 = op1_type->data.pointer.sentinel; - } else if (is_slice(op1_type)) { - ZigType *ptr_type = op1_type->data.structure.fields[slice_ptr_index]->type_entry; - child_type = ptr_type->data.pointer.child_type; - ZigValue *ptr_val = op1_val->data.x_struct.fields[slice_ptr_index]; - assert(ptr_val->data.x_ptr.special == ConstPtrSpecialBaseArray); - op1_array_val = ptr_val->data.x_ptr.data.base_array.array_val; - op1_array_index = ptr_val->data.x_ptr.data.base_array.elem_index; - ZigValue *len_val = op1_val->data.x_struct.fields[slice_len_index]; - op1_array_end = op1_array_index + bigint_as_usize(&len_val->data.x_bigint); - sentinel1 = ptr_type->data.pointer.sentinel; - } else if (op1_type->id == ZigTypeIdPointer && - op1_type->data.pointer.ptr_len == PtrLenSingle && - op1_type->data.pointer.child_type->id == ZigTypeIdArray) - { - ZigType *array_type = op1_type->data.pointer.child_type; - child_type = array_type->data.array.child_type; - op1_array_val = const_ptr_pointee(ira, ira->codegen, op1_val, op1->base.source_node); - if (op1_array_val == nullptr) - return ira->codegen->invalid_inst_gen; - op1_array_index = 0; - op1_array_end = array_type->data.array.len; - sentinel1 = array_type->data.array.sentinel; - } else { - ir_add_error(ira, &op1->base, buf_sprintf("expected array, found '%s'", buf_ptr(&op1->value->type->name))); - return ira->codegen->invalid_inst_gen; - } - - ZigValue *sentinel2 = nullptr; - ZigValue *op2_array_val; - size_t op2_array_index; - size_t op2_array_end; - bool op2_type_valid; - if (op2_type->id == ZigTypeIdArray) { - op2_type_valid = op2_type->data.array.child_type == child_type; - op2_array_val = op2_val; - op2_array_index = 0; - op2_array_end = op2_array_val->type->data.array.len; - sentinel2 = op2_type->data.array.sentinel; - } else if (op2_type->id == ZigTypeIdPointer && - op2_type->data.pointer.sentinel != nullptr && - op2_val->data.x_ptr.special == ConstPtrSpecialBaseArray) - { - op2_type_valid = op2_type->data.pointer.child_type == child_type; - op2_array_val = op2_val->data.x_ptr.data.base_array.array_val; - op2_array_index = op2_val->data.x_ptr.data.base_array.elem_index; - op2_array_end = op2_array_val->type->data.array.len; - - sentinel2 = op2_type->data.pointer.sentinel; - } else if (is_slice(op2_type)) { - ZigType *ptr_type = op2_type->data.structure.fields[slice_ptr_index]->type_entry; - op2_type_valid = ptr_type->data.pointer.child_type == child_type; - ZigValue *ptr_val = op2_val->data.x_struct.fields[slice_ptr_index]; - assert(ptr_val->data.x_ptr.special == ConstPtrSpecialBaseArray); - op2_array_val = ptr_val->data.x_ptr.data.base_array.array_val; - op2_array_index = ptr_val->data.x_ptr.data.base_array.elem_index; - ZigValue *len_val = op2_val->data.x_struct.fields[slice_len_index]; - op2_array_end = op2_array_index + bigint_as_usize(&len_val->data.x_bigint); - - sentinel2 = ptr_type->data.pointer.sentinel; - } else if (op2_type->id == ZigTypeIdPointer && op2_type->data.pointer.ptr_len == PtrLenSingle && - op2_type->data.pointer.child_type->id == ZigTypeIdArray) - { - ZigType *array_type = op2_type->data.pointer.child_type; - op2_type_valid = array_type->data.array.child_type == child_type; - op2_array_val = const_ptr_pointee(ira, ira->codegen, op2_val, op2->base.source_node); - if (op2_array_val == nullptr) - return ira->codegen->invalid_inst_gen; - op2_array_index = 0; - op2_array_end = array_type->data.array.len; - - sentinel2 = array_type->data.array.sentinel; - } else { - ir_add_error(ira, &op2->base, - buf_sprintf("expected array or C string literal, found '%s'", buf_ptr(&op2->value->type->name))); - return ira->codegen->invalid_inst_gen; - } - if (!op2_type_valid) { - ir_add_error(ira, &op2->base, buf_sprintf("expected array of type '%s', found '%s'", - buf_ptr(&child_type->name), - buf_ptr(&op2->value->type->name))); - return ira->codegen->invalid_inst_gen; - } - - ZigValue *sentinel; - if (sentinel1 != nullptr && sentinel2 != nullptr) { - // When there is a sentinel mismatch, no sentinel on the result. The type system - // will catch this if it is a problem. - sentinel = const_values_equal(ira->codegen, sentinel1, sentinel2) ? sentinel1 : nullptr; - } else if (sentinel1 != nullptr) { - sentinel = sentinel1; - } else if (sentinel2 != nullptr) { - sentinel = sentinel2; - } else { - sentinel = nullptr; - } - - // The type of result is populated in the following if blocks - IrInstGen *result = ir_const(ira, &instruction->base.base, nullptr); - ZigValue *out_val = result->value; - - ZigValue *out_array_val; - size_t new_len = (op1_array_end - op1_array_index) + (op2_array_end - op2_array_index); - if (op1_type->id == ZigTypeIdPointer || op2_type->id == ZigTypeIdPointer) { - out_array_val = ira->codegen->pass1_arena->create(); - out_array_val->special = ConstValSpecialStatic; - out_array_val->type = get_array_type(ira->codegen, child_type, new_len, sentinel); - - out_val->data.x_ptr.special = ConstPtrSpecialRef; - out_val->data.x_ptr.data.ref.pointee = out_array_val; - out_val->type = get_pointer_to_type(ira->codegen, out_array_val->type, true); - } else if (is_slice(op1_type) || is_slice(op2_type)) { - ZigType *ptr_type = get_pointer_to_type_extra2(ira->codegen, child_type, - true, false, PtrLenUnknown, 0, 0, 0, false, - VECTOR_INDEX_NONE, nullptr, sentinel); - result->value->type = get_slice_type(ira->codegen, ptr_type); - out_array_val = ira->codegen->pass1_arena->create(); - out_array_val->special = ConstValSpecialStatic; - out_array_val->type = get_array_type(ira->codegen, child_type, new_len, sentinel); - - out_val->data.x_struct.fields = alloc_const_vals_ptrs(ira->codegen, 2); - - out_val->data.x_struct.fields[slice_ptr_index]->type = ptr_type; - out_val->data.x_struct.fields[slice_ptr_index]->special = ConstValSpecialStatic; - out_val->data.x_struct.fields[slice_ptr_index]->data.x_ptr.special = ConstPtrSpecialBaseArray; - out_val->data.x_struct.fields[slice_ptr_index]->data.x_ptr.data.base_array.array_val = out_array_val; - out_val->data.x_struct.fields[slice_ptr_index]->data.x_ptr.data.base_array.elem_index = 0; - - out_val->data.x_struct.fields[slice_len_index]->type = ira->codegen->builtin_types.entry_usize; - out_val->data.x_struct.fields[slice_len_index]->special = ConstValSpecialStatic; - bigint_init_unsigned(&out_val->data.x_struct.fields[slice_len_index]->data.x_bigint, new_len); - } else if (op1_type->id == ZigTypeIdArray || op2_type->id == ZigTypeIdArray) { - result->value->type = get_array_type(ira->codegen, child_type, new_len, sentinel); - out_array_val = out_val; - } else { - result->value->type = get_pointer_to_type_extra2(ira->codegen, child_type, true, false, PtrLenUnknown, - 0, 0, 0, false, VECTOR_INDEX_NONE, nullptr, sentinel); - out_array_val = ira->codegen->pass1_arena->create(); - out_array_val->special = ConstValSpecialStatic; - out_array_val->type = get_array_type(ira->codegen, child_type, new_len, sentinel); - out_val->data.x_ptr.special = ConstPtrSpecialBaseArray; - out_val->data.x_ptr.data.base_array.array_val = out_array_val; - out_val->data.x_ptr.data.base_array.elem_index = 0; - } - - if (op1_array_val->data.x_array.special == ConstArraySpecialUndef && - op2_array_val->data.x_array.special == ConstArraySpecialUndef) - { - out_array_val->data.x_array.special = ConstArraySpecialUndef; - return result; - } - - uint64_t full_len = new_len + ((sentinel != nullptr) ? 1 : 0); - out_array_val->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(full_len); - // TODO handle the buf case here for an optimization - expand_undef_array(ira->codegen, op1_array_val); - expand_undef_array(ira->codegen, op2_array_val); - - size_t next_index = 0; - for (size_t i = op1_array_index; i < op1_array_end; i += 1, next_index += 1) { - ZigValue *elem_dest_val = &out_array_val->data.x_array.data.s_none.elements[next_index]; - copy_const_val(ira->codegen, elem_dest_val, &op1_array_val->data.x_array.data.s_none.elements[i]); - elem_dest_val->parent.id = ConstParentIdArray; - elem_dest_val->parent.data.p_array.array_val = out_array_val; - elem_dest_val->parent.data.p_array.elem_index = next_index; - } - for (size_t i = op2_array_index; i < op2_array_end; i += 1, next_index += 1) { - ZigValue *elem_dest_val = &out_array_val->data.x_array.data.s_none.elements[next_index]; - copy_const_val(ira->codegen, elem_dest_val, &op2_array_val->data.x_array.data.s_none.elements[i]); - elem_dest_val->parent.id = ConstParentIdArray; - elem_dest_val->parent.data.p_array.array_val = out_array_val; - elem_dest_val->parent.data.p_array.elem_index = next_index; - } - if (next_index < full_len) { - ZigValue *elem_dest_val = &out_array_val->data.x_array.data.s_none.elements[next_index]; - copy_const_val(ira->codegen, elem_dest_val, sentinel); - elem_dest_val->parent.id = ConstParentIdArray; - elem_dest_val->parent.data.p_array.array_val = out_array_val; - elem_dest_val->parent.data.p_array.elem_index = next_index; - next_index += 1; - } - assert(next_index == full_len); - - return result; -} - -static IrInstGen *ir_analyze_tuple_mult(IrAnalyze *ira, IrInst* source_instr, - IrInstGen *op1, IrInstGen *op2) -{ - Error err; - ZigType *op1_type = op1->value->type; - uint64_t op1_field_count = op1_type->data.structure.src_field_count; - - uint64_t mult_amt; - if (!ir_resolve_usize(ira, op2, &mult_amt)) - return ira->codegen->invalid_inst_gen; - - uint64_t new_field_count; - if (mul_u64_overflow(op1_field_count, mult_amt, &new_field_count)) { - ir_add_error(ira, source_instr, buf_sprintf("operation results in overflow")); - return ira->codegen->invalid_inst_gen; - } - - Buf *bare_name = buf_alloc(); - Buf *name = get_anon_type_name(ira->codegen, nullptr, container_string(ContainerKindStruct), - source_instr->scope, source_instr->source_node, bare_name); - ZigType *new_type = get_partial_container_type(ira->codegen, source_instr->scope, - ContainerKindStruct, source_instr->source_node, buf_ptr(name), bare_name, ContainerLayoutAuto); - new_type->data.structure.special = StructSpecialInferredTuple; - new_type->data.structure.resolve_status = ResolveStatusBeingInferred; - new_type->data.structure.src_field_count = new_field_count; - new_type->data.structure.fields = realloc_type_struct_fields( - new_type->data.structure.fields, 0, new_field_count); - - IrInstGen *new_struct_ptr = ir_resolve_result(ira, source_instr, no_result_loc(), - new_type, nullptr, false, true); - - for (uint64_t i = 0; i < new_field_count; i += 1) { - TypeStructField *src_field = op1_type->data.structure.fields[i % op1_field_count]; - TypeStructField *new_field = new_type->data.structure.fields[i]; - - new_field->name = buf_sprintf("%" ZIG_PRI_u64, i); - new_field->type_entry = src_field->type_entry; - new_field->type_val = src_field->type_val; - new_field->src_index = i; - new_field->decl_node = src_field->decl_node; - new_field->init_val = src_field->init_val; - new_field->is_comptime = src_field->is_comptime; - } - - if ((err = type_resolve(ira->codegen, new_type, ResolveStatusZeroBitsKnown))) - return ira->codegen->invalid_inst_gen; - - ZigList const_ptrs = {}; - for (uint64_t i = 0; i < new_field_count; i += 1) { - TypeStructField *src_field = op1_type->data.structure.fields[i % op1_field_count]; - TypeStructField *dst_field = new_type->data.structure.fields[i]; - - IrInstGen *field_value = ir_analyze_struct_value_field_value( - ira, source_instr, op1, src_field); - if (type_is_invalid(field_value->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *dest_ptr = ir_analyze_struct_field_ptr( - ira, source_instr, dst_field, new_struct_ptr, new_type, true); - if (type_is_invalid(dest_ptr->value->type)) - return ira->codegen->invalid_inst_gen; - - if (instr_is_comptime(field_value)) { - const_ptrs.append(dest_ptr); - } - - IrInstGen *store_ptr_inst = ir_analyze_store_ptr( - ira, source_instr, dest_ptr, field_value, true); - if (type_is_invalid(store_ptr_inst->value->type)) - return ira->codegen->invalid_inst_gen; - } - - if (const_ptrs.length != new_field_count) { - new_struct_ptr->value->special = ConstValSpecialRuntime; - for (size_t i = 0; i < const_ptrs.length; i += 1) { - IrInstGen *elem_result_loc = const_ptrs.at(i); - assert(elem_result_loc->value->special == ConstValSpecialStatic); - if (elem_result_loc->value->type->data.pointer.inferred_struct_field != nullptr) { - // This field will be generated comptime; no need to do this. - continue; - } - IrInstGen *deref = ir_get_deref(ira, &elem_result_loc->base, elem_result_loc, nullptr); - if (!type_requires_comptime(ira->codegen, elem_result_loc->value->type->data.pointer.child_type)) { - elem_result_loc->value->special = ConstValSpecialRuntime; - } - IrInstGen *store_ptr_inst = ir_analyze_store_ptr( - ira, &elem_result_loc->base, elem_result_loc, deref, true); - if (type_is_invalid(store_ptr_inst->value->type)) - return ira->codegen->invalid_inst_gen; - } - } - - const_ptrs.deinit(); - - return ir_get_deref(ira, source_instr, new_struct_ptr, nullptr); -} - -static IrInstGen *ir_analyze_array_mult(IrAnalyze *ira, IrInstSrcBinOp *instruction) { - IrInstGen *op1 = instruction->op1->child; - if (type_is_invalid(op1->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *op2 = instruction->op2->child; - if (type_is_invalid(op2->value->type)) - return ira->codegen->invalid_inst_gen; - - bool want_ptr_to_array = false; - ZigType *array_type; - ZigValue *array_val; - if (op1->value->type->id == ZigTypeIdArray) { - array_type = op1->value->type; - array_val = ir_resolve_const(ira, op1, UndefOk); - if (array_val == nullptr) - return ira->codegen->invalid_inst_gen; - } else if (op1->value->type->id == ZigTypeIdPointer && - op1->value->type->data.pointer.ptr_len == PtrLenSingle && - op1->value->type->data.pointer.child_type->id == ZigTypeIdArray) - { - array_type = op1->value->type->data.pointer.child_type; - IrInstGen *array_inst = ir_get_deref(ira, &op1->base, op1, nullptr); - if (type_is_invalid(array_inst->value->type)) - return ira->codegen->invalid_inst_gen; - array_val = ir_resolve_const(ira, array_inst, UndefOk); - if (array_val == nullptr) - return ira->codegen->invalid_inst_gen; - want_ptr_to_array = true; - } else if (is_tuple(op1->value->type)) { - return ir_analyze_tuple_mult(ira, &instruction->base.base, op1, op2); - } else { - ir_add_error(ira, &op1->base, buf_sprintf("expected array type, found '%s'", buf_ptr(&op1->value->type->name))); - return ira->codegen->invalid_inst_gen; - } - - uint64_t mult_amt; - if (!ir_resolve_usize(ira, op2, &mult_amt)) - return ira->codegen->invalid_inst_gen; - - uint64_t old_array_len = array_type->data.array.len; - uint64_t new_array_len; - - if (mul_u64_overflow(old_array_len, mult_amt, &new_array_len)) { - ir_add_error(ira, &instruction->base.base, buf_sprintf("operation results in overflow")); - return ira->codegen->invalid_inst_gen; - } - - ZigType *child_type = array_type->data.array.child_type; - ZigType *result_array_type = get_array_type(ira->codegen, child_type, new_array_len, - array_type->data.array.sentinel); - - IrInstGen *array_result; - if (array_val->special == ConstValSpecialUndef || array_val->data.x_array.special == ConstArraySpecialUndef) { - array_result = ir_const_undef(ira, &instruction->base.base, result_array_type); - } else { - array_result = ir_const(ira, &instruction->base.base, result_array_type); - ZigValue *out_val = array_result->value; - - switch (type_has_one_possible_value(ira->codegen, result_array_type)) { - case OnePossibleValueInvalid: - return ira->codegen->invalid_inst_gen; - case OnePossibleValueYes: - goto skip_computation; - case OnePossibleValueNo: - break; - } - - // TODO optimize the buf case - expand_undef_array(ira->codegen, array_val); - size_t extra_null_term = (array_type->data.array.sentinel != nullptr) ? 1 : 0; - out_val->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(new_array_len + extra_null_term); - - uint64_t i = 0; - for (uint64_t x = 0; x < mult_amt; x += 1) { - for (uint64_t y = 0; y < old_array_len; y += 1) { - ZigValue *elem_dest_val = &out_val->data.x_array.data.s_none.elements[i]; - copy_const_val(ira->codegen, elem_dest_val, &array_val->data.x_array.data.s_none.elements[y]); - elem_dest_val->parent.id = ConstParentIdArray; - elem_dest_val->parent.data.p_array.array_val = out_val; - elem_dest_val->parent.data.p_array.elem_index = i; - i += 1; - } - } - assert(i == new_array_len); - - if (array_type->data.array.sentinel != nullptr) { - ZigValue *elem_dest_val = &out_val->data.x_array.data.s_none.elements[i]; - copy_const_val(ira->codegen, elem_dest_val, array_type->data.array.sentinel); - elem_dest_val->parent.id = ConstParentIdArray; - elem_dest_val->parent.data.p_array.array_val = out_val; - elem_dest_val->parent.data.p_array.elem_index = i; - i += 1; - } - } -skip_computation: - if (want_ptr_to_array) { - return ir_get_ref(ira, &instruction->base.base, array_result, true, false); - } else { - return array_result; - } -} - -static IrInstGen *ir_analyze_instruction_merge_err_sets(IrAnalyze *ira, - IrInstSrcMergeErrSets *instruction) -{ - ZigType *op1_type = ir_resolve_error_set_type(ira, &instruction->base.base, instruction->op1->child); - if (type_is_invalid(op1_type)) - return ira->codegen->invalid_inst_gen; - - ZigType *op2_type = ir_resolve_error_set_type(ira, &instruction->base.base, instruction->op2->child); - if (type_is_invalid(op2_type)) - return ira->codegen->invalid_inst_gen; - - if (!resolve_inferred_error_set(ira->codegen, op1_type, instruction->op1->child->base.source_node)) { - return ira->codegen->invalid_inst_gen; - } - - if (!resolve_inferred_error_set(ira->codegen, op2_type, instruction->op2->child->base.source_node)) { - return ira->codegen->invalid_inst_gen; - } - - if (type_is_global_error_set(op1_type) || - type_is_global_error_set(op2_type)) - { - return ir_const_type(ira, &instruction->base.base, ira->codegen->builtin_types.entry_global_error_set); - } - - size_t errors_count = ira->codegen->errors_by_index.length; - ErrorTableEntry **errors = heap::c_allocator.allocate(errors_count); - for (uint32_t i = 0, count = op1_type->data.error_set.err_count; i < count; i += 1) { - ErrorTableEntry *error_entry = op1_type->data.error_set.errors[i]; - assert(errors[error_entry->value] == nullptr); - errors[error_entry->value] = error_entry; - } - ZigType *result_type = get_error_set_union(ira->codegen, errors, op1_type, op2_type, instruction->type_name); - heap::c_allocator.deallocate(errors, errors_count); - - return ir_const_type(ira, &instruction->base.base, result_type); -} - - -static IrInstGen *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstSrcBinOp *bin_op_instruction) { - IrBinOp op_id = bin_op_instruction->op_id; - switch (op_id) { - case IrBinOpInvalid: - zig_unreachable(); - case IrBinOpBoolOr: - case IrBinOpBoolAnd: - return ir_analyze_bin_op_bool(ira, bin_op_instruction); - case IrBinOpCmpEq: - case IrBinOpCmpNotEq: - case IrBinOpCmpLessThan: - case IrBinOpCmpGreaterThan: - case IrBinOpCmpLessOrEq: - case IrBinOpCmpGreaterOrEq: - return ir_analyze_bin_op_cmp(ira, bin_op_instruction); - case IrBinOpBitShiftLeftLossy: - case IrBinOpBitShiftLeftExact: - case IrBinOpBitShiftRightLossy: - case IrBinOpBitShiftRightExact: - return ir_analyze_bit_shift(ira, bin_op_instruction); - case IrBinOpBinOr: - case IrBinOpBinXor: - case IrBinOpBinAnd: - case IrBinOpAdd: - case IrBinOpAddWrap: - case IrBinOpSub: - case IrBinOpSubWrap: - case IrBinOpMult: - case IrBinOpMultWrap: - case IrBinOpDivUnspecified: - case IrBinOpDivTrunc: - case IrBinOpDivFloor: - case IrBinOpDivExact: - case IrBinOpRemUnspecified: - case IrBinOpRemRem: - case IrBinOpRemMod: - return ir_analyze_bin_op_math(ira, bin_op_instruction); - case IrBinOpArrayCat: - return ir_analyze_array_cat(ira, bin_op_instruction); - case IrBinOpArrayMult: - return ir_analyze_array_mult(ira, bin_op_instruction); - } - zig_unreachable(); -} - -static IrInstGen *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstSrcDeclVar *decl_var_instruction) { - Error err; - ZigVar *var = decl_var_instruction->var; - - ZigType *explicit_type = nullptr; - IrInstGen *var_type = nullptr; - if (decl_var_instruction->var_type != nullptr) { - var_type = decl_var_instruction->var_type->child; - ZigType *proposed_type = ir_resolve_type(ira, var_type); - explicit_type = validate_var_type(ira->codegen, &var->decl_node->data.variable_declaration, proposed_type); - if (type_is_invalid(explicit_type)) { - var->var_type = ira->codegen->builtin_types.entry_invalid; - return ira->codegen->invalid_inst_gen; - } - } - - AstNode *source_node = decl_var_instruction->base.base.source_node; - - bool is_comptime_var = ir_get_var_is_comptime(var); - - bool var_class_requires_const = false; - - IrInstGen *var_ptr = decl_var_instruction->ptr->child; - // if this is null, a compiler error happened and did not initialize the variable. - // if there are no compile errors there may be a missing ir_expr_wrap in pass1 IR generation. - if (var_ptr == nullptr || type_is_invalid(var_ptr->value->type)) { - ir_assert(var_ptr != nullptr || ira->codegen->errors.length != 0, &decl_var_instruction->base.base); - var->var_type = ira->codegen->builtin_types.entry_invalid; - return ira->codegen->invalid_inst_gen; - } - - // The ir_build_var_decl_src call is supposed to pass a pointer to the allocation, not an initialization value. - ir_assert(var_ptr->value->type->id == ZigTypeIdPointer, &decl_var_instruction->base.base); - - ZigType *result_type = var_ptr->value->type->data.pointer.child_type; - if (type_is_invalid(result_type)) { - result_type = ira->codegen->builtin_types.entry_invalid; - } else if (result_type->id == ZigTypeIdUnreachable || result_type->id == ZigTypeIdOpaque) { - zig_unreachable(); - } - - ZigValue *init_val = nullptr; - if (instr_is_comptime(var_ptr) && var_ptr->value->data.x_ptr.mut != ConstPtrMutRuntimeVar) { - ZigValue *ptr_val = ir_resolve_const(ira, var_ptr, UndefBad); - if (ptr_val == nullptr) - return ira->codegen->invalid_inst_gen; - - init_val = const_ptr_pointee(ira, ira->codegen, ptr_val, decl_var_instruction->base.base.source_node); - if (init_val == nullptr) - return ira->codegen->invalid_inst_gen; - - if (is_comptime_var) { - if (var->gen_is_const) { - var->const_value = init_val; - } else { - var->const_value = ira->codegen->pass1_arena->create(); - copy_const_val(ira->codegen, var->const_value, init_val); - } - } - } - - switch (type_requires_comptime(ira->codegen, result_type)) { - case ReqCompTimeInvalid: - result_type = ira->codegen->builtin_types.entry_invalid; - break; - case ReqCompTimeYes: - var_class_requires_const = true; - if (!var->gen_is_const && !is_comptime_var) { - ir_add_error_node(ira, source_node, - buf_sprintf("variable of type '%s' must be const or comptime", - buf_ptr(&result_type->name))); - result_type = ira->codegen->builtin_types.entry_invalid; - } - break; - case ReqCompTimeNo: - if (init_val != nullptr && value_is_comptime(init_val)) { - if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, - decl_var_instruction->base.base.source_node, init_val, UndefOk))) - { - result_type = ira->codegen->builtin_types.entry_invalid; - } else if (init_val->type->id == ZigTypeIdFn && - init_val->special != ConstValSpecialUndef && - init_val->data.x_ptr.special == ConstPtrSpecialFunction && - init_val->data.x_ptr.data.fn.fn_entry->fn_inline == FnInlineAlways) - { - var_class_requires_const = true; - if (!var->src_is_const && !is_comptime_var) { - ErrorMsg *msg = ir_add_error_node(ira, source_node, - buf_sprintf("functions marked inline must be stored in const or comptime var")); - AstNode *proto_node = init_val->data.x_ptr.data.fn.fn_entry->proto_node; - add_error_note(ira->codegen, msg, proto_node, buf_sprintf("declared here")); - result_type = ira->codegen->builtin_types.entry_invalid; - } - } - } - break; - } - - while (var->next_var != nullptr) { - var = var->next_var; - } - - // This must be done after possibly creating a new variable above - var->ref_count = 0; - - var->ptr_instruction = var_ptr; - var->var_type = result_type; - assert(var->var_type); - - if (type_is_invalid(result_type)) { - return ir_const_void(ira, &decl_var_instruction->base.base); - } - - if (decl_var_instruction->align_value == nullptr) { - if ((err = type_resolve(ira->codegen, result_type, ResolveStatusAlignmentKnown))) { - var->var_type = ira->codegen->builtin_types.entry_invalid; - return ir_const_void(ira, &decl_var_instruction->base.base); - } - var->align_bytes = get_ptr_align(ira->codegen, var_ptr->value->type); - } else { - if (!ir_resolve_align(ira, decl_var_instruction->align_value->child, nullptr, &var->align_bytes)) { - var->var_type = ira->codegen->builtin_types.entry_invalid; - } - } - - if (init_val != nullptr && value_is_comptime(init_val)) { - // Resolve ConstPtrMutInfer - if (var->gen_is_const) { - var_ptr->value->data.x_ptr.mut = ConstPtrMutComptimeConst; - } else if (is_comptime_var) { - var_ptr->value->data.x_ptr.mut = ConstPtrMutComptimeVar; - } else { - // we need a runtime ptr but we have a comptime val. - // since it's a comptime val there are no instructions for it. - // we memcpy the init value here - IrInstGen *deref = ir_get_deref(ira, &var_ptr->base, var_ptr, nullptr); - if (type_is_invalid(deref->value->type)) { - var->var_type = ira->codegen->builtin_types.entry_invalid; - return ira->codegen->invalid_inst_gen; - } - // If this assertion trips, something is wrong with the IR instructions, because - // we expected the above deref to return a constant value, but it created a runtime - // instruction. - assert(deref->value->special != ConstValSpecialRuntime); - var_ptr->value->special = ConstValSpecialRuntime; - ir_analyze_store_ptr(ira, &var_ptr->base, var_ptr, deref, false); - } - if (instr_is_comptime(var_ptr) && (is_comptime_var || (var_class_requires_const && var->gen_is_const))) { - return ir_const_void(ira, &decl_var_instruction->base.base); - } - } else if (is_comptime_var) { - ir_add_error(ira, &decl_var_instruction->base.base, - buf_sprintf("cannot store runtime value in compile time variable")); - var->var_type = ira->codegen->builtin_types.entry_invalid; - return ira->codegen->invalid_inst_gen; - } - - ZigFn *fn_entry = ira->new_irb.exec->fn_entry; - if (fn_entry) - fn_entry->variable_list.append(var); - - return ir_build_var_decl_gen(ira, &decl_var_instruction->base.base, var, var_ptr); -} - -static IrInstGen *ir_analyze_instruction_export(IrAnalyze *ira, IrInstSrcExport *instruction) { - IrInstGen *target = instruction->target->child; - if (type_is_invalid(target->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *options = instruction->options->child; - if (type_is_invalid(options->value->type)) - return ira->codegen->invalid_inst_gen; - - ZigType *options_type = options->value->type; - assert(options_type->id == ZigTypeIdStruct); - - TypeStructField *name_field = find_struct_type_field(options_type, buf_create_from_str("name")); - ir_assert(name_field != nullptr, &instruction->base.base); - IrInstGen *name_inst = ir_analyze_struct_value_field_value(ira, &instruction->base.base, options, name_field); - if (type_is_invalid(name_inst->value->type)) - return ira->codegen->invalid_inst_gen; - - TypeStructField *linkage_field = find_struct_type_field(options_type, buf_create_from_str("linkage")); - ir_assert(linkage_field != nullptr, &instruction->base.base); - IrInstGen *linkage_inst = ir_analyze_struct_value_field_value(ira, &instruction->base.base, options, linkage_field); - if (type_is_invalid(linkage_inst->value->type)) - return ira->codegen->invalid_inst_gen; - - TypeStructField *section_field = find_struct_type_field(options_type, buf_create_from_str("section")); - ir_assert(section_field != nullptr, &instruction->base.base); - IrInstGen *section_inst = ir_analyze_struct_value_field_value(ira, &instruction->base.base, options, section_field); - if (type_is_invalid(section_inst->value->type)) - return ira->codegen->invalid_inst_gen; - - // The `section` field is optional, we have to unwrap it first - IrInstGen *non_null_check = ir_analyze_test_non_null(ira, &instruction->base.base, section_inst); - bool is_non_null; - if (!ir_resolve_bool(ira, non_null_check, &is_non_null)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *section_str_inst = nullptr; - if (is_non_null) { - section_str_inst = ir_analyze_optional_value_payload_value(ira, &instruction->base.base, section_inst, false); - if (type_is_invalid(section_str_inst->value->type)) - return ira->codegen->invalid_inst_gen; - } - - // Resolve all the comptime values - Buf *symbol_name = ir_resolve_str(ira, name_inst); - if (!symbol_name) - return ira->codegen->invalid_inst_gen; - - if (buf_len(symbol_name) < 1) { - ir_add_error(ira, &name_inst->base, - buf_sprintf("exported symbol name cannot be empty")); - return ira->codegen->invalid_inst_gen; - } - - GlobalLinkageId global_linkage_id; - if (!ir_resolve_global_linkage(ira, linkage_inst, &global_linkage_id)) - return ira->codegen->invalid_inst_gen; - - Buf *section_name = nullptr; - if (section_str_inst != nullptr && !(section_name = ir_resolve_str(ira, section_str_inst))) - return ira->codegen->invalid_inst_gen; - - // TODO: This function needs to be audited. - // It's not clear how all the different types are supposed to be handled. - // Need comprehensive tests for exporting one thing in one file and declaring an extern var - // in another file. - TldFn *tld_fn = heap::c_allocator.create(); - tld_fn->base.id = TldIdFn; - tld_fn->base.source_node = instruction->base.base.source_node; - - auto entry = ira->codegen->exported_symbol_names.put_unique(symbol_name, &tld_fn->base); - if (entry) { - AstNode *other_export_node = entry->value->source_node; - ErrorMsg *msg = ir_add_error(ira, &instruction->base.base, - buf_sprintf("exported symbol collision: '%s'", buf_ptr(symbol_name))); - add_error_note(ira->codegen, msg, other_export_node, buf_sprintf("other symbol is here")); - return ira->codegen->invalid_inst_gen; - } - - Error err; - bool want_var_export = false; - switch (target->value->type->id) { - case ZigTypeIdInvalid: - case ZigTypeIdUnreachable: - zig_unreachable(); - case ZigTypeIdFn: { - assert(target->value->data.x_ptr.special == ConstPtrSpecialFunction); - ZigFn *fn_entry = target->value->data.x_ptr.data.fn.fn_entry; - tld_fn->fn_entry = fn_entry; - CallingConvention cc = fn_entry->type_entry->data.fn.fn_type_id.cc; - switch (cc) { - case CallingConventionUnspecified: { - ErrorMsg *msg = ir_add_error(ira, &target->base, - buf_sprintf("exported function must specify calling convention")); - add_error_note(ira->codegen, msg, fn_entry->proto_node, buf_sprintf("declared here")); - } break; - case CallingConventionAsync: { - ErrorMsg *msg = ir_add_error(ira, &target->base, - buf_sprintf("exported function cannot be async")); - add_error_note(ira->codegen, msg, fn_entry->proto_node, buf_sprintf("declared here")); - } break; - 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(ira->codegen, fn_entry, buf_ptr(symbol_name), global_linkage_id, cc); - fn_entry->section_name = section_name; - break; - } - } break; - case ZigTypeIdStruct: - if (is_slice(target->value->type)) { - ir_add_error(ira, &target->base, - buf_sprintf("unable to export value of type '%s'", buf_ptr(&target->value->type->name))); - } else if (target->value->type->data.structure.layout != ContainerLayoutExtern) { - ErrorMsg *msg = ir_add_error(ira, &target->base, - buf_sprintf("exported struct value must be declared extern")); - add_error_note(ira->codegen, msg, target->value->type->data.structure.decl_node, buf_sprintf("declared here")); - } else { - want_var_export = true; - } - break; - case ZigTypeIdUnion: - if (target->value->type->data.unionation.layout != ContainerLayoutExtern) { - ErrorMsg *msg = ir_add_error(ira, &target->base, - buf_sprintf("exported union value must be declared extern")); - add_error_note(ira->codegen, msg, target->value->type->data.unionation.decl_node, buf_sprintf("declared here")); - } else { - want_var_export = true; - } - break; - case ZigTypeIdEnum: - if (target->value->type->data.enumeration.layout != ContainerLayoutExtern) { - ErrorMsg *msg = ir_add_error(ira, &target->base, - buf_sprintf("exported enum value must be declared extern")); - add_error_note(ira->codegen, msg, target->value->type->data.enumeration.decl_node, buf_sprintf("declared here")); - } else { - want_var_export = true; - } - break; - case ZigTypeIdArray: { - bool ok_type; - if ((err = type_allowed_in_extern(ira->codegen, target->value->type->data.array.child_type, &ok_type))) - return ira->codegen->invalid_inst_gen; - - if (!ok_type) { - ir_add_error(ira, &target->base, - buf_sprintf("array element type '%s' not extern-compatible", - buf_ptr(&target->value->type->data.array.child_type->name))); - } else { - want_var_export = true; - } - break; - } - case ZigTypeIdMetaType: { - ZigType *type_value = target->value->data.x_type; - switch (type_value->id) { - case ZigTypeIdInvalid: - zig_unreachable(); - case ZigTypeIdStruct: - if (is_slice(type_value)) { - ir_add_error(ira, &target->base, - buf_sprintf("unable to export type '%s'", buf_ptr(&type_value->name))); - } else if (type_value->data.structure.layout != ContainerLayoutExtern) { - ErrorMsg *msg = ir_add_error(ira, &target->base, - buf_sprintf("exported struct must be declared extern")); - add_error_note(ira->codegen, msg, type_value->data.structure.decl_node, buf_sprintf("declared here")); - } - break; - case ZigTypeIdUnion: - if (type_value->data.unionation.layout != ContainerLayoutExtern) { - ErrorMsg *msg = ir_add_error(ira, &target->base, - buf_sprintf("exported union must be declared extern")); - add_error_note(ira->codegen, msg, type_value->data.unionation.decl_node, buf_sprintf("declared here")); - } - break; - case ZigTypeIdEnum: - if (type_value->data.enumeration.layout != ContainerLayoutExtern) { - ErrorMsg *msg = ir_add_error(ira, &target->base, - buf_sprintf("exported enum must be declared extern")); - add_error_note(ira->codegen, msg, type_value->data.enumeration.decl_node, buf_sprintf("declared here")); - } - break; - case ZigTypeIdFn: { - if (type_value->data.fn.fn_type_id.cc == CallingConventionUnspecified) { - ir_add_error(ira, &target->base, - buf_sprintf("exported function type must specify calling convention")); - } - } break; - case ZigTypeIdInt: - case ZigTypeIdFloat: - case ZigTypeIdPointer: - case ZigTypeIdArray: - case ZigTypeIdBool: - case ZigTypeIdVector: - break; - case ZigTypeIdMetaType: - case ZigTypeIdVoid: - case ZigTypeIdUnreachable: - case ZigTypeIdComptimeFloat: - case ZigTypeIdComptimeInt: - case ZigTypeIdEnumLiteral: - case ZigTypeIdUndefined: - case ZigTypeIdNull: - case ZigTypeIdOptional: - case ZigTypeIdErrorUnion: - case ZigTypeIdErrorSet: - case ZigTypeIdBoundFn: - case ZigTypeIdOpaque: - case ZigTypeIdFnFrame: - case ZigTypeIdAnyFrame: - ir_add_error(ira, &target->base, - buf_sprintf("invalid export target '%s'", buf_ptr(&type_value->name))); - break; - } - } break; - case ZigTypeIdInt: - want_var_export = true; - break; - case ZigTypeIdVoid: - case ZigTypeIdBool: - case ZigTypeIdFloat: - case ZigTypeIdPointer: - case ZigTypeIdComptimeFloat: - case ZigTypeIdComptimeInt: - case ZigTypeIdUndefined: - case ZigTypeIdNull: - case ZigTypeIdOptional: - case ZigTypeIdErrorUnion: - case ZigTypeIdErrorSet: - case ZigTypeIdVector: - zig_panic("TODO export const value of type %s", buf_ptr(&target->value->type->name)); - case ZigTypeIdBoundFn: - case ZigTypeIdOpaque: - case ZigTypeIdEnumLiteral: - case ZigTypeIdFnFrame: - case ZigTypeIdAnyFrame: - ir_add_error(ira, &target->base, - buf_sprintf("invalid export target type '%s'", buf_ptr(&target->value->type->name))); - break; - } - - // TODO audit the various ways to use @export - if (want_var_export && target->id == IrInstGenIdLoadPtr) { - IrInstGenLoadPtr *load_ptr = reinterpret_cast(target); - if (load_ptr->ptr->id == IrInstGenIdVarPtr) { - IrInstGenVarPtr *var_ptr = reinterpret_cast(load_ptr->ptr); - ZigVar *var = var_ptr->var; - add_var_export(ira->codegen, var, buf_ptr(symbol_name), global_linkage_id); - var->section_name = section_name; - } - } - - return ir_const_void(ira, &instruction->base.base); -} - -static bool exec_has_err_ret_trace(CodeGen *g, IrExecutableSrc *exec) { - ZigFn *fn_entry = exec_fn_entry(exec); - return fn_entry != nullptr && fn_entry->calls_or_awaits_errorable_fn && g->have_err_ret_tracing; -} - -static IrInstGen *ir_analyze_instruction_error_return_trace(IrAnalyze *ira, - IrInstSrcErrorReturnTrace *instruction) -{ - ZigType *ptr_to_stack_trace_type = get_pointer_to_type(ira->codegen, get_stack_trace_type(ira->codegen), false); - if (instruction->optional == IrInstErrorReturnTraceNull) { - ZigType *optional_type = get_optional_type(ira->codegen, ptr_to_stack_trace_type); - if (!exec_has_err_ret_trace(ira->codegen, ira->old_irb.exec)) { - IrInstGen *result = ir_const(ira, &instruction->base.base, optional_type); - ZigValue *out_val = result->value; - assert(get_src_ptr_type(optional_type) != nullptr); - out_val->data.x_ptr.special = ConstPtrSpecialHardCodedAddr; - out_val->data.x_ptr.data.hard_coded_addr.addr = 0; - return result; - } - return ir_build_error_return_trace_gen(ira, instruction->base.base.scope, - instruction->base.base.source_node, instruction->optional, optional_type); - } else { - assert(ira->codegen->have_err_ret_tracing); - return ir_build_error_return_trace_gen(ira, instruction->base.base.scope, - instruction->base.base.source_node, instruction->optional, ptr_to_stack_trace_type); - } -} - -static IrInstGen *ir_analyze_instruction_error_union(IrAnalyze *ira, IrInstSrcErrorUnion *instruction) { - IrInstGen *result = ir_const(ira, &instruction->base.base, ira->codegen->builtin_types.entry_type); - result->value->special = ConstValSpecialLazy; - - LazyValueErrUnionType *lazy_err_union_type = heap::c_allocator.create(); - lazy_err_union_type->ira = ira; ira_ref(ira); - result->value->data.x_lazy = &lazy_err_union_type->base; - lazy_err_union_type->base.id = LazyValueIdErrUnionType; - - lazy_err_union_type->err_set_type = instruction->err_set->child; - if (ir_resolve_type_lazy(ira, lazy_err_union_type->err_set_type) == nullptr) - return ira->codegen->invalid_inst_gen; - - lazy_err_union_type->payload_type = instruction->payload->child; - if (ir_resolve_type_lazy(ira, lazy_err_union_type->payload_type) == nullptr) - return ira->codegen->invalid_inst_gen; - - return result; -} - -static IrInstGen *ir_analyze_alloca(IrAnalyze *ira, IrInst *source_inst, ZigType *var_type, - uint32_t align, const char *name_hint, bool force_comptime) -{ - Error err; - - ZigValue *pointee = ira->codegen->pass1_arena->create(); - pointee->special = ConstValSpecialUndef; - pointee->llvm_align = align; - - IrInstGenAlloca *result = ir_build_alloca_gen(ira, source_inst, align, name_hint); - result->base.value->special = ConstValSpecialStatic; - result->base.value->data.x_ptr.special = ConstPtrSpecialRef; - result->base.value->data.x_ptr.mut = force_comptime ? ConstPtrMutComptimeVar : ConstPtrMutInfer; - result->base.value->data.x_ptr.data.ref.pointee = pointee; - - bool var_type_has_bits; - if ((err = type_has_bits2(ira->codegen, var_type, &var_type_has_bits))) - return ira->codegen->invalid_inst_gen; - if (align != 0) { - if ((err = type_resolve(ira->codegen, var_type, ResolveStatusAlignmentKnown))) - return ira->codegen->invalid_inst_gen; - if (!var_type_has_bits) { - ir_add_error(ira, source_inst, - buf_sprintf("variable '%s' of zero-bit type '%s' has no in-memory representation, it cannot be aligned", - name_hint, buf_ptr(&var_type->name))); - return ira->codegen->invalid_inst_gen; - } - } - assert(result->base.value->data.x_ptr.special != ConstPtrSpecialInvalid); - - pointee->type = var_type; - result->base.value->type = get_pointer_to_type_extra(ira->codegen, var_type, false, false, - PtrLenSingle, align, 0, 0, false); - - if (!force_comptime) { - ZigFn *fn_entry = ira->new_irb.exec->fn_entry; - if (fn_entry != nullptr) { - fn_entry->alloca_gen_list.append(result); - } - } - return &result->base; -} - -static ZigType *ir_result_loc_expected_type(IrAnalyze *ira, IrInst *suspend_source_instr, - ResultLoc *result_loc) -{ - switch (result_loc->id) { - case ResultLocIdInvalid: - case ResultLocIdPeerParent: - zig_unreachable(); - case ResultLocIdNone: - case ResultLocIdVar: - case ResultLocIdBitCast: - case ResultLocIdCast: - return nullptr; - case ResultLocIdInstruction: - return result_loc->source_instruction->child->value->type; - case ResultLocIdReturn: - return ira->explicit_return_type; - case ResultLocIdPeer: - return reinterpret_cast(result_loc)->parent->resolved_type; - } - zig_unreachable(); -} - -static bool type_can_bit_cast(ZigType *t) { - switch (t->id) { - case ZigTypeIdInvalid: - zig_unreachable(); - case ZigTypeIdMetaType: - case ZigTypeIdOpaque: - case ZigTypeIdBoundFn: - case ZigTypeIdUnreachable: - case ZigTypeIdComptimeFloat: - case ZigTypeIdComptimeInt: - case ZigTypeIdEnumLiteral: - case ZigTypeIdUndefined: - case ZigTypeIdNull: - case ZigTypeIdPointer: - return false; - default: - // TODO list these types out explicitly, there are probably some other invalid ones here - return true; - } -} - -static void set_up_result_loc_for_inferred_comptime(IrAnalyze *ira, IrInstGen *ptr) { - ZigValue *undef_child = ira->codegen->pass1_arena->create(); - undef_child->type = ptr->value->type->data.pointer.child_type; - undef_child->special = ConstValSpecialUndef; - ptr->value->special = ConstValSpecialStatic; - ptr->value->data.x_ptr.mut = ConstPtrMutInfer; - ptr->value->data.x_ptr.special = ConstPtrSpecialRef; - ptr->value->data.x_ptr.data.ref.pointee = undef_child; -} - -static Error ir_result_has_type(IrAnalyze *ira, ResultLoc *result_loc, bool *out) { - switch (result_loc->id) { - case ResultLocIdInvalid: - case ResultLocIdPeerParent: - zig_unreachable(); - case ResultLocIdNone: - case ResultLocIdPeer: - *out = false; - return ErrorNone; - case ResultLocIdReturn: - case ResultLocIdInstruction: - case ResultLocIdBitCast: - *out = true; - return ErrorNone; - case ResultLocIdCast: { - ResultLocCast *result_cast = reinterpret_cast(result_loc); - ZigType *dest_type = ir_resolve_type(ira, result_cast->base.source_instruction->child); - if (type_is_invalid(dest_type)) - return ErrorSemanticAnalyzeFail; - *out = (dest_type != ira->codegen->builtin_types.entry_anytype); - return ErrorNone; - } - case ResultLocIdVar: - *out = reinterpret_cast(result_loc)->var->decl_node->data.variable_declaration.type != nullptr; - return ErrorNone; - } - zig_unreachable(); -} - -static IrInstGen *ir_resolve_no_result_loc(IrAnalyze *ira, IrInst *suspend_source_instr, - ResultLoc *result_loc, ZigType *value_type) -{ - if (type_is_invalid(value_type)) - return ira->codegen->invalid_inst_gen; - IrInstGenAlloca *alloca_gen = ir_build_alloca_gen(ira, suspend_source_instr, 0, ""); - alloca_gen->base.value->type = get_pointer_to_type_extra(ira->codegen, value_type, false, false, - PtrLenSingle, 0, 0, 0, false); - set_up_result_loc_for_inferred_comptime(ira, &alloca_gen->base); - ZigFn *fn_entry = ira->new_irb.exec->fn_entry; - if (fn_entry != nullptr && get_scope_typeof(suspend_source_instr->scope) == nullptr) { - fn_entry->alloca_gen_list.append(alloca_gen); - } - result_loc->written = true; - result_loc->resolved_loc = &alloca_gen->base; - return result_loc->resolved_loc; -} - -static bool result_loc_is_discard(ResultLoc *result_loc_pass1) { - if (result_loc_pass1->id == ResultLocIdInstruction && - result_loc_pass1->source_instruction->id == IrInstSrcIdConst) - { - IrInstSrcConst *const_inst = reinterpret_cast(result_loc_pass1->source_instruction); - if (value_is_comptime(const_inst->value) && - const_inst->value->type->id == ZigTypeIdPointer && - const_inst->value->data.x_ptr.special == ConstPtrSpecialDiscard) - { - return true; - } - } - return false; -} - -// when calling this function, at the callsite must check for result type noreturn and propagate it up -static IrInstGen *ir_resolve_result_raw(IrAnalyze *ira, IrInst *suspend_source_instr, - ResultLoc *result_loc, ZigType *value_type, IrInstGen *value, bool force_runtime, - bool allow_discard) -{ - Error err; - if (result_loc->resolved_loc != nullptr) { - // allow to redo the result location if the value is known and comptime and the previous one isn't - if (value == nullptr || !instr_is_comptime(value) || instr_is_comptime(result_loc->resolved_loc)) { - return result_loc->resolved_loc; - } - } - result_loc->gen_instruction = value; - result_loc->implicit_elem_type = value_type; - switch (result_loc->id) { - case ResultLocIdInvalid: - case ResultLocIdPeerParent: - zig_unreachable(); - case ResultLocIdNone: { - if (value != nullptr) { - return nullptr; - } - // need to return a result location and don't have one. use a stack allocation - return ir_resolve_no_result_loc(ira, suspend_source_instr, result_loc, value_type); - } - case ResultLocIdVar: { - ResultLocVar *result_loc_var = reinterpret_cast(result_loc); - assert(result_loc->source_instruction->id == IrInstSrcIdAlloca); - IrInstSrcAlloca *alloca_src = reinterpret_cast(result_loc->source_instruction); - - ZigVar *var = result_loc_var->var; - if (var->var_type != nullptr && !ir_get_var_is_comptime(var)) { - // This is at least the second time we've seen this variable declaration during analysis. - // This means that this is actually a different variable due to, e.g. an inline while loop. - // We make a new variable so that it can hold a different type, and so the debug info can - // be distinct. - ZigVar *new_var = create_local_var(ira->codegen, var->decl_node, var->child_scope, - buf_create_from_str(var->name), var->src_is_const, var->gen_is_const, - var->shadowable, var->is_comptime, true); - new_var->align_bytes = var->align_bytes; - - var->next_var = new_var; - var = new_var; - } - if (value_type->id == ZigTypeIdUnreachable || value_type->id == ZigTypeIdOpaque) { - ir_add_error(ira, &result_loc->source_instruction->base, - buf_sprintf("variable of type '%s' not allowed", buf_ptr(&value_type->name))); - return ira->codegen->invalid_inst_gen; - } - if (alloca_src->base.child == nullptr || var->ptr_instruction == nullptr) { - bool force_comptime; - if (!ir_resolve_comptime(ira, alloca_src->is_comptime->child, &force_comptime)) - return ira->codegen->invalid_inst_gen; - uint32_t align = 0; - if (alloca_src->align != nullptr && !ir_resolve_align(ira, alloca_src->align->child, nullptr, &align)) { - return ira->codegen->invalid_inst_gen; - } - IrInstGen *alloca_gen = ir_analyze_alloca(ira, &result_loc->source_instruction->base, value_type, - align, alloca_src->name_hint, force_comptime); - if (force_runtime) { - alloca_gen->value->data.x_ptr.mut = ConstPtrMutRuntimeVar; - alloca_gen->value->special = ConstValSpecialRuntime; - } - if (alloca_src->base.child != nullptr && !result_loc->written) { - alloca_src->base.child->base.ref_count = 0; - } - alloca_src->base.child = alloca_gen; - var->ptr_instruction = alloca_gen; - } - result_loc->written = true; - result_loc->resolved_loc = alloca_src->base.child; - return alloca_src->base.child; - } - case ResultLocIdInstruction: { - result_loc->written = true; - result_loc->resolved_loc = result_loc->source_instruction->child; - return result_loc->resolved_loc; - } - case ResultLocIdReturn: { - if (value != nullptr) { - reinterpret_cast(result_loc)->implicit_return_type_done = true; - ira->src_implicit_return_type_list.append(value); - } - result_loc->written = true; - result_loc->resolved_loc = ira->return_ptr; - return result_loc->resolved_loc; - } - case ResultLocIdPeer: { - ResultLocPeer *result_peer = reinterpret_cast(result_loc); - ResultLocPeerParent *peer_parent = result_peer->parent; - - if (peer_parent->peers.length == 1) { - IrInstGen *parent_result_loc = ir_resolve_result(ira, suspend_source_instr, peer_parent->parent, - value_type, value, force_runtime, true); - result_peer->suspend_pos.basic_block_index = SIZE_MAX; - result_peer->suspend_pos.instruction_index = SIZE_MAX; - if (parent_result_loc == nullptr || type_is_invalid(parent_result_loc->value->type) || - parent_result_loc->value->type->id == ZigTypeIdUnreachable) - { - return parent_result_loc; - } - result_loc->written = true; - result_loc->resolved_loc = parent_result_loc; - return result_loc->resolved_loc; - } - - bool is_condition_comptime; - if (!ir_resolve_comptime(ira, peer_parent->is_comptime->child, &is_condition_comptime)) - return ira->codegen->invalid_inst_gen; - if (is_condition_comptime) { - peer_parent->skipped = true; - return ir_resolve_result(ira, suspend_source_instr, peer_parent->parent, - value_type, value, force_runtime, true); - } - bool peer_parent_has_type; - if ((err = ir_result_has_type(ira, peer_parent->parent, &peer_parent_has_type))) - return ira->codegen->invalid_inst_gen; - if (peer_parent_has_type) { - peer_parent->skipped = true; - IrInstGen *parent_result_loc = ir_resolve_result(ira, suspend_source_instr, peer_parent->parent, - value_type, value, force_runtime || !is_condition_comptime, true); - if (parent_result_loc == nullptr || type_is_invalid(parent_result_loc->value->type) || - parent_result_loc->value->type->id == ZigTypeIdUnreachable) - { - return parent_result_loc; - } - peer_parent->parent->written = true; - result_loc->written = true; - result_loc->resolved_loc = parent_result_loc; - return result_loc->resolved_loc; - } - - if (peer_parent->resolved_type == nullptr) { - if (peer_parent->end_bb->suspend_instruction_ref == nullptr) { - peer_parent->end_bb->suspend_instruction_ref = suspend_source_instr; - } - IrInstGen *unreach_inst = ira_suspend(ira, suspend_source_instr, result_peer->next_bb, - &result_peer->suspend_pos); - if (result_peer->next_bb == nullptr) { - ir_start_next_bb(ira); - } - return unreach_inst; - } - - IrInstGen *parent_result_loc = ir_resolve_result(ira, suspend_source_instr, peer_parent->parent, - peer_parent->resolved_type, nullptr, force_runtime, true); - if (parent_result_loc == nullptr || type_is_invalid(parent_result_loc->value->type) || - parent_result_loc->value->type->id == ZigTypeIdUnreachable) - { - return parent_result_loc; - } - // because is_condition_comptime is false, we mark this a runtime pointer - parent_result_loc->value->special = ConstValSpecialRuntime; - result_loc->written = true; - result_loc->resolved_loc = parent_result_loc; - return result_loc->resolved_loc; - } - case ResultLocIdCast: { - ResultLocCast *result_cast = reinterpret_cast(result_loc); - ZigType *dest_type = ir_resolve_type(ira, result_cast->base.source_instruction->child); - if (type_is_invalid(dest_type)) - return ira->codegen->invalid_inst_gen; - - if (dest_type == ira->codegen->builtin_types.entry_anytype) { - return ir_resolve_no_result_loc(ira, suspend_source_instr, result_loc, value_type); - } - - IrInstGen *casted_value; - if (value != nullptr) { - casted_value = ir_implicit_cast2(ira, suspend_source_instr, value, dest_type); - if (type_is_invalid(casted_value->value->type)) - return ira->codegen->invalid_inst_gen; - dest_type = casted_value->value->type; - } else { - casted_value = nullptr; - } - - IrInstGen *parent_result_loc = ir_resolve_result(ira, suspend_source_instr, result_cast->parent, - dest_type, casted_value, force_runtime, true); - if (parent_result_loc == nullptr || type_is_invalid(parent_result_loc->value->type) || - parent_result_loc->value->type->id == ZigTypeIdUnreachable) - { - return parent_result_loc; - } - - ZigType *parent_ptr_type = parent_result_loc->value->type; - assert(parent_ptr_type->id == ZigTypeIdPointer); - - if ((err = type_resolve(ira->codegen, parent_ptr_type->data.pointer.child_type, - ResolveStatusAlignmentKnown))) - { - return ira->codegen->invalid_inst_gen; - } - uint64_t parent_ptr_align = get_ptr_align(ira->codegen, parent_ptr_type); - if ((err = type_resolve(ira->codegen, value_type, ResolveStatusAlignmentKnown))) { - return ira->codegen->invalid_inst_gen; - } - if (!type_has_bits(ira->codegen, value_type)) { - parent_ptr_align = 0; - } - // If we're casting from a sentinel-terminated array to a non-sentinel-terminated array, - // we actually need the result location pointer to *not* have a sentinel. Otherwise the generated - // memcpy will write an extra byte to the destination, and THAT'S NO GOOD. - ZigType *ptr_elem_type; - if (value_type->id == ZigTypeIdArray && value_type->data.array.sentinel != nullptr && - dest_type->id == ZigTypeIdArray && dest_type->data.array.sentinel == nullptr) - { - ptr_elem_type = get_array_type(ira->codegen, value_type->data.array.child_type, - value_type->data.array.len, nullptr); - } else { - ptr_elem_type = value_type; - } - ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, ptr_elem_type, - parent_ptr_type->data.pointer.is_const, parent_ptr_type->data.pointer.is_volatile, PtrLenSingle, - parent_ptr_align, 0, 0, parent_ptr_type->data.pointer.allow_zero); - - ConstCastOnly const_cast_result = types_match_const_cast_only(ira, - parent_result_loc->value->type, ptr_type, - result_cast->base.source_instruction->base.source_node, false); - if (const_cast_result.id == ConstCastResultIdInvalid) - return ira->codegen->invalid_inst_gen; - if (const_cast_result.id != ConstCastResultIdOk) { - if (allow_discard) { - return parent_result_loc; - } - // We will not be able to provide a result location for this value. Create - // a new result location. - result_cast->parent->written = false; - return ir_resolve_no_result_loc(ira, suspend_source_instr, result_loc, value_type); - } - - result_loc->written = true; - result_loc->resolved_loc = ir_analyze_ptr_cast(ira, suspend_source_instr, parent_result_loc, - &parent_result_loc->base, ptr_type, &result_cast->base.source_instruction->base, false, false); - return result_loc->resolved_loc; - } - case ResultLocIdBitCast: { - ResultLocBitCast *result_bit_cast = reinterpret_cast(result_loc); - ZigType *dest_type = ir_resolve_type(ira, result_bit_cast->base.source_instruction->child); - if (type_is_invalid(dest_type)) - return ira->codegen->invalid_inst_gen; - - ZigType *dest_cg_ptr_type; - if ((err = get_codegen_ptr_type(ira->codegen, dest_type, &dest_cg_ptr_type))) - return ira->codegen->invalid_inst_gen; - if (dest_cg_ptr_type != nullptr) { - ir_add_error(ira, &result_loc->source_instruction->base, - buf_sprintf("unable to @bitCast to pointer type '%s'", buf_ptr(&dest_type->name))); - return ira->codegen->invalid_inst_gen; - } - - if (!type_can_bit_cast(dest_type)) { - ir_add_error(ira, &result_loc->source_instruction->base, - buf_sprintf("unable to @bitCast to type '%s'", buf_ptr(&dest_type->name))); - return ira->codegen->invalid_inst_gen; - } - - ZigType *value_cg_ptr_type; - if ((err = get_codegen_ptr_type(ira->codegen, value_type, &value_cg_ptr_type))) - return ira->codegen->invalid_inst_gen; - if (value_cg_ptr_type != nullptr) { - ir_add_error(ira, suspend_source_instr, - buf_sprintf("unable to @bitCast from pointer type '%s'", buf_ptr(&value_type->name))); - return ira->codegen->invalid_inst_gen; - } - - if (!type_can_bit_cast(value_type)) { - ir_add_error(ira, suspend_source_instr, - buf_sprintf("unable to @bitCast from type '%s'", buf_ptr(&value_type->name))); - return ira->codegen->invalid_inst_gen; - } - - IrInstGen *bitcasted_value; - if (value != nullptr) { - bitcasted_value = ir_analyze_bit_cast(ira, &result_loc->source_instruction->base, value, dest_type); - dest_type = bitcasted_value->value->type; - } else { - bitcasted_value = nullptr; - } - - if (bitcasted_value != nullptr && type_is_invalid(bitcasted_value->value->type)) { - return bitcasted_value; - } - - bool parent_was_written = result_bit_cast->parent->written; - IrInstGen *parent_result_loc = ir_resolve_result(ira, suspend_source_instr, result_bit_cast->parent, - dest_type, bitcasted_value, force_runtime, true); - if (parent_result_loc == nullptr || type_is_invalid(parent_result_loc->value->type) || - parent_result_loc->value->type->id == ZigTypeIdUnreachable) - { - return parent_result_loc; - } - ZigType *parent_ptr_type = parent_result_loc->value->type; - assert(parent_ptr_type->id == ZigTypeIdPointer); - ZigType *child_type = parent_ptr_type->data.pointer.child_type; - - if (result_loc_is_discard(result_bit_cast->parent)) { - assert(allow_discard); - return parent_result_loc; - } - - if ((err = type_resolve(ira->codegen, child_type, ResolveStatusSizeKnown))) { - return ira->codegen->invalid_inst_gen; - } - - if ((err = type_resolve(ira->codegen, value_type, ResolveStatusSizeKnown))) { - return ira->codegen->invalid_inst_gen; - } - - if (child_type != ira->codegen->builtin_types.entry_anytype) { - if (type_size(ira->codegen, child_type) != type_size(ira->codegen, value_type)) { - // pointer cast won't work; we need a temporary location. - result_bit_cast->parent->written = parent_was_written; - result_loc->written = true; - result_loc->resolved_loc = ir_resolve_result(ira, suspend_source_instr, no_result_loc(), - value_type, bitcasted_value, force_runtime, true); - return result_loc->resolved_loc; - } - } - uint64_t parent_ptr_align = 0; - if (type_has_bits(ira->codegen, value_type)) parent_ptr_align = get_ptr_align(ira->codegen, parent_ptr_type); - ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, value_type, - parent_ptr_type->data.pointer.is_const, parent_ptr_type->data.pointer.is_volatile, PtrLenSingle, - parent_ptr_align, 0, 0, parent_ptr_type->data.pointer.allow_zero); - - result_loc->written = true; - result_loc->resolved_loc = ir_analyze_ptr_cast(ira, suspend_source_instr, parent_result_loc, - &parent_result_loc->base, ptr_type, &result_bit_cast->base.source_instruction->base, false, false); - return result_loc->resolved_loc; - } - } - zig_unreachable(); -} - -static IrInstGen *ir_resolve_result(IrAnalyze *ira, IrInst *suspend_source_instr, - ResultLoc *result_loc_pass1, ZigType *value_type, IrInstGen *value, bool force_runtime, - bool allow_discard) -{ - if (!allow_discard && result_loc_is_discard(result_loc_pass1)) { - result_loc_pass1 = no_result_loc(); - } - bool was_written = result_loc_pass1->written; - IrInstGen *result_loc = ir_resolve_result_raw(ira, suspend_source_instr, result_loc_pass1, value_type, - value, force_runtime, allow_discard); - if (result_loc == nullptr || result_loc->value->type->id == ZigTypeIdUnreachable || - type_is_invalid(result_loc->value->type)) - { - return result_loc; - } - - if ((force_runtime || (value != nullptr && !instr_is_comptime(value))) && - result_loc_pass1->written && result_loc->value->data.x_ptr.mut == ConstPtrMutInfer) - { - result_loc->value->special = ConstValSpecialRuntime; - } - - InferredStructField *isf = result_loc->value->type->data.pointer.inferred_struct_field; - if (isf != nullptr) { - TypeStructField *field; - IrInstGen *casted_ptr; - if (isf->already_resolved) { - field = find_struct_type_field(isf->inferred_struct_type, isf->field_name); - casted_ptr = result_loc; - } else { - isf->already_resolved = true; - // Now it's time to add the field to the struct type. - uint32_t old_field_count = isf->inferred_struct_type->data.structure.src_field_count; - uint32_t new_field_count = old_field_count + 1; - isf->inferred_struct_type->data.structure.src_field_count = new_field_count; - isf->inferred_struct_type->data.structure.fields = realloc_type_struct_fields( - isf->inferred_struct_type->data.structure.fields, old_field_count, new_field_count); - - field = isf->inferred_struct_type->data.structure.fields[old_field_count]; - field->name = isf->field_name; - field->type_entry = value_type; - field->type_val = create_const_type(ira->codegen, field->type_entry); - field->src_index = old_field_count; - field->decl_node = value ? value->base.source_node : suspend_source_instr->source_node; - if (value && instr_is_comptime(value)) { - ZigValue *val = ir_resolve_const(ira, value, UndefOk); - if (!val) - return ira->codegen->invalid_inst_gen; - field->is_comptime = true; - field->init_val = ira->codegen->pass1_arena->create(); - copy_const_val(ira->codegen, field->init_val, val); - return result_loc; - } - - ZigType *struct_ptr_type = get_pointer_to_type(ira->codegen, isf->inferred_struct_type, false); - if (instr_is_comptime(result_loc)) { - casted_ptr = ir_const(ira, suspend_source_instr, struct_ptr_type); - copy_const_val(ira->codegen, casted_ptr->value, result_loc->value); - casted_ptr->value->type = struct_ptr_type; - } else { - casted_ptr = result_loc; - } - if (instr_is_comptime(casted_ptr)) { - ZigValue *ptr_val = ir_resolve_const(ira, casted_ptr, UndefBad); - if (!ptr_val) - return ira->codegen->invalid_inst_gen; - if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { - ZigValue *struct_val = const_ptr_pointee(ira, ira->codegen, ptr_val, - suspend_source_instr->source_node); - struct_val->special = ConstValSpecialStatic; - struct_val->data.x_struct.fields = realloc_const_vals_ptrs(ira->codegen, - struct_val->data.x_struct.fields, old_field_count, new_field_count); - - ZigValue *field_val = struct_val->data.x_struct.fields[old_field_count]; - field_val->special = ConstValSpecialUndef; - field_val->type = field->type_entry; - field_val->parent.id = ConstParentIdStruct; - field_val->parent.data.p_struct.struct_val = struct_val; - field_val->parent.data.p_struct.field_index = old_field_count; - } - } - } - - result_loc = ir_analyze_struct_field_ptr(ira, suspend_source_instr, field, casted_ptr, - isf->inferred_struct_type, true); - result_loc_pass1->resolved_loc = result_loc; - } - - if (was_written) { - return result_loc; - } - - ir_assert(result_loc->value->type->id == ZigTypeIdPointer, suspend_source_instr); - ZigType *actual_elem_type = result_loc->value->type->data.pointer.child_type; - if (actual_elem_type->id == ZigTypeIdOptional && value_type->id != ZigTypeIdOptional && - value_type->id != ZigTypeIdNull && value_type->id != ZigTypeIdUndefined) - { - bool same_comptime_repr = types_have_same_zig_comptime_repr(ira->codegen, actual_elem_type, value_type); - if (!same_comptime_repr) { - result_loc_pass1->written = was_written; - return ir_analyze_unwrap_optional_payload(ira, suspend_source_instr, result_loc, false, true); - } - } else if (actual_elem_type->id == ZigTypeIdErrorUnion && value_type->id != ZigTypeIdErrorUnion && - value_type->id != ZigTypeIdUndefined) - { - if (value_type->id == ZigTypeIdErrorSet) { - return ir_analyze_unwrap_err_code(ira, suspend_source_instr, result_loc, true); - } else { - IrInstGen *unwrapped_err_ptr = ir_analyze_unwrap_error_payload(ira, suspend_source_instr, - result_loc, false, true); - ZigType *actual_payload_type = actual_elem_type->data.error_union.payload_type; - if (actual_payload_type->id == ZigTypeIdOptional && value_type->id != ZigTypeIdOptional && - value_type->id != ZigTypeIdNull && value_type->id != ZigTypeIdUndefined) - { - return ir_analyze_unwrap_optional_payload(ira, suspend_source_instr, unwrapped_err_ptr, false, true); - } else { - return unwrapped_err_ptr; - } - } - } - return result_loc; -} - -static IrInstGen *ir_analyze_instruction_resolve_result(IrAnalyze *ira, IrInstSrcResolveResult *instruction) { - ZigType *implicit_elem_type; - if (instruction->ty == nullptr) { - if (instruction->result_loc->id == ResultLocIdCast) { - implicit_elem_type = ir_resolve_type(ira, - instruction->result_loc->source_instruction->child); - if (type_is_invalid(implicit_elem_type)) - return ira->codegen->invalid_inst_gen; - } else if (instruction->result_loc->id == ResultLocIdReturn) { - implicit_elem_type = ira->explicit_return_type; - if (type_is_invalid(implicit_elem_type)) - return ira->codegen->invalid_inst_gen; - } else { - implicit_elem_type = ira->codegen->builtin_types.entry_anytype; - } - if (implicit_elem_type == ira->codegen->builtin_types.entry_anytype) { - Buf *bare_name = buf_alloc(); - Buf *name = get_anon_type_name(ira->codegen, nullptr, container_string(ContainerKindStruct), - instruction->base.base.scope, instruction->base.base.source_node, bare_name); - - StructSpecial struct_special = StructSpecialInferredStruct; - if (instruction->base.base.source_node->type == NodeTypeContainerInitExpr && - instruction->base.base.source_node->data.container_init_expr.kind == ContainerInitKindArray) - { - struct_special = StructSpecialInferredTuple; - } - - ZigType *inferred_struct_type = get_partial_container_type(ira->codegen, - instruction->base.base.scope, ContainerKindStruct, instruction->base.base.source_node, - buf_ptr(name), bare_name, ContainerLayoutAuto); - inferred_struct_type->data.structure.special = struct_special; - inferred_struct_type->data.structure.resolve_status = ResolveStatusBeingInferred; - implicit_elem_type = inferred_struct_type; - } - } else { - implicit_elem_type = ir_resolve_type(ira, instruction->ty->child); - if (type_is_invalid(implicit_elem_type)) - return ira->codegen->invalid_inst_gen; - } - IrInstGen *result_loc = ir_resolve_result(ira, &instruction->base.base, instruction->result_loc, - implicit_elem_type, nullptr, false, true); - if (result_loc != nullptr) - return result_loc; - - ZigFn *fn = ira->new_irb.exec->fn_entry; - if (fn != nullptr && fn->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync && - instruction->result_loc->id == ResultLocIdReturn) - { - result_loc = ir_resolve_result(ira, &instruction->base.base, no_result_loc(), - implicit_elem_type, nullptr, false, true); - if (result_loc != nullptr && - (type_is_invalid(result_loc->value->type) || result_loc->value->type->id == ZigTypeIdUnreachable)) - { - return result_loc; - } - result_loc->value->special = ConstValSpecialRuntime; - return result_loc; - } - - IrInstGen *result = ir_const(ira, &instruction->base.base, implicit_elem_type); - result->value->special = ConstValSpecialUndef; - IrInstGen *ptr = ir_get_ref(ira, &instruction->base.base, result, false, false); - ptr->value->data.x_ptr.mut = ConstPtrMutComptimeVar; - return ptr; -} - -static void ir_reset_result(ResultLoc *result_loc) { - result_loc->written = false; - result_loc->resolved_loc = nullptr; - result_loc->gen_instruction = nullptr; - result_loc->implicit_elem_type = nullptr; - switch (result_loc->id) { - case ResultLocIdInvalid: - zig_unreachable(); - case ResultLocIdPeerParent: { - ResultLocPeerParent *peer_parent = reinterpret_cast(result_loc); - peer_parent->skipped = false; - peer_parent->done_resuming = false; - peer_parent->resolved_type = nullptr; - for (size_t i = 0; i < peer_parent->peers.length; i += 1) { - ir_reset_result(&peer_parent->peers.at(i)->base); - } - break; - } - case ResultLocIdVar: { - IrInstSrcAlloca *alloca_src = reinterpret_cast(result_loc->source_instruction); - alloca_src->base.child = nullptr; - break; - } - case ResultLocIdReturn: - reinterpret_cast(result_loc)->implicit_return_type_done = false; - break; - case ResultLocIdPeer: - case ResultLocIdNone: - case ResultLocIdInstruction: - case ResultLocIdBitCast: - case ResultLocIdCast: - break; - } -} - -static IrInstGen *ir_analyze_instruction_reset_result(IrAnalyze *ira, IrInstSrcResetResult *instruction) { - ir_reset_result(instruction->result_loc); - return ir_const_void(ira, &instruction->base.base); -} - -static IrInstGen *get_async_call_result_loc(IrAnalyze *ira, IrInst* source_instr, - ZigType *fn_ret_type, bool is_async_call_builtin, IrInstGen **args_ptr, size_t args_len, - IrInstGen *ret_ptr_uncasted) -{ - ir_assert(is_async_call_builtin, source_instr); - if (type_is_invalid(ret_ptr_uncasted->value->type)) - return ira->codegen->invalid_inst_gen; - if (ret_ptr_uncasted->value->type->id == ZigTypeIdVoid) { - // Result location will be inside the async frame. - return nullptr; - } - return ir_implicit_cast(ira, ret_ptr_uncasted, get_pointer_to_type(ira->codegen, fn_ret_type, false)); -} - -static IrInstGen *ir_analyze_async_call(IrAnalyze *ira, IrInst* source_instr, ZigFn *fn_entry, - ZigType *fn_type, IrInstGen *fn_ref, IrInstGen **casted_args, size_t arg_count, - IrInstGen *casted_new_stack, bool is_async_call_builtin, IrInstGen *ret_ptr_uncasted, - ResultLoc *call_result_loc) -{ - if (fn_entry == nullptr) { - if (fn_type->data.fn.fn_type_id.cc != CallingConventionAsync) { - ir_add_error(ira, &fn_ref->base, - buf_sprintf("expected async function, found '%s'", buf_ptr(&fn_type->name))); - return ira->codegen->invalid_inst_gen; - } - if (casted_new_stack == nullptr) { - ir_add_error(ira, &fn_ref->base, buf_sprintf("function is not comptime-known; @asyncCall required")); - return ira->codegen->invalid_inst_gen; - } - } - if (casted_new_stack != nullptr) { - ZigType *fn_ret_type = fn_type->data.fn.fn_type_id.return_type; - IrInstGen *ret_ptr = get_async_call_result_loc(ira, source_instr, fn_ret_type, is_async_call_builtin, - casted_args, arg_count, ret_ptr_uncasted); - if (ret_ptr != nullptr && type_is_invalid(ret_ptr->value->type)) - return ira->codegen->invalid_inst_gen; - - ZigType *anyframe_type = get_any_frame_type(ira->codegen, fn_ret_type); - - IrInstGenCall *call_gen = ir_build_call_gen(ira, source_instr, fn_entry, fn_ref, - arg_count, casted_args, CallModifierAsync, casted_new_stack, - is_async_call_builtin, ret_ptr, anyframe_type); - return &call_gen->base; - } else { - ZigType *frame_type = get_fn_frame_type(ira->codegen, fn_entry); - IrInstGen *result_loc = ir_resolve_result(ira, source_instr, call_result_loc, - frame_type, nullptr, true, false); - if (type_is_invalid(result_loc->value->type) || result_loc->value->type->id == ZigTypeIdUnreachable) { - return result_loc; - } - result_loc = ir_implicit_cast2(ira, source_instr, result_loc, - get_pointer_to_type(ira->codegen, frame_type, false)); - if (type_is_invalid(result_loc->value->type)) - return ira->codegen->invalid_inst_gen; - return &ir_build_call_gen(ira, source_instr, fn_entry, fn_ref, arg_count, - casted_args, CallModifierAsync, casted_new_stack, - is_async_call_builtin, result_loc, frame_type)->base; - } -} -static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node, - IrInstGen *arg, Scope **exec_scope, size_t *next_proto_i) -{ - AstNode *param_decl_node = fn_proto_node->data.fn_proto.params.at(*next_proto_i); - assert(param_decl_node->type == NodeTypeParamDecl); - - IrInstGen *casted_arg; - if (param_decl_node->data.param_decl.anytype_token == nullptr) { - AstNode *param_type_node = param_decl_node->data.param_decl.type; - ZigType *param_type = ir_analyze_type_expr(ira, *exec_scope, param_type_node); - if (type_is_invalid(param_type)) - return false; - - casted_arg = ir_implicit_cast(ira, arg, param_type); - if (type_is_invalid(casted_arg->value->type)) - return false; - } else { - casted_arg = arg; - } - - ZigValue *arg_val = ir_resolve_const(ira, casted_arg, UndefOk); - if (!arg_val) - return false; - - Buf *param_name = param_decl_node->data.param_decl.name; - ZigVar *var = add_variable(ira->codegen, param_decl_node, - *exec_scope, param_name, true, arg_val, nullptr, arg_val->type); - *exec_scope = var->child_scope; - *next_proto_i += 1; - - return true; -} - -static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_node, - IrInstGen *arg, IrInst *arg_src, Scope **child_scope, size_t *next_proto_i, - GenericFnTypeId *generic_id, FnTypeId *fn_type_id, IrInstGen **casted_args, - ZigFn *impl_fn) -{ - AstNode *param_decl_node = fn_proto_node->data.fn_proto.params.at(*next_proto_i); - assert(param_decl_node->type == NodeTypeParamDecl); - bool is_var_args = param_decl_node->data.param_decl.is_var_args; - bool arg_part_of_generic_id = false; - IrInstGen *casted_arg; - if (is_var_args) { - arg_part_of_generic_id = true; - casted_arg = arg; - } else { - if (param_decl_node->data.param_decl.anytype_token == nullptr) { - AstNode *param_type_node = param_decl_node->data.param_decl.type; - ZigType *param_type = ir_analyze_type_expr(ira, *child_scope, param_type_node); - if (type_is_invalid(param_type)) - return false; - - casted_arg = ir_implicit_cast2(ira, arg_src, arg, param_type); - if (type_is_invalid(casted_arg->value->type)) - return false; - } else { - arg_part_of_generic_id = true; - casted_arg = arg; - } - } - - bool comptime_arg = param_decl_node->data.param_decl.is_comptime; - if (!comptime_arg) { - switch (type_requires_comptime(ira->codegen, casted_arg->value->type)) { - case ReqCompTimeInvalid: - return false; - case ReqCompTimeYes: - comptime_arg = true; - break; - case ReqCompTimeNo: - break; - } - } - - ZigValue *arg_val; - - if (comptime_arg) { - arg_part_of_generic_id = true; - arg_val = ir_resolve_const(ira, casted_arg, UndefBad); - if (!arg_val) - return false; - } else { - arg_val = create_const_runtime(ira->codegen, casted_arg->value->type); - } - if (arg_part_of_generic_id) { - copy_const_val(ira->codegen, &generic_id->params[generic_id->param_count], arg_val); - generic_id->param_count += 1; - } - - Buf *param_name = param_decl_node->data.param_decl.name; - if (!param_name) return false; - if (!is_var_args) { - ZigVar *var = add_variable(ira->codegen, param_decl_node, - *child_scope, param_name, true, arg_val, nullptr, arg_val->type); - *child_scope = var->child_scope; - var->shadowable = !comptime_arg; - - *next_proto_i += 1; - } else if (casted_arg->value->type->id == ZigTypeIdComptimeInt || - casted_arg->value->type->id == ZigTypeIdComptimeFloat) - { - ir_add_error(ira, &casted_arg->base, - buf_sprintf("compiler bug: integer and float literals in var args function must be casted. https://github.com/ziglang/zig/issues/557")); - return false; - } - - if (!comptime_arg) { - casted_args[fn_type_id->param_count] = casted_arg; - FnTypeParamInfo *param_info = &fn_type_id->param_info[fn_type_id->param_count]; - param_info->type = casted_arg->value->type; - param_info->is_noalias = param_decl_node->data.param_decl.is_noalias; - impl_fn->param_source_nodes[fn_type_id->param_count] = param_decl_node; - fn_type_id->param_count += 1; - } - - return true; -} - -static IrInstGen *ir_get_var_ptr(IrAnalyze *ira, IrInst *source_instr, ZigVar *var) { - while (var->next_var != nullptr) { - var = var->next_var; - } - - if (var->var_type == nullptr || type_is_invalid(var->var_type)) - return ira->codegen->invalid_inst_gen; - - bool is_volatile = false; - ZigType *var_ptr_type = get_pointer_to_type_extra(ira->codegen, var->var_type, - var->src_is_const, is_volatile, PtrLenSingle, var->align_bytes, 0, 0, false); - - if (var->ptr_instruction != nullptr) { - return ir_implicit_cast(ira, var->ptr_instruction, var_ptr_type); - } - - bool comptime_var_mem = ir_get_var_is_comptime(var); - bool linkage_makes_it_runtime = var->decl_node->data.variable_declaration.is_extern; - - IrInstGen *result = ir_build_var_ptr_gen(ira, source_instr, var); - result->value->type = var_ptr_type; - - if (!linkage_makes_it_runtime && !var->is_thread_local && value_is_comptime(var->const_value)) { - ZigValue *val = var->const_value; - switch (val->special) { - case ConstValSpecialRuntime: - break; - case ConstValSpecialStatic: // fallthrough - case ConstValSpecialLazy: // fallthrough - case ConstValSpecialUndef: { - ConstPtrMut ptr_mut; - if (comptime_var_mem) { - ptr_mut = ConstPtrMutComptimeVar; - } else if (var->gen_is_const) { - ptr_mut = ConstPtrMutComptimeConst; - } else { - assert(!comptime_var_mem); - ptr_mut = ConstPtrMutRuntimeVar; - } - result->value->special = ConstValSpecialStatic; - result->value->data.x_ptr.mut = ptr_mut; - result->value->data.x_ptr.special = ConstPtrSpecialRef; - result->value->data.x_ptr.data.ref.pointee = val; - return result; - } - } - } - - bool in_fn_scope = (scope_fn_entry(var->parent_scope) != nullptr); - result->value->data.rh_ptr = in_fn_scope ? RuntimeHintPtrStack : RuntimeHintPtrNonStack; - - return result; -} - -// This function is called when a comptime value becomes accessible at runtime. -static void mark_comptime_value_escape(IrAnalyze *ira, IrInst* source_instr, ZigValue *val) { - ir_assert(value_is_comptime(val), source_instr); - if (val->special == ConstValSpecialUndef) - return; - - if (val->type->id == ZigTypeIdFn && val->type->data.fn.fn_type_id.cc == CallingConventionUnspecified) { - ir_assert(val->data.x_ptr.special == ConstPtrSpecialFunction, source_instr); - if (val->data.x_ptr.data.fn.fn_entry->non_async_node == nullptr) { - val->data.x_ptr.data.fn.fn_entry->non_async_node = source_instr->source_node; - } - } -} - -static IrInstGen *ir_analyze_store_ptr(IrAnalyze *ira, IrInst* source_instr, - IrInstGen *ptr, IrInstGen *uncasted_value, bool allow_write_through_const) -{ - assert(ptr->value->type->id == ZigTypeIdPointer); - - if (ptr->value->data.x_ptr.special == ConstPtrSpecialDiscard) { - if (uncasted_value->value->type->id == ZigTypeIdErrorUnion || - uncasted_value->value->type->id == ZigTypeIdErrorSet) - { - ir_add_error(ira, source_instr, buf_sprintf("error is discarded")); - return ira->codegen->invalid_inst_gen; - } - return ir_const_void(ira, source_instr); - } - - if (ptr->value->type->data.pointer.is_const && !allow_write_through_const) { - ir_add_error(ira, source_instr, buf_sprintf("cannot assign to constant")); - return ira->codegen->invalid_inst_gen; - } - - ZigType *child_type = ptr->value->type->data.pointer.child_type; - IrInstGen *value = ir_implicit_cast(ira, uncasted_value, child_type); - if (type_is_invalid(value->value->type)) - return ira->codegen->invalid_inst_gen; - - switch (type_has_one_possible_value(ira->codegen, child_type)) { - case OnePossibleValueInvalid: - return ira->codegen->invalid_inst_gen; - case OnePossibleValueYes: - return ir_const_void(ira, source_instr); - case OnePossibleValueNo: - break; - } - - if (instr_is_comptime(ptr) && ptr->value->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { - if (!allow_write_through_const && ptr->value->data.x_ptr.mut == ConstPtrMutComptimeConst) { - ir_add_error(ira, source_instr, buf_sprintf("cannot assign to constant")); - return ira->codegen->invalid_inst_gen; - } - if ((allow_write_through_const && ptr->value->data.x_ptr.mut == ConstPtrMutComptimeConst) || - ptr->value->data.x_ptr.mut == ConstPtrMutComptimeVar || - ptr->value->data.x_ptr.mut == ConstPtrMutInfer) - { - if (instr_is_comptime(value)) { - ZigValue *dest_val = const_ptr_pointee(ira, ira->codegen, ptr->value, source_instr->source_node); - if (dest_val == nullptr) - return ira->codegen->invalid_inst_gen; - if (dest_val->special != ConstValSpecialRuntime) { - copy_const_val(ira->codegen, dest_val, value->value); - - if (ptr->value->data.x_ptr.mut == ConstPtrMutComptimeVar && - !ira->new_irb.current_basic_block->must_be_comptime_source_instr) - { - ira->new_irb.current_basic_block->must_be_comptime_source_instr = source_instr; - } - return ir_const_void(ira, source_instr); - } - } - if (ptr->value->data.x_ptr.mut == ConstPtrMutInfer) { - ptr->value->special = ConstValSpecialRuntime; - } else { - ir_add_error(ira, source_instr, - buf_sprintf("cannot store runtime value in compile time variable")); - ZigValue *dest_val = const_ptr_pointee_unchecked(ira->codegen, ptr->value); - dest_val->type = ira->codegen->builtin_types.entry_invalid; - - return ira->codegen->invalid_inst_gen; - } - } - } - - if (ptr->value->type->data.pointer.inferred_struct_field != nullptr && - child_type == ira->codegen->builtin_types.entry_anytype) - { - child_type = ptr->value->type->data.pointer.inferred_struct_field->inferred_struct_type; - } - - switch (type_requires_comptime(ira->codegen, child_type)) { - case ReqCompTimeInvalid: - return ira->codegen->invalid_inst_gen; - case ReqCompTimeYes: - switch (type_has_one_possible_value(ira->codegen, ptr->value->type)) { - case OnePossibleValueInvalid: - return ira->codegen->invalid_inst_gen; - case OnePossibleValueNo: - ir_add_error(ira, source_instr, - buf_sprintf("cannot store runtime value in type '%s'", buf_ptr(&child_type->name))); - return ira->codegen->invalid_inst_gen; - case OnePossibleValueYes: - return ir_const_void(ira, source_instr); - } - zig_unreachable(); - case ReqCompTimeNo: - break; - } - - if (instr_is_comptime(value)) { - mark_comptime_value_escape(ira, source_instr, value->value); - } - - // If this is a store to a pointer with a runtime-known vector index, - // we have to figure out the IrInstGen which represents the index and - // emit a IrInstGenVectorStoreElem, or emit a compile error - // explaining why it is impossible for this store to work. Which is that - // the pointer address is of the vector; without the element index being known - // we cannot properly perform the insertion. - if (ptr->value->type->data.pointer.vector_index == VECTOR_INDEX_RUNTIME) { - if (ptr->id == IrInstGenIdElemPtr) { - IrInstGenElemPtr *elem_ptr = (IrInstGenElemPtr *)ptr; - return ir_build_vector_store_elem(ira, source_instr, elem_ptr->array_ptr, - elem_ptr->elem_index, value); - } - ir_add_error(ira, &ptr->base, - buf_sprintf("unable to determine vector element index of type '%s'", - buf_ptr(&ptr->value->type->name))); - return ira->codegen->invalid_inst_gen; - } - - return ir_build_store_ptr_gen(ira, source_instr, ptr, value); -} - -static IrInstGen *analyze_casted_new_stack(IrAnalyze *ira, IrInst* source_instr, - IrInstGen *new_stack, IrInst *new_stack_src, bool is_async_call_builtin, ZigFn *fn_entry) -{ - if (new_stack == nullptr) - return nullptr; - - if (!is_async_call_builtin && - arch_stack_pointer_register_name(ira->codegen->zig_target->arch) == nullptr) - { - ir_add_error(ira, source_instr, - buf_sprintf("target arch '%s' does not support calling with a new stack", - target_arch_name(ira->codegen->zig_target->arch))); - } - - if (is_async_call_builtin && - fn_entry != nullptr && new_stack->value->type->id == ZigTypeIdPointer && - new_stack->value->type->data.pointer.child_type->id == ZigTypeIdFnFrame) - { - ZigType *needed_frame_type = get_pointer_to_type(ira->codegen, - get_fn_frame_type(ira->codegen, fn_entry), false); - return ir_implicit_cast(ira, new_stack, needed_frame_type); - } else { - ZigType *u8_ptr = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, - false, false, PtrLenUnknown, target_fn_align(ira->codegen->zig_target), 0, 0, false); - ZigType *u8_slice = get_slice_type(ira->codegen, u8_ptr); - ira->codegen->need_frame_size_prefix_data = true; - return ir_implicit_cast2(ira, new_stack_src, new_stack, u8_slice); - } -} - -static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr, - ZigFn *fn_entry, ZigType *fn_type, IrInstGen *fn_ref, - IrInstGen *first_arg_ptr, IrInst *first_arg_ptr_src, CallModifier modifier, - IrInstGen *new_stack, IrInst *new_stack_src, bool is_async_call_builtin, - IrInstGen **args_ptr, size_t args_len, IrInstGen *ret_ptr, ResultLoc *call_result_loc) -{ - Error err; - FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; - size_t first_arg_1_or_0 = first_arg_ptr ? 1 : 0; - - // for extern functions, the var args argument is not counted. - // for zig functions, it is. - size_t var_args_1_or_0; - if (fn_type_id->cc == CallingConventionC) { - var_args_1_or_0 = 0; - } else { - var_args_1_or_0 = fn_type_id->is_var_args ? 1 : 0; - } - size_t src_param_count = fn_type_id->param_count - var_args_1_or_0; - size_t call_param_count = args_len + first_arg_1_or_0; - AstNode *source_node = source_instr->source_node; - - AstNode *fn_proto_node = fn_entry ? fn_entry->proto_node : nullptr;; - - if (fn_type_id->cc == CallingConventionNaked) { - ErrorMsg *msg = ir_add_error(ira, &fn_ref->base, buf_sprintf("unable to call function with naked calling convention")); - if (fn_proto_node) { - add_error_note(ira->codegen, msg, fn_proto_node, buf_sprintf("declared here")); - } - return ira->codegen->invalid_inst_gen; - } - - if (fn_type_id->is_var_args) { - if (call_param_count < src_param_count) { - ErrorMsg *msg = ir_add_error_node(ira, source_node, - buf_sprintf("expected at least %" ZIG_PRI_usize " argument(s), found %" ZIG_PRI_usize "", - src_param_count, call_param_count)); - if (fn_proto_node) { - add_error_note(ira->codegen, msg, fn_proto_node, - buf_sprintf("declared here")); - } - return ira->codegen->invalid_inst_gen; - } - } else if (src_param_count != call_param_count) { - ErrorMsg *msg = ir_add_error_node(ira, source_node, - buf_sprintf("expected %" ZIG_PRI_usize " argument(s), found %" ZIG_PRI_usize "", - src_param_count, call_param_count)); - if (fn_proto_node) { - add_error_note(ira->codegen, msg, fn_proto_node, - buf_sprintf("declared here")); - } - return ira->codegen->invalid_inst_gen; - } - - if (modifier == CallModifierCompileTime) { - // If we are evaluating an extern function in a TypeOf call, we can return an undefined value - // of its return type. - if (fn_entry != nullptr && get_scope_typeof(source_instr->scope) != nullptr && - fn_proto_node->data.fn_proto.is_extern) { - - assert(fn_entry->body_node == nullptr); - AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type; - ZigType *return_type = ir_analyze_type_expr(ira, source_instr->scope, return_type_node); - if (type_is_invalid(return_type)) - return ira->codegen->invalid_inst_gen; - - return ir_const_undef(ira, source_instr, return_type); - } - - // No special handling is needed for compile time evaluation of generic functions. - if (!fn_entry || fn_entry->body_node == nullptr) { - ir_add_error(ira, &fn_ref->base, buf_sprintf("unable to evaluate constant expression")); - return ira->codegen->invalid_inst_gen; - } - - if (!ir_emit_backward_branch(ira, source_instr)) - return ira->codegen->invalid_inst_gen; - - // Fork a scope of the function with known values for the parameters. - Scope *exec_scope = &fn_entry->fndef_scope->base; - - size_t next_proto_i = 0; - if (first_arg_ptr) { - assert(first_arg_ptr->value->type->id == ZigTypeIdPointer); - - bool first_arg_known_bare = false; - if (fn_type_id->next_param_index >= 1) { - ZigType *param_type = fn_type_id->param_info[next_proto_i].type; - if (type_is_invalid(param_type)) - return ira->codegen->invalid_inst_gen; - first_arg_known_bare = param_type->id != ZigTypeIdPointer; - } - - IrInstGen *first_arg; - if (!first_arg_known_bare) { - first_arg = first_arg_ptr; - } else { - first_arg = ir_get_deref(ira, &first_arg_ptr->base, first_arg_ptr, nullptr); - if (type_is_invalid(first_arg->value->type)) - return ira->codegen->invalid_inst_gen; - } - - if (!ir_analyze_fn_call_inline_arg(ira, fn_proto_node, first_arg, &exec_scope, &next_proto_i)) - return ira->codegen->invalid_inst_gen; - } - - for (size_t call_i = 0; call_i < args_len; call_i += 1) { - IrInstGen *old_arg = args_ptr[call_i]; - - if (!ir_analyze_fn_call_inline_arg(ira, fn_proto_node, old_arg, &exec_scope, &next_proto_i)) - return ira->codegen->invalid_inst_gen; - } - - AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type; - if (return_type_node == nullptr) { - ir_add_error(ira, &fn_ref->base, - buf_sprintf("TODO implement inferred return types https://github.com/ziglang/zig/issues/447")); - return ira->codegen->invalid_inst_gen; - } - ZigType *specified_return_type = ir_analyze_type_expr(ira, exec_scope, return_type_node); - if (type_is_invalid(specified_return_type)) - return ira->codegen->invalid_inst_gen; - ZigType *return_type; - ZigType *inferred_err_set_type = nullptr; - if (fn_proto_node->data.fn_proto.auto_err_set) { - inferred_err_set_type = get_auto_err_set_type(ira->codegen, fn_entry); - if ((err = type_resolve(ira->codegen, specified_return_type, ResolveStatusSizeKnown))) - return ira->codegen->invalid_inst_gen; - return_type = get_error_union_type(ira->codegen, inferred_err_set_type, specified_return_type); - } else { - return_type = specified_return_type; - } - - bool cacheable = fn_eval_cacheable(exec_scope, return_type); - ZigValue *result = nullptr; - if (cacheable) { - auto entry = ira->codegen->memoized_fn_eval_table.maybe_get(exec_scope); - if (entry) - result = entry->value; - } - - if (result == nullptr) { - // Analyze the fn body block like any other constant expression. - AstNode *body_node = fn_entry->body_node; - ZigValue *result_ptr; - create_result_ptr(ira->codegen, return_type, &result, &result_ptr); - - if ((err = ir_eval_const_value(ira->codegen, exec_scope, body_node, result_ptr, - ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, - fn_entry, nullptr, source_instr->source_node, nullptr, ira->new_irb.exec, return_type_node, - UndefOk))) - { - return ira->codegen->invalid_inst_gen; - } - - if (inferred_err_set_type != nullptr) { - inferred_err_set_type->data.error_set.incomplete = false; - if (result->type->id == ZigTypeIdErrorUnion) { - ErrorTableEntry *err = result->data.x_err_union.error_set->data.x_err_set; - if (err != nullptr) { - inferred_err_set_type->data.error_set.err_count = 1; - inferred_err_set_type->data.error_set.errors = heap::c_allocator.create(); - inferred_err_set_type->data.error_set.errors[0] = err; - } - ZigType *fn_inferred_err_set_type = result->type->data.error_union.err_set_type; - inferred_err_set_type->data.error_set.err_count = fn_inferred_err_set_type->data.error_set.err_count; - inferred_err_set_type->data.error_set.errors = fn_inferred_err_set_type->data.error_set.errors; - } else if (result->type->id == ZigTypeIdErrorSet) { - inferred_err_set_type->data.error_set.err_count = result->type->data.error_set.err_count; - inferred_err_set_type->data.error_set.errors = result->type->data.error_set.errors; - } - } - - if (cacheable) { - ira->codegen->memoized_fn_eval_table.put(exec_scope, result); - } - - if (type_is_invalid(result->type)) { - return ira->codegen->invalid_inst_gen; - } - } - - IrInstGen *new_instruction = ir_const_move(ira, source_instr, result); - return ir_finish_anal(ira, new_instruction); - } - - if (fn_type->data.fn.is_generic) { - if (!fn_entry) { - ir_add_error(ira, &fn_ref->base, - buf_sprintf("calling a generic function requires compile-time known function value")); - return ira->codegen->invalid_inst_gen; - } - - size_t new_fn_arg_count = first_arg_1_or_0 + args_len; - - IrInstGen **casted_args = heap::c_allocator.allocate(new_fn_arg_count); - - // Fork a scope of the function with known values for the parameters. - Scope *parent_scope = fn_entry->fndef_scope->base.parent; - ZigFn *impl_fn = create_fn(ira->codegen, fn_proto_node); - impl_fn->param_source_nodes = heap::c_allocator.allocate(new_fn_arg_count); - buf_init_from_buf(&impl_fn->symbol_name, &fn_entry->symbol_name); - impl_fn->fndef_scope = create_fndef_scope(ira->codegen, impl_fn->body_node, parent_scope, impl_fn); - impl_fn->child_scope = &impl_fn->fndef_scope->base; - FnTypeId inst_fn_type_id = {0}; - init_fn_type_id(&inst_fn_type_id, fn_proto_node, fn_type_id->cc, new_fn_arg_count); - inst_fn_type_id.param_count = 0; - inst_fn_type_id.is_var_args = false; - - // TODO maybe GenericFnTypeId can be replaced with using the child_scope directly - // as the key in generic_table - GenericFnTypeId *generic_id = heap::c_allocator.create(); - generic_id->fn_entry = fn_entry; - generic_id->param_count = 0; - generic_id->params = ira->codegen->pass1_arena->allocate(new_fn_arg_count); - size_t next_proto_i = 0; - - if (first_arg_ptr) { - assert(first_arg_ptr->value->type->id == ZigTypeIdPointer); - - bool first_arg_known_bare = false; - if (fn_type_id->next_param_index >= 1) { - ZigType *param_type = fn_type_id->param_info[next_proto_i].type; - if (type_is_invalid(param_type)) - return ira->codegen->invalid_inst_gen; - first_arg_known_bare = param_type->id != ZigTypeIdPointer; - } - - IrInstGen *first_arg; - if (!first_arg_known_bare) { - first_arg = first_arg_ptr; - } else { - first_arg = ir_get_deref(ira, &first_arg_ptr->base, first_arg_ptr, nullptr); - if (type_is_invalid(first_arg->value->type)) - return ira->codegen->invalid_inst_gen; - } - - if (!ir_analyze_fn_call_generic_arg(ira, fn_proto_node, first_arg, first_arg_ptr_src, - &impl_fn->child_scope, &next_proto_i, generic_id, &inst_fn_type_id, casted_args, impl_fn)) - { - return ira->codegen->invalid_inst_gen; - } - } - - ZigFn *parent_fn_entry = ira->new_irb.exec->fn_entry; - assert(parent_fn_entry); - for (size_t call_i = 0; call_i < args_len; call_i += 1) { - IrInstGen *arg = args_ptr[call_i]; - - AstNode *param_decl_node = fn_proto_node->data.fn_proto.params.at(next_proto_i); - assert(param_decl_node->type == NodeTypeParamDecl); - - if (!ir_analyze_fn_call_generic_arg(ira, fn_proto_node, arg, &arg->base, &impl_fn->child_scope, - &next_proto_i, generic_id, &inst_fn_type_id, casted_args, impl_fn)) - { - return ira->codegen->invalid_inst_gen; - } - } - - if (fn_proto_node->data.fn_proto.align_expr != nullptr) { - ZigValue *align_result; - ZigValue *result_ptr; - create_result_ptr(ira->codegen, get_align_amt_type(ira->codegen), &align_result, &result_ptr); - if ((err = ir_eval_const_value(ira->codegen, impl_fn->child_scope, - fn_proto_node->data.fn_proto.align_expr, result_ptr, - ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, - nullptr, nullptr, fn_proto_node->data.fn_proto.align_expr, nullptr, ira->new_irb.exec, - nullptr, UndefBad))) - { - return ira->codegen->invalid_inst_gen; - } - IrInstGenConst *const_instruction = ir_create_inst_noval(&ira->new_irb, - impl_fn->child_scope, fn_proto_node->data.fn_proto.align_expr); - const_instruction->base.value = align_result; - - uint32_t align_bytes = 0; - ir_resolve_align(ira, &const_instruction->base, nullptr, &align_bytes); - impl_fn->align_bytes = align_bytes; - inst_fn_type_id.alignment = align_bytes; - } - - if (fn_proto_node->data.fn_proto.return_anytype_token == nullptr) { - AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type; - ZigType *specified_return_type = ir_analyze_type_expr(ira, impl_fn->child_scope, return_type_node); - if (type_is_invalid(specified_return_type)) - return ira->codegen->invalid_inst_gen; - - if(!is_valid_return_type(specified_return_type)){ - ErrorMsg *msg = ir_add_error(ira, source_instr, - buf_sprintf("call to generic function with %s return type '%s' not allowed", type_id_name(specified_return_type->id), buf_ptr(&specified_return_type->name))); - add_error_note(ira->codegen, msg, fn_proto_node, buf_sprintf("function declared here")); - - Tld *tld = find_decl(ira->codegen, &fn_entry->fndef_scope->base, &specified_return_type->name); - if (tld != nullptr) { - add_error_note(ira->codegen, msg, tld->source_node, buf_sprintf("type declared here")); - } - return ira->codegen->invalid_inst_gen; - } - - if (fn_proto_node->data.fn_proto.auto_err_set) { - ZigType *inferred_err_set_type = get_auto_err_set_type(ira->codegen, impl_fn); - if ((err = type_resolve(ira->codegen, specified_return_type, ResolveStatusSizeKnown))) - return ira->codegen->invalid_inst_gen; - inst_fn_type_id.return_type = get_error_union_type(ira->codegen, inferred_err_set_type, specified_return_type); - } else { - inst_fn_type_id.return_type = specified_return_type; - } - - switch (type_requires_comptime(ira->codegen, specified_return_type)) { - case ReqCompTimeYes: - // Throw out our work and call the function as if it were comptime. - return ir_analyze_fn_call(ira, source_instr, fn_entry, fn_type, fn_ref, first_arg_ptr, - first_arg_ptr_src, CallModifierCompileTime, new_stack, new_stack_src, is_async_call_builtin, - args_ptr, args_len, ret_ptr, call_result_loc); - case ReqCompTimeInvalid: - return ira->codegen->invalid_inst_gen; - case ReqCompTimeNo: - break; - } - } - - auto existing_entry = ira->codegen->generic_table.put_unique(generic_id, impl_fn); - if (existing_entry) { - // throw away all our work and use the existing function - impl_fn = existing_entry->value; - } else { - // finish instantiating the function - impl_fn->type_entry = get_fn_type(ira->codegen, &inst_fn_type_id); - if (type_is_invalid(impl_fn->type_entry)) - return ira->codegen->invalid_inst_gen; - - impl_fn->ir_executable->source_node = source_instr->source_node; - impl_fn->ir_executable->parent_exec = ira->new_irb.exec; - impl_fn->analyzed_executable.source_node = source_instr->source_node; - impl_fn->analyzed_executable.parent_exec = ira->new_irb.exec; - impl_fn->analyzed_executable.backward_branch_quota = ira->new_irb.exec->backward_branch_quota; - impl_fn->analyzed_executable.is_generic_instantiation = true; - - ira->codegen->fn_defs.append(impl_fn); - } - - FnTypeId *impl_fn_type_id = &impl_fn->type_entry->data.fn.fn_type_id; - - if (fn_type_can_fail(impl_fn_type_id)) { - parent_fn_entry->calls_or_awaits_errorable_fn = true; - } - - IrInstGen *casted_new_stack = analyze_casted_new_stack(ira, source_instr, new_stack, - new_stack_src, is_async_call_builtin, impl_fn); - if (casted_new_stack != nullptr && type_is_invalid(casted_new_stack->value->type)) - return ira->codegen->invalid_inst_gen; - - size_t impl_param_count = impl_fn_type_id->param_count; - if (modifier == CallModifierAsync) { - IrInstGen *result = ir_analyze_async_call(ira, source_instr, impl_fn, impl_fn->type_entry, - nullptr, casted_args, impl_param_count, casted_new_stack, is_async_call_builtin, ret_ptr, - call_result_loc); - return ir_finish_anal(ira, result); - } - - IrInstGen *result_loc; - if (handle_is_ptr(ira->codegen, impl_fn_type_id->return_type)) { - result_loc = ir_resolve_result(ira, source_instr, call_result_loc, - impl_fn_type_id->return_type, nullptr, true, false); - if (result_loc != nullptr) { - if (type_is_invalid(result_loc->value->type) || result_loc->value->type->id == ZigTypeIdUnreachable) { - return result_loc; - } - if (result_loc->value->type->data.pointer.is_const) { - ir_add_error(ira, source_instr, buf_sprintf("cannot assign to constant")); - return ira->codegen->invalid_inst_gen; - } - - IrInstGen *dummy_value = ir_const(ira, source_instr, impl_fn_type_id->return_type); - dummy_value->value->special = ConstValSpecialRuntime; - IrInstGen *dummy_result = ir_implicit_cast2(ira, source_instr, - dummy_value, result_loc->value->type->data.pointer.child_type); - if (type_is_invalid(dummy_result->value->type)) - return ira->codegen->invalid_inst_gen; - ZigType *res_child_type = result_loc->value->type->data.pointer.child_type; - if (res_child_type == ira->codegen->builtin_types.entry_anytype) { - res_child_type = impl_fn_type_id->return_type; - } - if (!handle_is_ptr(ira->codegen, res_child_type)) { - ir_reset_result(call_result_loc); - result_loc = nullptr; - } - } - } else if (is_async_call_builtin) { - result_loc = get_async_call_result_loc(ira, source_instr, impl_fn_type_id->return_type, - is_async_call_builtin, args_ptr, args_len, ret_ptr); - if (result_loc != nullptr && type_is_invalid(result_loc->value->type)) - return ira->codegen->invalid_inst_gen; - } else { - result_loc = nullptr; - } - - if (impl_fn_type_id->cc == CallingConventionAsync && - parent_fn_entry->inferred_async_node == nullptr && - modifier != CallModifierNoSuspend) - { - parent_fn_entry->inferred_async_node = fn_ref->base.source_node; - parent_fn_entry->inferred_async_fn = impl_fn; - } - - IrInstGenCall *new_call_instruction = ir_build_call_gen(ira, source_instr, - impl_fn, nullptr, impl_param_count, casted_args, modifier, casted_new_stack, - is_async_call_builtin, result_loc, impl_fn_type_id->return_type); - - if (get_scope_typeof(source_instr->scope) == nullptr) { - parent_fn_entry->call_list.append(new_call_instruction); - } - - return ir_finish_anal(ira, &new_call_instruction->base); - } - - ZigFn *parent_fn_entry = ira->new_irb.exec->fn_entry; - assert(fn_type_id->return_type != nullptr); - assert(parent_fn_entry != nullptr); - if (fn_type_can_fail(fn_type_id)) { - parent_fn_entry->calls_or_awaits_errorable_fn = true; - } - - - IrInstGen **casted_args = heap::c_allocator.allocate(call_param_count); - size_t next_arg_index = 0; - if (first_arg_ptr) { - assert(first_arg_ptr->value->type->id == ZigTypeIdPointer); - - ZigType *param_type = fn_type_id->param_info[next_arg_index].type; - if (type_is_invalid(param_type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *first_arg; - if (param_type->id == ZigTypeIdPointer) { - first_arg = first_arg_ptr; - } else { - first_arg = ir_get_deref(ira, &first_arg_ptr->base, first_arg_ptr, nullptr); - if (type_is_invalid(first_arg->value->type)) - return ira->codegen->invalid_inst_gen; - } - - IrInstGen *casted_arg = ir_implicit_cast2(ira, first_arg_ptr_src, first_arg, param_type); - if (type_is_invalid(casted_arg->value->type)) - return ira->codegen->invalid_inst_gen; - - casted_args[next_arg_index] = casted_arg; - next_arg_index += 1; - } - for (size_t call_i = 0; call_i < args_len; call_i += 1) { - IrInstGen *old_arg = args_ptr[call_i]; - if (type_is_invalid(old_arg->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *casted_arg; - if (next_arg_index < src_param_count) { - ZigType *param_type = fn_type_id->param_info[next_arg_index].type; - if (type_is_invalid(param_type)) - return ira->codegen->invalid_inst_gen; - casted_arg = ir_implicit_cast(ira, old_arg, param_type); - if (type_is_invalid(casted_arg->value->type)) - return ira->codegen->invalid_inst_gen; - } else { - casted_arg = old_arg; - } - - casted_args[next_arg_index] = casted_arg; - next_arg_index += 1; - } - - assert(next_arg_index == call_param_count); - - ZigType *return_type = fn_type_id->return_type; - if (type_is_invalid(return_type)) - return ira->codegen->invalid_inst_gen; - - if (fn_entry != nullptr && fn_entry->fn_inline == FnInlineAlways && modifier == CallModifierNeverInline) { - ir_add_error(ira, source_instr, - buf_sprintf("no-inline call of inline function")); - return ira->codegen->invalid_inst_gen; - } - - IrInstGen *casted_new_stack = analyze_casted_new_stack(ira, source_instr, new_stack, new_stack_src, - is_async_call_builtin, fn_entry); - if (casted_new_stack != nullptr && type_is_invalid(casted_new_stack->value->type)) - return ira->codegen->invalid_inst_gen; - - if (modifier == CallModifierAsync) { - IrInstGen *result = ir_analyze_async_call(ira, source_instr, fn_entry, fn_type, fn_ref, - casted_args, call_param_count, casted_new_stack, is_async_call_builtin, ret_ptr, call_result_loc); - return ir_finish_anal(ira, result); - } - - if (fn_type_id->cc == CallingConventionAsync && - parent_fn_entry->inferred_async_node == nullptr && - modifier != CallModifierNoSuspend) - { - parent_fn_entry->inferred_async_node = fn_ref->base.source_node; - parent_fn_entry->inferred_async_fn = fn_entry; - } - - IrInstGen *result_loc; - if (handle_is_ptr(ira->codegen, return_type)) { - result_loc = ir_resolve_result(ira, source_instr, call_result_loc, - return_type, nullptr, true, false); - if (result_loc != nullptr) { - if (type_is_invalid(result_loc->value->type) || result_loc->value->type->id == ZigTypeIdUnreachable) { - return result_loc; - } - if (result_loc->value->type->data.pointer.is_const) { - ir_add_error(ira, source_instr, buf_sprintf("cannot assign to constant")); - return ira->codegen->invalid_inst_gen; - } - - ZigType *expected_return_type = result_loc->value->type->data.pointer.child_type; - - IrInstGen *dummy_value = ir_const(ira, source_instr, return_type); - dummy_value->value->special = ConstValSpecialRuntime; - IrInstGen *dummy_result = ir_implicit_cast2(ira, source_instr, - dummy_value, expected_return_type); - if (type_is_invalid(dummy_result->value->type)) { - if ((return_type->id == ZigTypeIdErrorUnion || return_type->id == ZigTypeIdErrorSet) && - expected_return_type->id != ZigTypeIdErrorUnion && expected_return_type->id != ZigTypeIdErrorSet) - { - if (call_result_loc->id == ResultLocIdReturn) { - add_error_note(ira->codegen, ira->new_irb.exec->first_err_trace_msg, - ira->explicit_return_type_source_node, buf_sprintf("function cannot return an error")); - } else { - add_error_note(ira->codegen, ira->new_irb.exec->first_err_trace_msg, result_loc->base.source_node, - buf_sprintf("cannot store an error in type '%s'", buf_ptr(&expected_return_type->name))); - } - } - return ira->codegen->invalid_inst_gen; - } - if (expected_return_type == ira->codegen->builtin_types.entry_anytype) { - expected_return_type = return_type; - } - if (!handle_is_ptr(ira->codegen, expected_return_type)) { - ir_reset_result(call_result_loc); - result_loc = nullptr; - } - } - } else if (is_async_call_builtin) { - result_loc = get_async_call_result_loc(ira, source_instr, return_type, is_async_call_builtin, - args_ptr, args_len, ret_ptr); - if (result_loc != nullptr && type_is_invalid(result_loc->value->type)) - return ira->codegen->invalid_inst_gen; - } else { - result_loc = nullptr; - } - - IrInstGenCall *new_call_instruction = ir_build_call_gen(ira, source_instr, fn_entry, fn_ref, - call_param_count, casted_args, modifier, casted_new_stack, - is_async_call_builtin, result_loc, return_type); - if (get_scope_typeof(source_instr->scope) == nullptr) { - parent_fn_entry->call_list.append(new_call_instruction); - } - return ir_finish_anal(ira, &new_call_instruction->base); -} - -static IrInstGen *ir_analyze_fn_call_src(IrAnalyze *ira, IrInstSrcCall *call_instruction, - ZigFn *fn_entry, ZigType *fn_type, IrInstGen *fn_ref, - IrInstGen *first_arg_ptr, IrInst *first_arg_ptr_src, CallModifier modifier) -{ - IrInstGen *new_stack = nullptr; - IrInst *new_stack_src = nullptr; - if (call_instruction->new_stack) { - new_stack = call_instruction->new_stack->child; - if (type_is_invalid(new_stack->value->type)) - return ira->codegen->invalid_inst_gen; - new_stack_src = &call_instruction->new_stack->base; - } - IrInstGen **args_ptr = heap::c_allocator.allocate(call_instruction->arg_count); - for (size_t i = 0; i < call_instruction->arg_count; i += 1) { - args_ptr[i] = call_instruction->args[i]->child; - if (type_is_invalid(args_ptr[i]->value->type)) - return ira->codegen->invalid_inst_gen; - } - IrInstGen *ret_ptr = nullptr; - if (call_instruction->ret_ptr != nullptr) { - ret_ptr = call_instruction->ret_ptr->child; - if (type_is_invalid(ret_ptr->value->type)) - return ira->codegen->invalid_inst_gen; - } - IrInstGen *result = ir_analyze_fn_call(ira, &call_instruction->base.base, fn_entry, fn_type, fn_ref, - first_arg_ptr, first_arg_ptr_src, modifier, new_stack, new_stack_src, - call_instruction->is_async_call_builtin, args_ptr, call_instruction->arg_count, ret_ptr, - call_instruction->result_loc); - heap::c_allocator.deallocate(args_ptr, call_instruction->arg_count); - return result; -} - -static IrInstGen *ir_analyze_call_extra(IrAnalyze *ira, IrInst* source_instr, - IrInstSrc *pass1_options, IrInstSrc *pass1_fn_ref, IrInstGen **args_ptr, size_t args_len, - ResultLoc *result_loc) -{ - IrInstGen *options = pass1_options->child; - if (type_is_invalid(options->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *fn_ref = pass1_fn_ref->child; - if (type_is_invalid(fn_ref->value->type)) - return ira->codegen->invalid_inst_gen; - - TypeStructField *modifier_field = find_struct_type_field(options->value->type, buf_create_from_str("modifier")); - ir_assert(modifier_field != nullptr, source_instr); - IrInstGen *modifier_inst = ir_analyze_struct_value_field_value(ira, source_instr, options, modifier_field); - ZigValue *modifier_val = ir_resolve_const(ira, modifier_inst, UndefBad); - if (modifier_val == nullptr) - return ira->codegen->invalid_inst_gen; - CallModifier modifier = (CallModifier)bigint_as_u32(&modifier_val->data.x_enum_tag); - - if (ir_should_inline(ira->old_irb.exec, source_instr->scope)) { - switch (modifier) { - case CallModifierBuiltin: - zig_unreachable(); - case CallModifierAsync: - ir_add_error(ira, source_instr, buf_sprintf("TODO: comptime @call with async modifier")); - return ira->codegen->invalid_inst_gen; - case CallModifierCompileTime: - case CallModifierNone: - case CallModifierAlwaysInline: - case CallModifierAlwaysTail: - case CallModifierNoSuspend: - modifier = CallModifierCompileTime; - break; - case CallModifierNeverInline: - ir_add_error(ira, source_instr, - buf_sprintf("unable to perform 'never_inline' call at compile-time")); - return ira->codegen->invalid_inst_gen; - case CallModifierNeverTail: - ir_add_error(ira, source_instr, - buf_sprintf("unable to perform 'never_tail' call at compile-time")); - return ira->codegen->invalid_inst_gen; - } - } - - IrInstGen *first_arg_ptr = nullptr; - IrInst *first_arg_ptr_src = nullptr; - ZigFn *fn = nullptr; - if (instr_is_comptime(fn_ref)) { - if (fn_ref->value->type->id == ZigTypeIdBoundFn) { - assert(fn_ref->value->special == ConstValSpecialStatic); - fn = fn_ref->value->data.x_bound_fn.fn; - first_arg_ptr = fn_ref->value->data.x_bound_fn.first_arg; - first_arg_ptr_src = fn_ref->value->data.x_bound_fn.first_arg_src; - if (type_is_invalid(first_arg_ptr->value->type)) - return ira->codegen->invalid_inst_gen; - } else { - fn = ir_resolve_fn(ira, fn_ref); - } - } - - // Some modifiers require the callee to be comptime-known - switch (modifier) { - case CallModifierCompileTime: - case CallModifierAlwaysInline: - case CallModifierAsync: - if (fn == nullptr) { - ir_add_error(ira, &modifier_inst->base, - buf_sprintf("the specified modifier requires a comptime-known function")); - return ira->codegen->invalid_inst_gen; - } - ZIG_FALLTHROUGH; - default: - break; - } - - ZigType *fn_type = (fn != nullptr) ? fn->type_entry : fn_ref->value->type; - - TypeStructField *stack_field = find_struct_type_field(options->value->type, buf_create_from_str("stack")); - ir_assert(stack_field != nullptr, source_instr); - IrInstGen *opt_stack = ir_analyze_struct_value_field_value(ira, source_instr, options, stack_field); - if (type_is_invalid(opt_stack->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *stack_is_non_null_inst = ir_analyze_test_non_null(ira, source_instr, opt_stack); - bool stack_is_non_null; - if (!ir_resolve_bool(ira, stack_is_non_null_inst, &stack_is_non_null)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *stack = nullptr; - IrInst *stack_src = nullptr; - if (stack_is_non_null) { - stack = ir_analyze_optional_value_payload_value(ira, source_instr, opt_stack, false); - if (type_is_invalid(stack->value->type)) - return ira->codegen->invalid_inst_gen; - stack_src = &stack->base; - } - - return ir_analyze_fn_call(ira, source_instr, fn, fn_type, fn_ref, first_arg_ptr, first_arg_ptr_src, - modifier, stack, stack_src, false, args_ptr, args_len, nullptr, result_loc); -} - -static IrInstGen *ir_analyze_async_call_extra(IrAnalyze *ira, IrInst* source_instr, CallModifier modifier, - IrInstSrc *pass1_fn_ref, IrInstSrc *ret_ptr, IrInstSrc *new_stack, IrInstGen **args_ptr, size_t args_len, ResultLoc *result_loc) -{ - IrInstGen *fn_ref = pass1_fn_ref->child; - if (type_is_invalid(fn_ref->value->type)) - return ira->codegen->invalid_inst_gen; - - if (ir_should_inline(ira->old_irb.exec, source_instr->scope)) { - ir_add_error(ira, source_instr, buf_sprintf("TODO: comptime @asyncCall")); - return ira->codegen->invalid_inst_gen; - } - - IrInstGen *first_arg_ptr = nullptr; - IrInst *first_arg_ptr_src = nullptr; - ZigFn *fn = nullptr; - if (instr_is_comptime(fn_ref)) { - if (fn_ref->value->type->id == ZigTypeIdBoundFn) { - assert(fn_ref->value->special == ConstValSpecialStatic); - fn = fn_ref->value->data.x_bound_fn.fn; - first_arg_ptr = fn_ref->value->data.x_bound_fn.first_arg; - first_arg_ptr_src = fn_ref->value->data.x_bound_fn.first_arg_src; - if (type_is_invalid(first_arg_ptr->value->type)) - return ira->codegen->invalid_inst_gen; - } else { - fn = ir_resolve_fn(ira, fn_ref); - } - } - - IrInstGen *ret_ptr_uncasted = nullptr; - if (ret_ptr != nullptr) { - ret_ptr_uncasted = ret_ptr->child; - if (type_is_invalid(ret_ptr_uncasted->value->type)) - return ira->codegen->invalid_inst_gen; - } - - ZigType *fn_type = (fn != nullptr) ? fn->type_entry : fn_ref->value->type; - IrInstGen *casted_new_stack = analyze_casted_new_stack(ira, source_instr, new_stack->child, - &new_stack->base, true, fn); - if (casted_new_stack != nullptr && type_is_invalid(casted_new_stack->value->type)) - return ira->codegen->invalid_inst_gen; - - return ir_analyze_fn_call(ira, source_instr, fn, fn_type, fn_ref, first_arg_ptr, first_arg_ptr_src, - modifier, casted_new_stack, &new_stack->base, true, args_ptr, args_len, ret_ptr_uncasted, result_loc); -} - -static bool ir_extract_tuple_call_args(IrAnalyze *ira, IrInst *source_instr, IrInstGen *args, IrInstGen ***args_ptr, size_t *args_len) { - ZigType *args_type = args->value->type; - if (type_is_invalid(args_type)) - return false; - - if (args_type->id != ZigTypeIdStruct) { - ir_add_error(ira, &args->base, - buf_sprintf("expected tuple or struct, found '%s'", buf_ptr(&args_type->name))); - return false; - } - - if (is_tuple(args_type)) { - *args_len = args_type->data.structure.src_field_count; - *args_ptr = heap::c_allocator.allocate(*args_len); - for (size_t i = 0; i < *args_len; i += 1) { - TypeStructField *arg_field = args_type->data.structure.fields[i]; - (*args_ptr)[i] = ir_analyze_struct_value_field_value(ira, source_instr, args, arg_field); - if (type_is_invalid((*args_ptr)[i]->value->type)) - return false; - } - } else { - ir_add_error(ira, &args->base, buf_sprintf("TODO: struct args")); - return false; - } - return true; -} - -static IrInstGen *ir_analyze_instruction_call_extra(IrAnalyze *ira, IrInstSrcCallExtra *instruction) { - IrInstGen *args = instruction->args->child; - IrInstGen **args_ptr = nullptr; - size_t args_len = 0; - if (!ir_extract_tuple_call_args(ira, &instruction->base.base, args, &args_ptr, &args_len)) { - return ira->codegen->invalid_inst_gen; - } - - IrInstGen *result = ir_analyze_call_extra(ira, &instruction->base.base, instruction->options, - instruction->fn_ref, args_ptr, args_len, instruction->result_loc); - heap::c_allocator.deallocate(args_ptr, args_len); - return result; -} - -static IrInstGen *ir_analyze_instruction_async_call_extra(IrAnalyze *ira, IrInstSrcAsyncCallExtra *instruction) { - IrInstGen *args = instruction->args->child; - IrInstGen **args_ptr = nullptr; - size_t args_len = 0; - if (!ir_extract_tuple_call_args(ira, &instruction->base.base, args, &args_ptr, &args_len)) { - return ira->codegen->invalid_inst_gen; - } - - IrInstGen *result = ir_analyze_async_call_extra(ira, &instruction->base.base, instruction->modifier, - instruction->fn_ref, instruction->ret_ptr, instruction->new_stack, args_ptr, args_len, instruction->result_loc); - heap::c_allocator.deallocate(args_ptr, args_len); - return result; -} - -static IrInstGen *ir_analyze_instruction_call_args(IrAnalyze *ira, IrInstSrcCallArgs *instruction) { - IrInstGen **args_ptr = heap::c_allocator.allocate(instruction->args_len); - for (size_t i = 0; i < instruction->args_len; i += 1) { - args_ptr[i] = instruction->args_ptr[i]->child; - if (type_is_invalid(args_ptr[i]->value->type)) - return ira->codegen->invalid_inst_gen; - } - - IrInstGen *result = ir_analyze_call_extra(ira, &instruction->base.base, instruction->options, - instruction->fn_ref, args_ptr, instruction->args_len, instruction->result_loc); - heap::c_allocator.deallocate(args_ptr, instruction->args_len); - return result; -} - -static IrInstGen *ir_analyze_instruction_call(IrAnalyze *ira, IrInstSrcCall *call_instruction) { - IrInstGen *fn_ref = call_instruction->fn_ref->child; - if (type_is_invalid(fn_ref->value->type)) - return ira->codegen->invalid_inst_gen; - - bool is_comptime = (call_instruction->modifier == CallModifierCompileTime) || - ir_should_inline(ira->old_irb.exec, call_instruction->base.base.scope); - CallModifier modifier = is_comptime ? CallModifierCompileTime : call_instruction->modifier; - - if (is_comptime || instr_is_comptime(fn_ref)) { - if (fn_ref->value->type->id == ZigTypeIdMetaType) { - ZigType *ty = ir_resolve_type(ira, fn_ref); - if (ty == nullptr) - return ira->codegen->invalid_inst_gen; - ErrorMsg *msg = ir_add_error(ira, &fn_ref->base, - buf_sprintf("type '%s' not a function", buf_ptr(&ty->name))); - add_error_note(ira->codegen, msg, call_instruction->base.base.source_node, - buf_sprintf("use @as builtin for type coercion")); - return ira->codegen->invalid_inst_gen; - } else if (fn_ref->value->type->id == ZigTypeIdFn) { - ZigFn *fn_table_entry = ir_resolve_fn(ira, fn_ref); - ZigType *fn_type = fn_table_entry ? fn_table_entry->type_entry : fn_ref->value->type; - CallModifier modifier = is_comptime ? CallModifierCompileTime : call_instruction->modifier; - return ir_analyze_fn_call_src(ira, call_instruction, fn_table_entry, fn_type, - fn_ref, nullptr, nullptr, modifier); - } else if (fn_ref->value->type->id == ZigTypeIdBoundFn) { - assert(fn_ref->value->special == ConstValSpecialStatic); - ZigFn *fn_table_entry = fn_ref->value->data.x_bound_fn.fn; - IrInstGen *first_arg_ptr = fn_ref->value->data.x_bound_fn.first_arg; - IrInst *first_arg_ptr_src = fn_ref->value->data.x_bound_fn.first_arg_src; - CallModifier modifier = is_comptime ? CallModifierCompileTime : call_instruction->modifier; - return ir_analyze_fn_call_src(ira, call_instruction, fn_table_entry, fn_table_entry->type_entry, - fn_ref, first_arg_ptr, first_arg_ptr_src, modifier); - } else { - ir_add_error(ira, &fn_ref->base, - buf_sprintf("type '%s' not a function", buf_ptr(&fn_ref->value->type->name))); - return ira->codegen->invalid_inst_gen; - } - } - - if (fn_ref->value->type->id == ZigTypeIdFn) { - return ir_analyze_fn_call_src(ira, call_instruction, nullptr, fn_ref->value->type, - fn_ref, nullptr, nullptr, modifier); - } else { - ir_add_error(ira, &fn_ref->base, - buf_sprintf("type '%s' not a function", buf_ptr(&fn_ref->value->type->name))); - return ira->codegen->invalid_inst_gen; - } -} - -// out_val->type must be the type to read the pointer as -// if the type is different than the actual type then it does a comptime byte reinterpretation -static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, - ZigValue *out_val, ZigValue *ptr_val) -{ - Error err; - assert(out_val->type != nullptr); - - ZigValue *pointee = const_ptr_pointee_unchecked(codegen, ptr_val); - src_assert(pointee->type != nullptr, source_node); - - if ((err = type_resolve(codegen, pointee->type, ResolveStatusSizeKnown))) - return ErrorSemanticAnalyzeFail; - if ((err = type_resolve(codegen, out_val->type, ResolveStatusSizeKnown))) - return ErrorSemanticAnalyzeFail; - - size_t src_size = type_size(codegen, pointee->type); - size_t dst_size = type_size(codegen, out_val->type); - - if (dst_size <= src_size) { - if (src_size == dst_size && types_have_same_zig_comptime_repr(codegen, out_val->type, pointee->type)) { - copy_const_val(codegen, out_val, pointee); - return ErrorNone; - } - Buf buf = BUF_INIT; - buf_resize(&buf, src_size); - buf_write_value_bytes(codegen, (uint8_t*)buf_ptr(&buf), pointee); - if ((err = buf_read_value_bytes(ira, codegen, source_node, (uint8_t*)buf_ptr(&buf), out_val))) - return err; - buf_deinit(&buf); - return ErrorNone; - } - - switch (ptr_val->data.x_ptr.special) { - case ConstPtrSpecialInvalid: - zig_unreachable(); - case ConstPtrSpecialNull: - if (dst_size == 0) - return ErrorNone; - opt_ir_add_error_node(ira, codegen, source_node, - buf_sprintf("attempt to read %" ZIG_PRI_usize " bytes from null pointer", - dst_size)); - return ErrorSemanticAnalyzeFail; - case ConstPtrSpecialRef: { - opt_ir_add_error_node(ira, codegen, source_node, - buf_sprintf("attempt to read %" ZIG_PRI_usize " bytes from pointer to %s which is %" ZIG_PRI_usize " bytes", - dst_size, buf_ptr(&pointee->type->name), src_size)); - return ErrorSemanticAnalyzeFail; - } - case ConstPtrSpecialSubArray: { - ZigValue *array_val = ptr_val->data.x_ptr.data.base_array.array_val; - assert(array_val->type->id == ZigTypeIdArray); - if (array_val->data.x_array.special != ConstArraySpecialNone) - zig_panic("TODO"); - if (dst_size > src_size) { - size_t elem_index = ptr_val->data.x_ptr.data.base_array.elem_index; - opt_ir_add_error_node(ira, codegen, source_node, - buf_sprintf("attempt to read %" ZIG_PRI_usize " bytes from %s at index %" ZIG_PRI_usize " which is %" ZIG_PRI_usize " bytes", - dst_size, buf_ptr(&array_val->type->name), elem_index, src_size)); - return ErrorSemanticAnalyzeFail; - } - size_t elem_size = src_size; - size_t elem_count = (dst_size % elem_size == 0) ? (dst_size / elem_size) : (dst_size / elem_size + 1); - Buf buf = BUF_INIT; - buf_resize(&buf, elem_count * elem_size); - for (size_t i = 0; i < elem_count; i += 1) { - ZigValue *elem_val = &array_val->data.x_array.data.s_none.elements[i]; - buf_write_value_bytes(codegen, (uint8_t*)buf_ptr(&buf) + (i * elem_size), elem_val); - } - if ((err = buf_read_value_bytes(ira, codegen, source_node, (uint8_t*)buf_ptr(&buf), out_val))) - return err; - buf_deinit(&buf); - return ErrorNone; - } - case ConstPtrSpecialBaseArray: { - ZigValue *array_val = ptr_val->data.x_ptr.data.base_array.array_val; - assert(array_val->type->id == ZigTypeIdArray); - if (array_val->data.x_array.special != ConstArraySpecialNone) - zig_panic("TODO"); - size_t elem_size = src_size; - size_t elem_index = ptr_val->data.x_ptr.data.base_array.elem_index; - src_size = elem_size * (array_val->type->data.array.len - elem_index); - if (dst_size > src_size) { - opt_ir_add_error_node(ira, codegen, source_node, - buf_sprintf("attempt to read %" ZIG_PRI_usize " bytes from %s at index %" ZIG_PRI_usize " which is %" ZIG_PRI_usize " bytes", - dst_size, buf_ptr(&array_val->type->name), elem_index, src_size)); - return ErrorSemanticAnalyzeFail; - } - size_t elem_count = (dst_size % elem_size == 0) ? (dst_size / elem_size) : (dst_size / elem_size + 1); - Buf buf = BUF_INIT; - buf_resize(&buf, elem_count * elem_size); - for (size_t i = 0; i < elem_count; i += 1) { - ZigValue *elem_val = &array_val->data.x_array.data.s_none.elements[elem_index + i]; - buf_write_value_bytes(codegen, (uint8_t*)buf_ptr(&buf) + (i * elem_size), elem_val); - } - if ((err = buf_read_value_bytes(ira, codegen, source_node, (uint8_t*)buf_ptr(&buf), out_val))) - return err; - buf_deinit(&buf); - return ErrorNone; - } - case ConstPtrSpecialBaseStruct: - case ConstPtrSpecialBaseErrorUnionCode: - case ConstPtrSpecialBaseErrorUnionPayload: - case ConstPtrSpecialBaseOptionalPayload: - case ConstPtrSpecialDiscard: - case ConstPtrSpecialHardCodedAddr: - case ConstPtrSpecialFunction: - zig_panic("TODO"); - } - zig_unreachable(); -} - -static IrInstGen *ir_analyze_optional_type(IrAnalyze *ira, IrInstSrcUnOp *instruction) { - IrInstGen *result = ir_const(ira, &instruction->base.base, ira->codegen->builtin_types.entry_type); - result->value->special = ConstValSpecialLazy; - - LazyValueOptType *lazy_opt_type = heap::c_allocator.create(); - lazy_opt_type->ira = ira; ira_ref(ira); - result->value->data.x_lazy = &lazy_opt_type->base; - lazy_opt_type->base.id = LazyValueIdOptType; - - lazy_opt_type->payload_type = instruction->value->child; - if (ir_resolve_type_lazy(ira, lazy_opt_type->payload_type) == nullptr) - return ira->codegen->invalid_inst_gen; - - return result; -} - -static ErrorMsg *ir_eval_negation_scalar(IrAnalyze *ira, IrInst* source_instr, ZigType *scalar_type, - ZigValue *operand_val, ZigValue *scalar_out_val, bool is_wrap_op) -{ - bool is_float = (scalar_type->id == ZigTypeIdFloat || scalar_type->id == ZigTypeIdComptimeFloat); - - bool ok_type = ((scalar_type->id == ZigTypeIdInt && scalar_type->data.integral.is_signed) || - scalar_type->id == ZigTypeIdComptimeInt || (is_float && !is_wrap_op)); - - if (!ok_type) { - const char *fmt = is_wrap_op ? "invalid wrapping negation type: '%s'" : "invalid negation type: '%s'"; - return ir_add_error(ira, source_instr, buf_sprintf(fmt, buf_ptr(&scalar_type->name))); - } - - if (is_float) { - float_negate(scalar_out_val, operand_val); - } else if (is_wrap_op) { - bigint_negate_wrap(&scalar_out_val->data.x_bigint, &operand_val->data.x_bigint, - scalar_type->data.integral.bit_count); - } else { - bigint_negate(&scalar_out_val->data.x_bigint, &operand_val->data.x_bigint); - } - - scalar_out_val->type = scalar_type; - scalar_out_val->special = ConstValSpecialStatic; - - if (is_wrap_op || is_float || scalar_type->id == ZigTypeIdComptimeInt) { - return nullptr; - } - - if (!bigint_fits_in_bits(&scalar_out_val->data.x_bigint, scalar_type->data.integral.bit_count, true)) { - return ir_add_error(ira, source_instr, buf_sprintf("negation caused overflow")); - } - return nullptr; -} - -static IrInstGen *ir_analyze_negation(IrAnalyze *ira, IrInstSrcUnOp *instruction) { - IrInstGen *value = instruction->value->child; - ZigType *expr_type = value->value->type; - if (type_is_invalid(expr_type)) - return ira->codegen->invalid_inst_gen; - - bool is_wrap_op = (instruction->op_id == IrUnOpNegationWrap); - - switch (expr_type->id) { - case ZigTypeIdComptimeInt: - case ZigTypeIdFloat: - case ZigTypeIdComptimeFloat: - case ZigTypeIdVector: - break; - case ZigTypeIdInt: - if (is_wrap_op || expr_type->data.integral.is_signed) - break; - ZIG_FALLTHROUGH; - default: - ir_add_error(ira, &instruction->base.base, - buf_sprintf("negation of type '%s'", buf_ptr(&expr_type->name))); - return ira->codegen->invalid_inst_gen; - } - - ZigType *scalar_type = (expr_type->id == ZigTypeIdVector) ? expr_type->data.vector.elem_type : expr_type; - - if (instr_is_comptime(value)) { - ZigValue *operand_val = ir_resolve_const(ira, value, UndefBad); - if (!operand_val) - return ira->codegen->invalid_inst_gen; - - IrInstGen *result_instruction = ir_const(ira, &instruction->base.base, expr_type); - ZigValue *out_val = result_instruction->value; - if (expr_type->id == ZigTypeIdVector) { - expand_undef_array(ira->codegen, operand_val); - out_val->special = ConstValSpecialUndef; - expand_undef_array(ira->codegen, out_val); - size_t len = expr_type->data.vector.len; - for (size_t i = 0; i < len; i += 1) { - ZigValue *scalar_operand_val = &operand_val->data.x_array.data.s_none.elements[i]; - ZigValue *scalar_out_val = &out_val->data.x_array.data.s_none.elements[i]; - assert(scalar_operand_val->type == scalar_type); - assert(scalar_out_val->type == scalar_type); - ErrorMsg *msg = ir_eval_negation_scalar(ira, &instruction->base.base, scalar_type, - scalar_operand_val, scalar_out_val, is_wrap_op); - if (msg != nullptr) { - add_error_note(ira->codegen, msg, instruction->base.base.source_node, - buf_sprintf("when computing vector element at index %" ZIG_PRI_usize, i)); - return ira->codegen->invalid_inst_gen; - } - } - out_val->type = expr_type; - out_val->special = ConstValSpecialStatic; - } else { - if (ir_eval_negation_scalar(ira, &instruction->base.base, scalar_type, operand_val, out_val, - is_wrap_op) != nullptr) - { - return ira->codegen->invalid_inst_gen; - } - } - return result_instruction; - } - - if (is_wrap_op) { - return ir_build_negation_wrapping(ira, &instruction->base.base, value, expr_type); - } else { - return ir_build_negation(ira, &instruction->base.base, value, expr_type); - } -} - -static IrInstGen *ir_analyze_bin_not(IrAnalyze *ira, IrInstSrcUnOp *instruction) { - IrInstGen *value = instruction->value->child; - ZigType *expr_type = value->value->type; - if (type_is_invalid(expr_type)) - return ira->codegen->invalid_inst_gen; - - ZigType *scalar_type = (expr_type->id == ZigTypeIdVector) ? - expr_type->data.vector.elem_type : expr_type; - - if (scalar_type->id != ZigTypeIdInt) { - ir_add_error(ira, &instruction->base.base, - buf_sprintf("unable to perform binary not operation on type '%s'", buf_ptr(&expr_type->name))); - return ira->codegen->invalid_inst_gen; - } - - if (instr_is_comptime(value)) { - ZigValue *expr_val = ir_resolve_const(ira, value, UndefBad); - if (expr_val == nullptr) - return ira->codegen->invalid_inst_gen; - - IrInstGen *result = ir_const(ira, &instruction->base.base, expr_type); - - if (expr_type->id == ZigTypeIdVector) { - expand_undef_array(ira->codegen, expr_val); - result->value->special = ConstValSpecialUndef; - expand_undef_array(ira->codegen, result->value); - - for (size_t i = 0; i < expr_type->data.vector.len; i++) { - ZigValue *src_val = &expr_val->data.x_array.data.s_none.elements[i]; - ZigValue *dst_val = &result->value->data.x_array.data.s_none.elements[i]; - - dst_val->type = scalar_type; - dst_val->special = ConstValSpecialStatic; - bigint_not(&dst_val->data.x_bigint, &src_val->data.x_bigint, - scalar_type->data.integral.bit_count, scalar_type->data.integral.is_signed); - } - } else { - bigint_not(&result->value->data.x_bigint, &expr_val->data.x_bigint, - scalar_type->data.integral.bit_count, scalar_type->data.integral.is_signed); - } - - return result; - } - - return ir_build_binary_not(ira, &instruction->base.base, value, expr_type); -} - -static IrInstGen *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstSrcUnOp *instruction) { - IrUnOp op_id = instruction->op_id; - switch (op_id) { - case IrUnOpInvalid: - zig_unreachable(); - case IrUnOpBinNot: - return ir_analyze_bin_not(ira, instruction); - case IrUnOpNegation: - case IrUnOpNegationWrap: - return ir_analyze_negation(ira, instruction); - case IrUnOpDereference: { - IrInstGen *ptr = instruction->value->child; - if (type_is_invalid(ptr->value->type)) - return ira->codegen->invalid_inst_gen; - ZigType *ptr_type = ptr->value->type; - if (ptr_type->id == ZigTypeIdPointer && ptr_type->data.pointer.ptr_len == PtrLenUnknown) { - ir_add_error_node(ira, instruction->base.base.source_node, - buf_sprintf("index syntax required for unknown-length pointer type '%s'", - buf_ptr(&ptr_type->name))); - return ira->codegen->invalid_inst_gen; - } - - IrInstGen *result = ir_get_deref(ira, &instruction->base.base, ptr, instruction->result_loc); - if (type_is_invalid(result->value->type)) - return ira->codegen->invalid_inst_gen; - - // If the result needs to be an lvalue, type check it - if (instruction->lval != LValNone && result->value->type->id != ZigTypeIdPointer) { - ir_add_error(ira, &instruction->base.base, - buf_sprintf("attempt to dereference non-pointer type '%s'", buf_ptr(&result->value->type->name))); - return ira->codegen->invalid_inst_gen; - } - - return result; - } - case IrUnOpOptional: - return ir_analyze_optional_type(ira, instruction); - } - zig_unreachable(); -} - -static void ir_push_resume(IrAnalyze *ira, IrSuspendPosition pos) { - IrBasicBlockSrc *old_bb = ira->old_irb.exec->basic_block_list.at(pos.basic_block_index); - if (old_bb->in_resume_stack) return; - ira->resume_stack.append(pos); - old_bb->in_resume_stack = true; -} - -static void ir_push_resume_block(IrAnalyze *ira, IrBasicBlockSrc *old_bb) { - if (ira->resume_stack.length != 0) { - ir_push_resume(ira, {old_bb->index, 0}); - } -} - -static IrInstGen *ir_analyze_instruction_br(IrAnalyze *ira, IrInstSrcBr *br_instruction) { - IrBasicBlockSrc *old_dest_block = br_instruction->dest_block; - - bool is_comptime; - if (!ir_resolve_comptime(ira, br_instruction->is_comptime->child, &is_comptime)) - return ir_unreach_error(ira); - - if (is_comptime || (old_dest_block->ref_count == 1 && old_dest_block->suspend_instruction_ref == nullptr)) - return ir_inline_bb(ira, &br_instruction->base.base, old_dest_block); - - IrBasicBlockGen *new_bb = ir_get_new_bb_runtime(ira, old_dest_block, &br_instruction->base.base); - if (new_bb == nullptr) - return ir_unreach_error(ira); - - ir_push_resume_block(ira, old_dest_block); - - IrInstGen *result = ir_build_br_gen(ira, &br_instruction->base.base, new_bb); - return ir_finish_anal(ira, result); -} - -static IrInstGen *ir_analyze_instruction_cond_br(IrAnalyze *ira, IrInstSrcCondBr *cond_br_instruction) { - IrInstGen *condition = cond_br_instruction->condition->child; - if (type_is_invalid(condition->value->type)) - return ir_unreach_error(ira); - - bool is_comptime; - if (!ir_resolve_comptime(ira, cond_br_instruction->is_comptime->child, &is_comptime)) - return ir_unreach_error(ira); - - ZigType *bool_type = ira->codegen->builtin_types.entry_bool; - IrInstGen *casted_condition = ir_implicit_cast(ira, condition, bool_type); - if (type_is_invalid(casted_condition->value->type)) - return ir_unreach_error(ira); - - if (is_comptime || instr_is_comptime(casted_condition)) { - bool cond_is_true; - if (!ir_resolve_bool(ira, casted_condition, &cond_is_true)) - return ir_unreach_error(ira); - - IrBasicBlockSrc *old_dest_block = cond_is_true ? - cond_br_instruction->then_block : cond_br_instruction->else_block; - - if (is_comptime || (old_dest_block->ref_count == 1 && old_dest_block->suspend_instruction_ref == nullptr)) - return ir_inline_bb(ira, &cond_br_instruction->base.base, old_dest_block); - - IrBasicBlockGen *new_dest_block = ir_get_new_bb_runtime(ira, old_dest_block, &cond_br_instruction->base.base); - if (new_dest_block == nullptr) - return ir_unreach_error(ira); - - ir_push_resume_block(ira, old_dest_block); - - IrInstGen *result = ir_build_br_gen(ira, &cond_br_instruction->base.base, new_dest_block); - return ir_finish_anal(ira, result); - } - - assert(cond_br_instruction->then_block != cond_br_instruction->else_block); - IrBasicBlockGen *new_then_block = ir_get_new_bb_runtime(ira, cond_br_instruction->then_block, &cond_br_instruction->base.base); - if (new_then_block == nullptr) - return ir_unreach_error(ira); - - IrBasicBlockGen *new_else_block = ir_get_new_bb_runtime(ira, cond_br_instruction->else_block, &cond_br_instruction->base.base); - if (new_else_block == nullptr) - return ir_unreach_error(ira); - - ir_push_resume_block(ira, cond_br_instruction->else_block); - ir_push_resume_block(ira, cond_br_instruction->then_block); - - IrInstGen *result = ir_build_cond_br_gen(ira, &cond_br_instruction->base.base, - casted_condition, new_then_block, new_else_block); - return ir_finish_anal(ira, result); -} - -static IrInstGen *ir_analyze_instruction_unreachable(IrAnalyze *ira, - IrInstSrcUnreachable *unreachable_instruction) -{ - IrInstGen *result = ir_build_unreachable_gen(ira, &unreachable_instruction->base.base); - return ir_finish_anal(ira, result); -} - -static IrInstGen *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstSrcPhi *phi_instruction) { - Error err; - - if (ira->const_predecessor_bb) { - for (size_t i = 0; i < phi_instruction->incoming_count; i += 1) { - IrBasicBlockSrc *predecessor = phi_instruction->incoming_blocks[i]; - if (predecessor != ira->const_predecessor_bb) - continue; - IrInstGen *value = phi_instruction->incoming_values[i]->child; - assert(value->value->type); - if (type_is_invalid(value->value->type)) - return ira->codegen->invalid_inst_gen; - - if (value->value->special != ConstValSpecialRuntime) { - IrInstGen *result = ir_const(ira, &phi_instruction->base.base, nullptr); - copy_const_val(ira->codegen, result->value, value->value); - return result; - } else { - return value; - } - } - zig_unreachable(); - } - - ResultLocPeerParent *peer_parent = phi_instruction->peer_parent; - if (peer_parent != nullptr && !peer_parent->skipped && !peer_parent->done_resuming && - peer_parent->peers.length >= 2) - { - if (peer_parent->resolved_type == nullptr) { - IrInstGen **instructions = heap::c_allocator.allocate(peer_parent->peers.length); - for (size_t i = 0; i < peer_parent->peers.length; i += 1) { - ResultLocPeer *this_peer = peer_parent->peers.at(i); - - IrInstGen *gen_instruction = this_peer->base.gen_instruction; - if (gen_instruction == nullptr) { - // unreachable instructions will cause implicit_elem_type to be null - if (this_peer->base.implicit_elem_type == nullptr) { - instructions[i] = ir_const_unreachable(ira, &this_peer->base.source_instruction->base); - } else { - instructions[i] = ir_const(ira, &this_peer->base.source_instruction->base, - this_peer->base.implicit_elem_type); - instructions[i]->value->special = ConstValSpecialRuntime; - } - } else { - instructions[i] = gen_instruction; - } - - } - ZigType *expected_type = ir_result_loc_expected_type(ira, &phi_instruction->base.base, peer_parent->parent); - peer_parent->resolved_type = ir_resolve_peer_types(ira, - peer_parent->base.source_instruction->base.source_node, expected_type, instructions, - peer_parent->peers.length); - if (type_is_invalid(peer_parent->resolved_type)) - return ira->codegen->invalid_inst_gen; - - // the logic below assumes there are no instructions in the new current basic block yet - ir_assert(ira->new_irb.current_basic_block->instruction_list.length == 0, &phi_instruction->base.base); - - // In case resolving the parent activates a suspend, do it now - IrInstGen *parent_result_loc = ir_resolve_result(ira, &phi_instruction->base.base, peer_parent->parent, - peer_parent->resolved_type, nullptr, false, true); - if (parent_result_loc != nullptr && - (type_is_invalid(parent_result_loc->value->type) || parent_result_loc->value->type->id == ZigTypeIdUnreachable)) - { - return parent_result_loc; - } - // If the above code generated any instructions in the current basic block, we need - // to move them to the peer parent predecessor. - ZigList instrs_to_move = {}; - while (ira->new_irb.current_basic_block->instruction_list.length != 0) { - instrs_to_move.append(ira->new_irb.current_basic_block->instruction_list.pop()); - } - if (instrs_to_move.length != 0) { - IrBasicBlockGen *predecessor = peer_parent->base.source_instruction->child->owner_bb; - IrInstGen *branch_instruction = predecessor->instruction_list.pop(); - ir_assert(branch_instruction->value->type->id == ZigTypeIdUnreachable, &phi_instruction->base.base); - while (instrs_to_move.length != 0) { - predecessor->instruction_list.append(instrs_to_move.pop()); - } - predecessor->instruction_list.append(branch_instruction); - } - } - - IrSuspendPosition suspend_pos; - ira_suspend(ira, &phi_instruction->base.base, nullptr, &suspend_pos); - ir_push_resume(ira, suspend_pos); - - for (size_t i = 0; i < peer_parent->peers.length; i += 1) { - ResultLocPeer *opposite_peer = peer_parent->peers.at(peer_parent->peers.length - i - 1); - if (opposite_peer->base.implicit_elem_type != nullptr && - opposite_peer->base.implicit_elem_type->id != ZigTypeIdUnreachable) - { - ir_push_resume(ira, opposite_peer->suspend_pos); - } - } - - peer_parent->done_resuming = true; - return ira_resume(ira); - } - - ZigList new_incoming_blocks = {0}; - ZigList new_incoming_values = {0}; - - for (size_t i = 0; i < phi_instruction->incoming_count; i += 1) { - IrBasicBlockSrc *predecessor = phi_instruction->incoming_blocks[i]; - if (predecessor->ref_count == 0) - continue; - - - IrInstSrc *old_value = phi_instruction->incoming_values[i]; - assert(old_value); - IrInstGen *new_value = old_value->child; - if (!new_value || new_value->value->type->id == ZigTypeIdUnreachable || predecessor->child == nullptr) - continue; - - if (type_is_invalid(new_value->value->type)) - return ira->codegen->invalid_inst_gen; - - - assert(predecessor->child); - new_incoming_blocks.append(predecessor->child); - new_incoming_values.append(new_value); - } - - if (new_incoming_blocks.length == 0) { - IrInstGen *result = ir_build_unreachable_gen(ira, &phi_instruction->base.base); - return ir_finish_anal(ira, result); - } - - if (new_incoming_blocks.length == 1) { - return new_incoming_values.at(0); - } - - ZigType *resolved_type = nullptr; - if (peer_parent != nullptr) { - bool peer_parent_has_type; - if ((err = ir_result_has_type(ira, peer_parent->parent, &peer_parent_has_type))) - return ira->codegen->invalid_inst_gen; - if (peer_parent_has_type) { - if (peer_parent->parent->id == ResultLocIdReturn) { - resolved_type = ira->explicit_return_type; - } else if (peer_parent->parent->id == ResultLocIdCast) { - resolved_type = ir_resolve_type(ira, peer_parent->parent->source_instruction->child); - } else if (peer_parent->parent->resolved_loc) { - ZigType *resolved_loc_ptr_type = peer_parent->parent->resolved_loc->value->type; - ir_assert(resolved_loc_ptr_type->id == ZigTypeIdPointer, &phi_instruction->base.base); - resolved_type = resolved_loc_ptr_type->data.pointer.child_type; - } - - if (resolved_type != nullptr && type_is_invalid(resolved_type)) - return ira->codegen->invalid_inst_gen; - } - } - - if (resolved_type == nullptr) { - resolved_type = ir_resolve_peer_types(ira, phi_instruction->base.base.source_node, nullptr, - new_incoming_values.items, new_incoming_values.length); - if (type_is_invalid(resolved_type)) - return ira->codegen->invalid_inst_gen; - } - - switch (type_has_one_possible_value(ira->codegen, resolved_type)) { - case OnePossibleValueInvalid: - return ira->codegen->invalid_inst_gen; - case OnePossibleValueYes: - return ir_const_move(ira, &phi_instruction->base.base, - get_the_one_possible_value(ira->codegen, resolved_type)); - case OnePossibleValueNo: - break; - } - - switch (type_requires_comptime(ira->codegen, resolved_type)) { - case ReqCompTimeInvalid: - return ira->codegen->invalid_inst_gen; - case ReqCompTimeYes: - ir_add_error(ira, &phi_instruction->base.base, - buf_sprintf("values of type '%s' must be comptime known", buf_ptr(&resolved_type->name))); - return ira->codegen->invalid_inst_gen; - case ReqCompTimeNo: - break; - } - - bool all_stack_ptrs = (resolved_type->id == ZigTypeIdPointer); - - // cast all values to the resolved type. however we can't put cast instructions in front of the phi instruction. - // so we go back and insert the casts as the last instruction in the corresponding predecessor blocks, and - // then make sure the branch instruction is preserved. - IrBasicBlockGen *cur_bb = ira->new_irb.current_basic_block; - for (size_t i = 0; i < new_incoming_values.length; i += 1) { - IrInstGen *new_value = new_incoming_values.at(i); - IrBasicBlockGen *predecessor = new_incoming_blocks.at(i); - ir_assert(predecessor->instruction_list.length != 0, &phi_instruction->base.base); - IrInstGen *branch_instruction = predecessor->instruction_list.pop(); - ir_set_cursor_at_end_gen(&ira->new_irb, predecessor); - IrInstGen *casted_value = ir_implicit_cast(ira, new_value, resolved_type); - if (type_is_invalid(casted_value->value->type)) { - return ira->codegen->invalid_inst_gen; - } - new_incoming_values.items[i] = casted_value; - predecessor->instruction_list.append(branch_instruction); - - if (all_stack_ptrs && (casted_value->value->special != ConstValSpecialRuntime || - casted_value->value->data.rh_ptr != RuntimeHintPtrStack)) - { - all_stack_ptrs = false; - } - } - ir_set_cursor_at_end_gen(&ira->new_irb, cur_bb); - - IrInstGen *result = ir_build_phi_gen(ira, &phi_instruction->base.base, - new_incoming_blocks.length, new_incoming_blocks.items, new_incoming_values.items, resolved_type); - - if (all_stack_ptrs) { - assert(result->value->special == ConstValSpecialRuntime); - result->value->data.rh_ptr = RuntimeHintPtrStack; - } - - return result; -} - -static IrInstGen *ir_analyze_instruction_var_ptr(IrAnalyze *ira, IrInstSrcVarPtr *instruction) { - ZigVar *var = instruction->var; - IrInstGen *result = ir_get_var_ptr(ira, &instruction->base.base, var); - if (instruction->crossed_fndef_scope != nullptr && !instr_is_comptime(result)) { - ErrorMsg *msg = ir_add_error(ira, &instruction->base.base, - buf_sprintf("'%s' not accessible from inner function", var->name)); - add_error_note(ira->codegen, msg, instruction->crossed_fndef_scope->base.source_node, - buf_sprintf("crossed function definition here")); - add_error_note(ira->codegen, msg, var->decl_node, - buf_sprintf("declared here")); - return ira->codegen->invalid_inst_gen; - } - return result; -} - -static ZigType *adjust_ptr_align(CodeGen *g, ZigType *ptr_type, uint32_t new_align) { - assert(ptr_type->id == ZigTypeIdPointer); - return get_pointer_to_type_extra2(g, - ptr_type->data.pointer.child_type, - ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, - ptr_type->data.pointer.ptr_len, - new_align, - ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes, - ptr_type->data.pointer.allow_zero, - ptr_type->data.pointer.vector_index, - ptr_type->data.pointer.inferred_struct_field, - ptr_type->data.pointer.sentinel); -} - -static ZigType *adjust_ptr_sentinel(CodeGen *g, ZigType *ptr_type, ZigValue *new_sentinel) { - assert(ptr_type->id == ZigTypeIdPointer); - return get_pointer_to_type_extra2(g, - ptr_type->data.pointer.child_type, - ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, - ptr_type->data.pointer.ptr_len, - ptr_type->data.pointer.explicit_alignment, - ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes, - ptr_type->data.pointer.allow_zero, - ptr_type->data.pointer.vector_index, - ptr_type->data.pointer.inferred_struct_field, - new_sentinel); -} - -static ZigType *adjust_slice_align(CodeGen *g, ZigType *slice_type, uint32_t new_align) { - assert(is_slice(slice_type)); - ZigType *ptr_type = adjust_ptr_align(g, slice_type->data.structure.fields[slice_ptr_index]->type_entry, - new_align); - return get_slice_type(g, ptr_type); -} - -static ZigType *adjust_ptr_len(CodeGen *g, ZigType *ptr_type, PtrLen ptr_len) { - assert(ptr_type->id == ZigTypeIdPointer); - return get_pointer_to_type_extra2(g, - ptr_type->data.pointer.child_type, - ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, - ptr_len, - ptr_type->data.pointer.explicit_alignment, - ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes, - ptr_type->data.pointer.allow_zero, - ptr_type->data.pointer.vector_index, - ptr_type->data.pointer.inferred_struct_field, - (ptr_len != PtrLenUnknown) ? nullptr : ptr_type->data.pointer.sentinel); -} - -static ZigType *adjust_ptr_allow_zero(CodeGen *g, ZigType *ptr_type, bool allow_zero) { - assert(ptr_type->id == ZigTypeIdPointer); - return get_pointer_to_type_extra2(g, - ptr_type->data.pointer.child_type, - ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, - ptr_type->data.pointer.ptr_len, - ptr_type->data.pointer.explicit_alignment, - ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes, - allow_zero, - ptr_type->data.pointer.vector_index, - ptr_type->data.pointer.inferred_struct_field, - ptr_type->data.pointer.sentinel); -} - -static ZigType *adjust_ptr_const(CodeGen *g, ZigType *ptr_type, bool is_const) { - assert(ptr_type->id == ZigTypeIdPointer); - return get_pointer_to_type_extra2(g, - ptr_type->data.pointer.child_type, - is_const, ptr_type->data.pointer.is_volatile, - ptr_type->data.pointer.ptr_len, - ptr_type->data.pointer.explicit_alignment, - ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes, - ptr_type->data.pointer.allow_zero, - ptr_type->data.pointer.vector_index, - ptr_type->data.pointer.inferred_struct_field, - ptr_type->data.pointer.sentinel); -} - -static Error compute_elem_align(IrAnalyze *ira, ZigType *elem_type, uint32_t base_ptr_align, - uint64_t elem_index, uint32_t *result) -{ - Error err; - - if (base_ptr_align == 0) { - *result = 0; - return ErrorNone; - } - - // figure out the largest alignment possible - if ((err = type_resolve(ira->codegen, elem_type, ResolveStatusSizeKnown))) - return err; - - uint64_t elem_size = type_size(ira->codegen, elem_type); - uint64_t abi_align = get_abi_alignment(ira->codegen, elem_type); - uint64_t ptr_align = base_ptr_align; - - uint64_t chosen_align = abi_align; - if (ptr_align >= abi_align) { - while (ptr_align > abi_align) { - if ((elem_index * elem_size) % ptr_align == 0) { - chosen_align = ptr_align; - break; - } - ptr_align >>= 1; - } - } else if (elem_size >= ptr_align && elem_size % ptr_align == 0) { - chosen_align = ptr_align; - } else { - // can't get here because guaranteed elem_size >= abi_align - zig_unreachable(); - } - - *result = chosen_align; - return ErrorNone; -} - -static IrInstGen *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstSrcElemPtr *elem_ptr_instruction) { - Error err; - IrInstGen *array_ptr = elem_ptr_instruction->array_ptr->child; - if (type_is_invalid(array_ptr->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *elem_index = elem_ptr_instruction->elem_index->child; - if (type_is_invalid(elem_index->value->type)) - return ira->codegen->invalid_inst_gen; - - ZigValue *orig_array_ptr_val = array_ptr->value; - - ZigType *ptr_type = orig_array_ptr_val->type; - assert(ptr_type->id == ZigTypeIdPointer); - - ZigType *array_type = ptr_type->data.pointer.child_type; - - // At first return_type will be the pointer type we want to return, except with an optimistic alignment. - // We will adjust return_type's alignment before returning it. - ZigType *return_type; - - if (type_is_invalid(array_type)) - return ira->codegen->invalid_inst_gen; - - if (array_type->id == ZigTypeIdPointer && - array_type->data.pointer.ptr_len == PtrLenSingle && - array_type->data.pointer.child_type->id == ZigTypeIdArray) - { - IrInstGen *ptr_value = ir_get_deref(ira, &elem_ptr_instruction->base.base, - array_ptr, nullptr); - if (type_is_invalid(ptr_value->value->type)) - return ira->codegen->invalid_inst_gen; - - array_type = array_type->data.pointer.child_type; - ptr_type = ptr_type->data.pointer.child_type; - - orig_array_ptr_val = ptr_value->value; - } - - if (array_type->id == ZigTypeIdArray) { - if(array_type->data.array.len == 0 && array_type->data.array.sentinel == nullptr){ - ir_add_error(ira, &elem_ptr_instruction->base.base, buf_sprintf("accessing a zero length array is not allowed")); - return ira->codegen->invalid_inst_gen; - } - - ZigType *child_type = array_type->data.array.child_type; - if (ptr_type->data.pointer.host_int_bytes == 0) { - return_type = get_pointer_to_type_extra(ira->codegen, child_type, - ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, - elem_ptr_instruction->ptr_len, - ptr_type->data.pointer.explicit_alignment, 0, 0, false); - } else { - uint64_t elem_val_scalar; - if (!ir_resolve_usize(ira, elem_index, &elem_val_scalar)) - return ira->codegen->invalid_inst_gen; - - size_t bit_width = type_size_bits(ira->codegen, child_type); - size_t bit_offset = bit_width * elem_val_scalar; - - return_type = get_pointer_to_type_extra(ira->codegen, child_type, - ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, - elem_ptr_instruction->ptr_len, - 1, (uint32_t)bit_offset, ptr_type->data.pointer.host_int_bytes, false); - } - } else if (array_type->id == ZigTypeIdPointer) { - if (array_type->data.pointer.ptr_len == PtrLenSingle) { - ir_add_error_node(ira, elem_ptr_instruction->base.base.source_node, - buf_sprintf("index of single-item pointer")); - return ira->codegen->invalid_inst_gen; - } - return_type = adjust_ptr_len(ira->codegen, array_type, elem_ptr_instruction->ptr_len); - } else if (is_slice(array_type)) { - return_type = adjust_ptr_len(ira->codegen, array_type->data.structure.fields[slice_ptr_index]->type_entry, - elem_ptr_instruction->ptr_len); - } else if (array_type->id == ZigTypeIdVector) { - // This depends on whether the element index is comptime, so it is computed later. - return_type = nullptr; - } else if (elem_ptr_instruction->init_array_type_source_node != nullptr && - array_type->id == ZigTypeIdStruct && - array_type->data.structure.resolve_status == ResolveStatusBeingInferred) - { - ZigType *usize = ira->codegen->builtin_types.entry_usize; - IrInstGen *casted_elem_index = ir_implicit_cast(ira, elem_index, usize); - if (type_is_invalid(casted_elem_index->value->type)) - return ira->codegen->invalid_inst_gen; - ir_assert(instr_is_comptime(casted_elem_index), &elem_ptr_instruction->base.base); - Buf *field_name = buf_alloc(); - bigint_append_buf(field_name, &casted_elem_index->value->data.x_bigint, 10); - return ir_analyze_inferred_field_ptr(ira, field_name, &elem_ptr_instruction->base.base, - array_ptr, array_type); - } else if (is_tuple(array_type)) { - uint64_t elem_index_scalar; - if (!ir_resolve_usize(ira, elem_index, &elem_index_scalar)) - return ira->codegen->invalid_inst_gen; - if (elem_index_scalar >= array_type->data.structure.src_field_count) { - ir_add_error(ira, &elem_ptr_instruction->base.base, buf_sprintf( - "field index %" ZIG_PRI_u64 " outside tuple '%s' which has %" PRIu32 " fields", - elem_index_scalar, buf_ptr(&array_type->name), - array_type->data.structure.src_field_count)); - return ira->codegen->invalid_inst_gen; - } - TypeStructField *field = array_type->data.structure.fields[elem_index_scalar]; - return ir_analyze_struct_field_ptr(ira, &elem_ptr_instruction->base.base, field, array_ptr, - array_type, false); - } else { - ir_add_error_node(ira, elem_ptr_instruction->base.base.source_node, - buf_sprintf("array access of non-array type '%s'", buf_ptr(&array_type->name))); - return ira->codegen->invalid_inst_gen; - } - - ZigType *usize = ira->codegen->builtin_types.entry_usize; - IrInstGen *casted_elem_index = ir_implicit_cast(ira, elem_index, usize); - if (type_is_invalid(casted_elem_index->value->type)) - return ira->codegen->invalid_inst_gen; - - bool safety_check_on = elem_ptr_instruction->safety_check_on; - if (instr_is_comptime(casted_elem_index)) { - ZigValue *index_val = ir_resolve_const(ira, casted_elem_index, UndefBad); - if (index_val == nullptr) - return ira->codegen->invalid_inst_gen; - uint64_t index = bigint_as_u64(&index_val->data.x_bigint); - - if (array_type->id == ZigTypeIdArray) { - uint64_t array_len = array_type->data.array.len + - (array_type->data.array.sentinel != nullptr); - if (index >= array_len) { - ir_add_error_node(ira, elem_ptr_instruction->base.base.source_node, - buf_sprintf("index %" ZIG_PRI_u64 " outside array of size %" ZIG_PRI_u64, - index, array_len)); - return ira->codegen->invalid_inst_gen; - } - safety_check_on = false; - } else if (array_type->id == ZigTypeIdVector) { - uint64_t vector_len = array_type->data.vector.len; - if (index >= vector_len) { - ir_add_error_node(ira, elem_ptr_instruction->base.base.source_node, - buf_sprintf("index %" ZIG_PRI_u64 " outside vector of size %" ZIG_PRI_u64, - index, vector_len)); - return ira->codegen->invalid_inst_gen; - } - safety_check_on = false; - } - - if (array_type->id == ZigTypeIdVector) { - ZigType *elem_type = array_type->data.vector.elem_type; - uint32_t host_vec_len = array_type->data.vector.len; - return_type = get_pointer_to_type_extra2(ira->codegen, elem_type, - ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, - elem_ptr_instruction->ptr_len, - get_ptr_align(ira->codegen, ptr_type), 0, host_vec_len, false, (uint32_t)index, - nullptr, nullptr); - } else if (return_type->data.pointer.explicit_alignment != 0) { - uint32_t chosen_align; - if ((err = compute_elem_align(ira, return_type->data.pointer.child_type, - return_type->data.pointer.explicit_alignment, index, &chosen_align))) - { - return ira->codegen->invalid_inst_gen; - } - return_type = adjust_ptr_align(ira->codegen, return_type, chosen_align); - } - - // TODO The `array_type->id == ZigTypeIdArray` exception here should not be an exception; - // the `orig_array_ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar` clause should be omitted completely. - // However there are bugs to fix before this improvement can be made. - if (orig_array_ptr_val->special != ConstValSpecialRuntime && - orig_array_ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr && - (orig_array_ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar || array_type->id == ZigTypeIdArray)) - { - if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, - elem_ptr_instruction->base.base.source_node, orig_array_ptr_val, UndefBad))) - { - return ira->codegen->invalid_inst_gen; - } - - ZigValue *array_ptr_val = const_ptr_pointee(ira, ira->codegen, orig_array_ptr_val, - elem_ptr_instruction->base.base.source_node); - if (array_ptr_val == nullptr) - return ira->codegen->invalid_inst_gen; - - if (array_ptr_val->special == ConstValSpecialUndef && - elem_ptr_instruction->init_array_type_source_node != nullptr) - { - if (array_type->id == ZigTypeIdArray || array_type->id == ZigTypeIdVector) { - array_ptr_val->data.x_array.special = ConstArraySpecialNone; - array_ptr_val->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(array_type->data.array.len); - array_ptr_val->special = ConstValSpecialStatic; - for (size_t i = 0; i < array_type->data.array.len; i += 1) { - ZigValue *elem_val = &array_ptr_val->data.x_array.data.s_none.elements[i]; - elem_val->special = ConstValSpecialUndef; - elem_val->type = array_type->data.array.child_type; - elem_val->parent.id = ConstParentIdArray; - elem_val->parent.data.p_array.array_val = array_ptr_val; - elem_val->parent.data.p_array.elem_index = i; - } - } else if (is_slice(array_type)) { - ir_assert(array_ptr->value->type->id == ZigTypeIdPointer, &elem_ptr_instruction->base.base); - ZigType *actual_array_type = array_ptr->value->type->data.pointer.child_type; - - if (type_is_invalid(actual_array_type)) - return ira->codegen->invalid_inst_gen; - if (actual_array_type->id != ZigTypeIdArray) { - ir_add_error_node(ira, elem_ptr_instruction->init_array_type_source_node, - buf_sprintf("array literal requires address-of operator to coerce to slice type '%s'", - buf_ptr(&actual_array_type->name))); - return ira->codegen->invalid_inst_gen; - } - - ZigValue *array_init_val = ira->codegen->pass1_arena->create(); - array_init_val->special = ConstValSpecialStatic; - array_init_val->type = actual_array_type; - array_init_val->data.x_array.special = ConstArraySpecialNone; - array_init_val->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(actual_array_type->data.array.len); - array_init_val->special = ConstValSpecialStatic; - for (size_t i = 0; i < actual_array_type->data.array.len; i += 1) { - ZigValue *elem_val = &array_init_val->data.x_array.data.s_none.elements[i]; - elem_val->special = ConstValSpecialUndef; - elem_val->type = actual_array_type->data.array.child_type; - elem_val->parent.id = ConstParentIdArray; - elem_val->parent.data.p_array.array_val = array_init_val; - elem_val->parent.data.p_array.elem_index = i; - } - - init_const_slice(ira->codegen, array_ptr_val, array_init_val, 0, actual_array_type->data.array.len, - false); - array_ptr_val->data.x_struct.fields[slice_ptr_index]->data.x_ptr.mut = ConstPtrMutInfer; - } else { - ir_add_error_node(ira, elem_ptr_instruction->init_array_type_source_node, - buf_sprintf("expected array type or [_], found '%s'", - buf_ptr(&array_type->name))); - return ira->codegen->invalid_inst_gen; - } - } - - if (array_ptr_val->special != ConstValSpecialRuntime && - (array_type->id != ZigTypeIdPointer || - array_ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr)) - { - if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, - elem_ptr_instruction->base.base.source_node, array_ptr_val, UndefOk))) - { - return ira->codegen->invalid_inst_gen; - } - if (array_type->id == ZigTypeIdPointer) { - IrInstGen *result = ir_const(ira, &elem_ptr_instruction->base.base, return_type); - ZigValue *out_val = result->value; - out_val->data.x_ptr.mut = array_ptr_val->data.x_ptr.mut; - size_t new_index; - size_t mem_size; - size_t old_size; - switch (array_ptr_val->data.x_ptr.special) { - case ConstPtrSpecialInvalid: - case ConstPtrSpecialDiscard: - zig_unreachable(); - case ConstPtrSpecialRef: - if (array_ptr_val->data.x_ptr.data.ref.pointee->type->id == ZigTypeIdArray) { - ZigValue *array_val = array_ptr_val->data.x_ptr.data.ref.pointee; - new_index = index; - ZigType *array_type = array_val->type; - mem_size = array_type->data.array.len; - if (array_type->data.array.sentinel != nullptr) { - mem_size += 1; - } - old_size = mem_size; - - out_val->data.x_ptr.special = ConstPtrSpecialBaseArray; - out_val->data.x_ptr.data.base_array.array_val = array_val; - out_val->data.x_ptr.data.base_array.elem_index = new_index; - } else { - mem_size = 1; - old_size = 1; - new_index = index; - - out_val->data.x_ptr.special = ConstPtrSpecialRef; - out_val->data.x_ptr.data.ref.pointee = array_ptr_val->data.x_ptr.data.ref.pointee; - } - break; - case ConstPtrSpecialBaseArray: - case ConstPtrSpecialSubArray: - { - size_t offset = array_ptr_val->data.x_ptr.data.base_array.elem_index; - new_index = offset + index; - ZigType *array_type = array_ptr_val->data.x_ptr.data.base_array.array_val->type; - mem_size = array_type->data.array.len; - if (array_type->data.array.sentinel != nullptr) { - mem_size += 1; - } - old_size = mem_size - offset; - - assert(array_ptr_val->data.x_ptr.data.base_array.array_val); - - out_val->data.x_ptr.special = ConstPtrSpecialBaseArray; - out_val->data.x_ptr.data.base_array.array_val = - array_ptr_val->data.x_ptr.data.base_array.array_val; - out_val->data.x_ptr.data.base_array.elem_index = new_index; - - break; - } - case ConstPtrSpecialBaseStruct: - zig_panic("TODO elem ptr on a const inner struct"); - case ConstPtrSpecialBaseErrorUnionCode: - zig_panic("TODO elem ptr on a const inner error union code"); - case ConstPtrSpecialBaseErrorUnionPayload: - zig_panic("TODO elem ptr on a const inner error union payload"); - case ConstPtrSpecialBaseOptionalPayload: - zig_panic("TODO elem ptr on a const inner optional payload"); - case ConstPtrSpecialHardCodedAddr: - zig_unreachable(); - case ConstPtrSpecialFunction: - zig_panic("TODO element ptr of a function casted to a ptr"); - case ConstPtrSpecialNull: - zig_panic("TODO elem ptr on a null pointer"); - } - if (new_index >= mem_size) { - ir_add_error_node(ira, elem_ptr_instruction->base.base.source_node, - buf_sprintf("index %" ZIG_PRI_u64 " outside pointer of size %" ZIG_PRI_usize "", index, old_size)); - return ira->codegen->invalid_inst_gen; - } - return result; - } else if (is_slice(array_type)) { - ZigValue *ptr_field = array_ptr_val->data.x_struct.fields[slice_ptr_index]; - ir_assert(ptr_field != nullptr, &elem_ptr_instruction->base.base); - if (ptr_field->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) { - return ir_build_elem_ptr_gen(ira, elem_ptr_instruction->base.base.scope, - elem_ptr_instruction->base.base.source_node, array_ptr, casted_elem_index, false, - return_type); - } - ZigValue *len_field = array_ptr_val->data.x_struct.fields[slice_len_index]; - IrInstGen *result = ir_const(ira, &elem_ptr_instruction->base.base, return_type); - ZigValue *out_val = result->value; - ZigType *slice_ptr_type = array_type->data.structure.fields[slice_ptr_index]->type_entry; - uint64_t slice_len = bigint_as_u64(&len_field->data.x_bigint); - uint64_t full_slice_len = slice_len + - ((slice_ptr_type->data.pointer.sentinel != nullptr) ? 1 : 0); - if (index >= full_slice_len) { - ir_add_error_node(ira, elem_ptr_instruction->base.base.source_node, - buf_sprintf("index %" ZIG_PRI_u64 " outside slice of size %" ZIG_PRI_u64, - index, slice_len)); - return ira->codegen->invalid_inst_gen; - } - out_val->data.x_ptr.mut = ptr_field->data.x_ptr.mut; - switch (ptr_field->data.x_ptr.special) { - case ConstPtrSpecialInvalid: - case ConstPtrSpecialDiscard: - zig_unreachable(); - case ConstPtrSpecialRef: - out_val->data.x_ptr.special = ConstPtrSpecialRef; - out_val->data.x_ptr.data.ref.pointee = ptr_field->data.x_ptr.data.ref.pointee; - break; - case ConstPtrSpecialSubArray: - case ConstPtrSpecialBaseArray: - { - size_t offset = ptr_field->data.x_ptr.data.base_array.elem_index; - uint64_t new_index = offset + index; - if (ptr_field->data.x_ptr.data.base_array.array_val->data.x_array.special != - ConstArraySpecialBuf) - { - ir_assert(new_index < - ptr_field->data.x_ptr.data.base_array.array_val->type->data.array.len, - &elem_ptr_instruction->base.base); - } - out_val->data.x_ptr.special = ConstPtrSpecialBaseArray; - out_val->data.x_ptr.data.base_array.array_val = - ptr_field->data.x_ptr.data.base_array.array_val; - out_val->data.x_ptr.data.base_array.elem_index = new_index; - break; - } - case ConstPtrSpecialBaseStruct: - zig_panic("TODO elem ptr on a slice backed by const inner struct"); - case ConstPtrSpecialBaseErrorUnionCode: - zig_panic("TODO elem ptr on a slice backed by const inner error union code"); - case ConstPtrSpecialBaseErrorUnionPayload: - zig_panic("TODO elem ptr on a slice backed by const inner error union payload"); - case ConstPtrSpecialBaseOptionalPayload: - zig_panic("TODO elem ptr on a slice backed by const optional payload"); - case ConstPtrSpecialHardCodedAddr: - zig_unreachable(); - case ConstPtrSpecialFunction: - zig_panic("TODO elem ptr on a slice that was ptrcast from a function"); - case ConstPtrSpecialNull: - zig_panic("TODO elem ptr on a slice has a null pointer"); - } - return result; - } else if (array_type->id == ZigTypeIdArray || array_type->id == ZigTypeIdVector) { - IrInstGen *result; - if (orig_array_ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { - result = ir_build_elem_ptr_gen(ira, elem_ptr_instruction->base.base.scope, - elem_ptr_instruction->base.base.source_node, array_ptr, casted_elem_index, - false, return_type); - result->value->special = ConstValSpecialStatic; - } else { - result = ir_const(ira, &elem_ptr_instruction->base.base, return_type); - } - ZigValue *out_val = result->value; - out_val->data.x_ptr.special = ConstPtrSpecialBaseArray; - out_val->data.x_ptr.mut = orig_array_ptr_val->data.x_ptr.mut; - out_val->data.x_ptr.data.base_array.array_val = array_ptr_val; - out_val->data.x_ptr.data.base_array.elem_index = index; - return result; - } else { - zig_unreachable(); - } - } - } - } else if (array_type->id == ZigTypeIdVector) { - // runtime known element index - ZigType *elem_type = array_type->data.vector.elem_type; - uint32_t host_vec_len = array_type->data.vector.len; - return_type = get_pointer_to_type_extra2(ira->codegen, elem_type, - ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, - elem_ptr_instruction->ptr_len, - get_ptr_align(ira->codegen, ptr_type), 0, host_vec_len, false, VECTOR_INDEX_RUNTIME, - nullptr, nullptr); - } else { - // runtime known element index - switch (type_requires_comptime(ira->codegen, return_type)) { - case ReqCompTimeYes: - ir_add_error(ira, &elem_index->base, - buf_sprintf("values of type '%s' must be comptime known, but index value is runtime known", - buf_ptr(&return_type->data.pointer.child_type->name))); - return ira->codegen->invalid_inst_gen; - case ReqCompTimeInvalid: - return ira->codegen->invalid_inst_gen; - case ReqCompTimeNo: - break; - } - - if (return_type->data.pointer.explicit_alignment != 0) { - if ((err = type_resolve(ira->codegen, return_type->data.pointer.child_type, ResolveStatusSizeKnown))) - return ira->codegen->invalid_inst_gen; - - uint64_t elem_size = type_size(ira->codegen, return_type->data.pointer.child_type); - uint64_t abi_align = get_abi_alignment(ira->codegen, return_type->data.pointer.child_type); - uint64_t ptr_align = get_ptr_align(ira->codegen, return_type); - if (ptr_align < abi_align) { - if (elem_size >= ptr_align && elem_size % ptr_align == 0) { - return_type = adjust_ptr_align(ira->codegen, return_type, ptr_align); - } else { - // can't get here because guaranteed elem_size >= abi_align - zig_unreachable(); - } - } else { - return_type = adjust_ptr_align(ira->codegen, return_type, abi_align); - } - } - } - - return ir_build_elem_ptr_gen(ira, elem_ptr_instruction->base.base.scope, - elem_ptr_instruction->base.base.source_node, array_ptr, casted_elem_index, safety_check_on, return_type); -} - -static IrInstGen *ir_analyze_container_member_access_inner(IrAnalyze *ira, - ZigType *bare_struct_type, Buf *field_name, IrInst* source_instr, - IrInstGen *container_ptr, IrInst *container_ptr_src, ZigType *container_type) -{ - if (!is_slice(bare_struct_type)) { - ScopeDecls *container_scope = get_container_scope(bare_struct_type); - assert(container_scope != nullptr); - auto tld = find_container_decl(ira->codegen, container_scope, field_name); - if (tld) { - if (tld->id == TldIdFn) { - resolve_top_level_decl(ira->codegen, tld, source_instr->source_node, false); - if (tld->resolution == TldResolutionInvalid) - return ira->codegen->invalid_inst_gen; - if (tld->resolution == TldResolutionResolving) - return ir_error_dependency_loop(ira, source_instr); - - if (tld->visib_mod == VisibModPrivate && - tld->import != get_scope_import(source_instr->scope)) - { - ErrorMsg *msg = ir_add_error(ira, source_instr, - buf_sprintf("'%s' is private", buf_ptr(field_name))); - add_error_note(ira->codegen, msg, tld->source_node, buf_sprintf("declared here")); - return ira->codegen->invalid_inst_gen; - } - - TldFn *tld_fn = (TldFn *)tld; - ZigFn *fn_entry = tld_fn->fn_entry; - assert(fn_entry != nullptr); - - if (type_is_invalid(fn_entry->type_entry)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *bound_fn_value = ir_const_bound_fn(ira, source_instr, fn_entry, container_ptr, - container_ptr_src); - return ir_get_ref(ira, source_instr, bound_fn_value, true, false); - } else if (tld->id == TldIdVar) { - resolve_top_level_decl(ira->codegen, tld, source_instr->source_node, false); - if (tld->resolution == TldResolutionInvalid) - return ira->codegen->invalid_inst_gen; - if (tld->resolution == TldResolutionResolving) - return ir_error_dependency_loop(ira, source_instr); - - TldVar *tld_var = (TldVar *)tld; - ZigVar *var = tld_var->var; - assert(var != nullptr); - - if (type_is_invalid(var->var_type)) - return ira->codegen->invalid_inst_gen; - - if (var->const_value->type->id == ZigTypeIdFn) { - ir_assert(var->const_value->data.x_ptr.special == ConstPtrSpecialFunction, source_instr); - ZigFn *fn = var->const_value->data.x_ptr.data.fn.fn_entry; - IrInstGen *bound_fn_value = ir_const_bound_fn(ira, source_instr, fn, container_ptr, - container_ptr_src); - return ir_get_ref(ira, source_instr, bound_fn_value, true, false); - } - } - } - } - const char *prefix_name; - if (is_slice(bare_struct_type)) { - prefix_name = ""; - } else if (bare_struct_type->id == ZigTypeIdStruct) { - prefix_name = "struct "; - } else if (bare_struct_type->id == ZigTypeIdEnum) { - prefix_name = "enum "; - } else if (bare_struct_type->id == ZigTypeIdUnion) { - prefix_name = "union "; - } else { - prefix_name = ""; - } - ir_add_error_node(ira, source_instr->source_node, - buf_sprintf("no member named '%s' in %s'%s'", buf_ptr(field_name), prefix_name, buf_ptr(&bare_struct_type->name))); - return ira->codegen->invalid_inst_gen; -} - -static void memoize_field_init_val(CodeGen *codegen, ZigType *container_type, TypeStructField *field) { - if (field->init_val != nullptr) return; - if (field->decl_node == nullptr) return; - if (field->decl_node->type != NodeTypeStructField) return; - AstNode *init_node = field->decl_node->data.struct_field.value; - if (init_node == nullptr) return; - // scope is not the scope of the struct init, it's the scope of the struct type decl - Scope *analyze_scope = &get_container_scope(container_type)->base; - // memoize it - field->init_val = analyze_const_value(codegen, analyze_scope, init_node, - field->type_entry, nullptr, UndefOk); -} - -static IrInstGen *ir_analyze_struct_field_ptr(IrAnalyze *ira, IrInst* source_instr, - TypeStructField *field, IrInstGen *struct_ptr, ZigType *struct_type, bool initializing) -{ - Error err; - ZigType *field_type = resolve_struct_field_type(ira->codegen, field); - if (field_type == nullptr) - return ira->codegen->invalid_inst_gen; - if (field->is_comptime) { - IrInstGen *elem = ir_const(ira, source_instr, field_type); - memoize_field_init_val(ira->codegen, struct_type, field); - if(field->init_val != nullptr && type_is_invalid(field->init_val->type)){ - return ira->codegen->invalid_inst_gen; - } - copy_const_val(ira->codegen, elem->value, field->init_val); - return ir_get_ref2(ira, source_instr, elem, field_type, true, false); - } - switch (type_has_one_possible_value(ira->codegen, field_type)) { - case OnePossibleValueInvalid: - return ira->codegen->invalid_inst_gen; - case OnePossibleValueYes: { - IrInstGen *elem = ir_const_move(ira, source_instr, - get_the_one_possible_value(ira->codegen, field_type)); - return ir_get_ref(ira, source_instr, elem, - struct_ptr->value->type->data.pointer.is_const, - struct_ptr->value->type->data.pointer.is_volatile); - } - case OnePossibleValueNo: - break; - } - bool is_const = struct_ptr->value->type->data.pointer.is_const; - bool is_volatile = struct_ptr->value->type->data.pointer.is_volatile; - ZigType *ptr_type; - if (is_anon_container(struct_type)) { - ptr_type = get_pointer_to_type_extra(ira->codegen, field_type, - is_const, is_volatile, PtrLenSingle, 0, 0, 0, false); - } else { - ResolveStatus needed_resolve_status = - (struct_type->data.structure.layout == ContainerLayoutAuto) ? - ResolveStatusZeroBitsKnown : ResolveStatusSizeKnown; - if ((err = type_resolve(ira->codegen, struct_type, needed_resolve_status))) - return ira->codegen->invalid_inst_gen; - assert(struct_ptr->value->type->id == ZigTypeIdPointer); - uint32_t ptr_bit_offset = struct_ptr->value->type->data.pointer.bit_offset_in_host; - uint32_t ptr_host_int_bytes = struct_ptr->value->type->data.pointer.host_int_bytes; - uint32_t host_int_bytes_for_result_type = (ptr_host_int_bytes == 0) ? - get_host_int_bytes(ira->codegen, struct_type, field) : ptr_host_int_bytes; - ptr_type = get_pointer_to_type_extra(ira->codegen, field_type, - is_const, is_volatile, PtrLenSingle, field->align, - (uint32_t)(ptr_bit_offset + field->bit_offset_in_host), - (uint32_t)host_int_bytes_for_result_type, false); - } - if (instr_is_comptime(struct_ptr)) { - ZigValue *ptr_val = ir_resolve_const(ira, struct_ptr, UndefBad); - if (!ptr_val) - return ira->codegen->invalid_inst_gen; - - if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { - ZigValue *struct_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node); - if (struct_val == nullptr) - return ira->codegen->invalid_inst_gen; - if (type_is_invalid(struct_val->type)) - return ira->codegen->invalid_inst_gen; - - // This to allow lazy values to be resolved. - if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, - source_instr->source_node, struct_val, UndefOk))) - { - return ira->codegen->invalid_inst_gen; - } - if (initializing && struct_val->special == ConstValSpecialUndef) { - struct_val->data.x_struct.fields = alloc_const_vals_ptrs(ira->codegen, struct_type->data.structure.src_field_count); - struct_val->special = ConstValSpecialStatic; - for (size_t i = 0; i < struct_type->data.structure.src_field_count; i += 1) { - ZigValue *field_val = struct_val->data.x_struct.fields[i]; - field_val->special = ConstValSpecialUndef; - field_val->type = resolve_struct_field_type(ira->codegen, - struct_type->data.structure.fields[i]); - field_val->parent.id = ConstParentIdStruct; - field_val->parent.data.p_struct.struct_val = struct_val; - field_val->parent.data.p_struct.field_index = i; - } - } - IrInstGen *result; - if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { - result = ir_build_struct_field_ptr(ira, source_instr, struct_ptr, field, ptr_type); - result->value->special = ConstValSpecialStatic; - } else { - result = ir_const(ira, source_instr, ptr_type); - } - ZigValue *const_val = result->value; - const_val->data.x_ptr.special = ConstPtrSpecialBaseStruct; - const_val->data.x_ptr.mut = ptr_val->data.x_ptr.mut; - const_val->data.x_ptr.data.base_struct.struct_val = struct_val; - const_val->data.x_ptr.data.base_struct.field_index = field->src_index; - return result; - } - } - return ir_build_struct_field_ptr(ira, source_instr, struct_ptr, field, ptr_type); -} - -static IrInstGen *ir_analyze_inferred_field_ptr(IrAnalyze *ira, Buf *field_name, - IrInst* source_instr, IrInstGen *container_ptr, ZigType *container_type) -{ - // The type of the field is not available until a store using this pointer happens. - // So, here we create a special pointer type which has the inferred struct type and - // field name encoded in the type. Later, when there is a store via this pointer, - // the field type will then be available, and the field will be added to the inferred - // struct. - - ZigType *container_ptr_type = container_ptr->value->type; - ir_assert(container_ptr_type->id == ZigTypeIdPointer, source_instr); - - InferredStructField *inferred_struct_field = heap::c_allocator.create(); - inferred_struct_field->inferred_struct_type = container_type; - inferred_struct_field->field_name = field_name; - - ZigType *elem_type = ira->codegen->builtin_types.entry_anytype; - ZigType *field_ptr_type = get_pointer_to_type_extra2(ira->codegen, elem_type, - container_ptr_type->data.pointer.is_const, container_ptr_type->data.pointer.is_volatile, - PtrLenSingle, 0, 0, 0, false, VECTOR_INDEX_NONE, inferred_struct_field, nullptr); - - if (instr_is_comptime(container_ptr)) { - ZigValue *ptr_val = ir_resolve_const(ira, container_ptr, UndefBad); - if (ptr_val == nullptr) - return ira->codegen->invalid_inst_gen; - - IrInstGen *result; - if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { - result = ir_build_cast(ira, source_instr, container_ptr_type, container_ptr, CastOpNoop); - } else { - result = ir_const(ira, source_instr, field_ptr_type); - } - copy_const_val(ira->codegen, result->value, ptr_val); - result->value->type = field_ptr_type; - return result; - } - - return ir_build_cast(ira, source_instr, field_ptr_type, container_ptr, CastOpNoop); -} - -static IrInstGen *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_name, - IrInst* source_instr, IrInstGen *container_ptr, IrInst *container_ptr_src, - ZigType *container_type, bool initializing) -{ - Error err; - - ZigType *bare_type = container_ref_type(container_type); - - if (initializing && bare_type->id == ZigTypeIdStruct && - bare_type->data.structure.resolve_status == ResolveStatusBeingInferred) - { - return ir_analyze_inferred_field_ptr(ira, field_name, source_instr, container_ptr, bare_type); - } - - // Tracks wether we should return an undefined value of the correct type. - // We do this if the container pointer is undefined and we are in a TypeOf call. - bool return_undef = container_ptr->value->special == ConstValSpecialUndef && \ - get_scope_typeof(source_instr->scope) != nullptr; - - if ((err = type_resolve(ira->codegen, bare_type, ResolveStatusZeroBitsKnown))) - return ira->codegen->invalid_inst_gen; - - assert(container_ptr->value->type->id == ZigTypeIdPointer); - if (bare_type->id == ZigTypeIdStruct) { - TypeStructField *field = find_struct_type_field(bare_type, field_name); - if (field != nullptr) { - if (return_undef) { - ZigType *field_ptr_type = get_pointer_to_type(ira->codegen, resolve_struct_field_type(ira->codegen, field), - container_ptr->value->type->data.pointer.is_const); - return ir_const_undef(ira, source_instr, field_ptr_type); - } - - return ir_analyze_struct_field_ptr(ira, source_instr, field, container_ptr, bare_type, initializing); - } else { - return ir_analyze_container_member_access_inner(ira, bare_type, field_name, - source_instr, container_ptr, container_ptr_src, container_type); - } - } - - if (bare_type->id == ZigTypeIdEnum) { - return ir_analyze_container_member_access_inner(ira, bare_type, field_name, - source_instr, container_ptr, container_ptr_src, container_type); - } - - if (bare_type->id == ZigTypeIdUnion) { - bool is_const = container_ptr->value->type->data.pointer.is_const; - bool is_volatile = container_ptr->value->type->data.pointer.is_volatile; - - TypeUnionField *field = find_union_type_field(bare_type, field_name); - if (field == nullptr) { - return ir_analyze_container_member_access_inner(ira, bare_type, field_name, - source_instr, container_ptr, container_ptr_src, container_type); - } - - ZigType *field_type = resolve_union_field_type(ira->codegen, field); - if (field_type == nullptr) - return ira->codegen->invalid_inst_gen; - - ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, field_type, - is_const, is_volatile, PtrLenSingle, 0, 0, 0, false); - if (instr_is_comptime(container_ptr)) { - ZigValue *ptr_val = ir_resolve_const(ira, container_ptr, UndefBad); - if (!ptr_val) - return ira->codegen->invalid_inst_gen; - - if (ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar && - ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { - ZigValue *union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node); - if (union_val == nullptr) - return ira->codegen->invalid_inst_gen; - if (type_is_invalid(union_val->type)) - return ira->codegen->invalid_inst_gen; - - if (initializing) { - ZigValue *payload_val = ira->codegen->pass1_arena->create(); - payload_val->special = ConstValSpecialUndef; - payload_val->type = field_type; - payload_val->parent.id = ConstParentIdUnion; - payload_val->parent.data.p_union.union_val = union_val; - - union_val->special = ConstValSpecialStatic; - bigint_init_bigint(&union_val->data.x_union.tag, &field->enum_field->value); - union_val->data.x_union.payload = payload_val; - } else if (bare_type->data.unionation.layout != ContainerLayoutExtern) { - TypeUnionField *actual_field = find_union_field_by_tag(bare_type, &union_val->data.x_union.tag); - if (actual_field == nullptr) - zig_unreachable(); - - if (field != actual_field) { - ir_add_error_node(ira, source_instr->source_node, - buf_sprintf("accessing union field '%s' while field '%s' is set", buf_ptr(field_name), - buf_ptr(actual_field->name))); - return ira->codegen->invalid_inst_gen; - } - } - - ZigValue *payload_val = union_val->data.x_union.payload; - - IrInstGen *result; - if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { - result = ir_build_union_field_ptr(ira, source_instr, container_ptr, field, true, - initializing, ptr_type); - result->value->special = ConstValSpecialStatic; - } else { - result = ir_const(ira, source_instr, ptr_type); - } - ZigValue *const_val = result->value; - const_val->data.x_ptr.special = ConstPtrSpecialRef; - const_val->data.x_ptr.mut = container_ptr->value->data.x_ptr.mut; - const_val->data.x_ptr.data.ref.pointee = payload_val; - return result; - } - } - - return ir_build_union_field_ptr(ira, source_instr, container_ptr, field, true, initializing, ptr_type); - } - - zig_unreachable(); -} - -static void add_link_lib_symbol(IrAnalyze *ira, Buf *lib_name, Buf *symbol_name, AstNode *source_node) { - bool is_libc = target_is_libc_lib_name(ira->codegen->zig_target, buf_ptr(lib_name)); - if (is_libc && ira->codegen->libc_link_lib == nullptr && !ira->codegen->reported_bad_link_libc_error) { - ir_add_error_node(ira, source_node, - buf_sprintf("dependency on library c must be explicitly specified in the build command")); - ira->codegen->reported_bad_link_libc_error = true; - } - - LinkLib *link_lib = add_link_lib(ira->codegen, lib_name); - for (size_t i = 0; i < link_lib->symbols.length; i += 1) { - Buf *existing_symbol_name = link_lib->symbols.at(i); - if (buf_eql_buf(existing_symbol_name, symbol_name)) { - return; - } - } - - if (!is_libc && !target_is_wasm(ira->codegen->zig_target) && !ira->codegen->have_pic && !ira->codegen->reported_bad_link_libc_error) { - ErrorMsg *msg = ir_add_error_node(ira, source_node, - buf_sprintf("dependency on dynamic library '%s' requires enabling Position Independent Code", - buf_ptr(lib_name))); - add_error_note(ira->codegen, msg, source_node, - buf_sprintf("fixed by `--library %s` or `-fPIC`", buf_ptr(lib_name))); - ira->codegen->reported_bad_link_libc_error = true; - } - - for (size_t i = 0; i < ira->codegen->forbidden_libs.length; i += 1) { - Buf *forbidden_lib_name = ira->codegen->forbidden_libs.at(i); - if (buf_eql_buf(lib_name, forbidden_lib_name)) { - ir_add_error_node(ira, source_node, - buf_sprintf("linking against forbidden library '%s'", buf_ptr(symbol_name))); - } - } - link_lib->symbols.append(symbol_name); -} - -static IrInstGen *ir_error_dependency_loop(IrAnalyze *ira, IrInst* source_instr) { - ir_add_error(ira, source_instr, buf_sprintf("dependency loop detected")); - return ira->codegen->invalid_inst_gen; -} - -static IrInstGen *ir_analyze_decl_ref(IrAnalyze *ira, IrInst* source_instruction, Tld *tld) { - resolve_top_level_decl(ira->codegen, tld, source_instruction->source_node, true); - if (tld->resolution == TldResolutionInvalid) { - return ira->codegen->invalid_inst_gen; - } - if (tld->resolution == TldResolutionResolving) - return ir_error_dependency_loop(ira, source_instruction); - - switch (tld->id) { - case TldIdContainer: - case TldIdCompTime: - case TldIdUsingNamespace: - zig_unreachable(); - case TldIdVar: { - TldVar *tld_var = (TldVar *)tld; - ZigVar *var = tld_var->var; - assert(var != nullptr); - - if (tld_var->extern_lib_name != nullptr) { - add_link_lib_symbol(ira, tld_var->extern_lib_name, buf_create_from_str(var->name), - source_instruction->source_node); - } - - return ir_get_var_ptr(ira, source_instruction, var); - } - case TldIdFn: { - TldFn *tld_fn = (TldFn *)tld; - ZigFn *fn_entry = tld_fn->fn_entry; - assert(fn_entry->type_entry != nullptr); - - if (type_is_invalid(fn_entry->type_entry)) - return ira->codegen->invalid_inst_gen; - - if (tld_fn->extern_lib_name != nullptr) { - add_link_lib_symbol(ira, tld_fn->extern_lib_name, &fn_entry->symbol_name, source_instruction->source_node); - } - - IrInstGen *fn_inst = ir_const_fn(ira, source_instruction, fn_entry); - return ir_get_ref(ira, source_instruction, fn_inst, true, false); - } - } - zig_unreachable(); -} - -static ErrorTableEntry *find_err_table_entry(ZigType *err_set_type, Buf *field_name) { - assert(err_set_type->id == ZigTypeIdErrorSet); - for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { - ErrorTableEntry *err_table_entry = err_set_type->data.error_set.errors[i]; - if (buf_eql_buf(&err_table_entry->name, field_name)) { - return err_table_entry; - } - } - return nullptr; -} - -static IrInstGen *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstSrcFieldPtr *field_ptr_instruction) { - Error err; - IrInstGen *container_ptr = field_ptr_instruction->container_ptr->child; - if (type_is_invalid(container_ptr->value->type)) - return ira->codegen->invalid_inst_gen; - - ZigType *container_type = container_ptr->value->type->data.pointer.child_type; - - Buf *field_name = field_ptr_instruction->field_name_buffer; - if (!field_name) { - IrInstGen *field_name_expr = field_ptr_instruction->field_name_expr->child; - field_name = ir_resolve_str(ira, field_name_expr); - if (!field_name) - return ira->codegen->invalid_inst_gen; - } - - - AstNode *source_node = field_ptr_instruction->base.base.source_node; - - if (type_is_invalid(container_type)) { - return ira->codegen->invalid_inst_gen; - } else if (is_tuple(container_type) && !field_ptr_instruction->initializing && buf_eql_str(field_name, "len")) { - IrInstGen *len_inst = ir_const_unsigned(ira, &field_ptr_instruction->base.base, - container_type->data.structure.src_field_count); - return ir_get_ref(ira, &field_ptr_instruction->base.base, len_inst, true, false); - } else if (is_slice(container_type) || is_container_ref(container_type)) { - assert(container_ptr->value->type->id == ZigTypeIdPointer); - if (container_type->id == ZigTypeIdPointer) { - ZigType *bare_type = container_ref_type(container_type); - IrInstGen *container_child = ir_get_deref(ira, &field_ptr_instruction->base.base, container_ptr, nullptr); - IrInstGen *result = ir_analyze_container_field_ptr(ira, field_name, &field_ptr_instruction->base.base, - container_child, &field_ptr_instruction->container_ptr->base, bare_type, - field_ptr_instruction->initializing); - return result; - } else { - IrInstGen *result = ir_analyze_container_field_ptr(ira, field_name, &field_ptr_instruction->base.base, - container_ptr, &field_ptr_instruction->container_ptr->base, container_type, - field_ptr_instruction->initializing); - return result; - } - } else if (is_array_ref(container_type) && !field_ptr_instruction->initializing) { - if (buf_eql_str(field_name, "len")) { - ZigValue *len_val = ira->codegen->pass1_arena->create(); - if (container_type->id == ZigTypeIdPointer) { - init_const_usize(ira->codegen, len_val, container_type->data.pointer.child_type->data.array.len); - } else { - init_const_usize(ira->codegen, len_val, container_type->data.array.len); - } - - ZigType *usize = ira->codegen->builtin_types.entry_usize; - bool ptr_is_const = true; - bool ptr_is_volatile = false; - return ir_get_const_ptr(ira, &field_ptr_instruction->base.base, len_val, - usize, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0); - } else { - ir_add_error_node(ira, source_node, - buf_sprintf("no field named '%s' in '%s'", buf_ptr(field_name), - buf_ptr(&container_type->name))); - return ira->codegen->invalid_inst_gen; - } - } else if (container_type->id == ZigTypeIdMetaType) { - ZigValue *container_ptr_val = ir_resolve_const(ira, container_ptr, UndefBad); - if (!container_ptr_val) - return ira->codegen->invalid_inst_gen; - - assert(container_ptr->value->type->id == ZigTypeIdPointer); - ZigValue *child_val = const_ptr_pointee(ira, ira->codegen, container_ptr_val, source_node); - if (child_val == nullptr) - return ira->codegen->invalid_inst_gen; - if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, - field_ptr_instruction->base.base.source_node, child_val, UndefBad))) - { - return ira->codegen->invalid_inst_gen; - } - ZigType *child_type = child_val->data.x_type; - - if (type_is_invalid(child_type)) { - return ira->codegen->invalid_inst_gen; - } else if (is_container(child_type)) { - if (child_type->id == ZigTypeIdEnum) { - if ((err = type_resolve(ira->codegen, child_type, ResolveStatusSizeKnown))) - return ira->codegen->invalid_inst_gen; - - TypeEnumField *field = find_enum_type_field(child_type, field_name); - if (field) { - bool ptr_is_const = true; - bool ptr_is_volatile = false; - return ir_get_const_ptr(ira, &field_ptr_instruction->base.base, - create_const_enum(ira->codegen, child_type, &field->value), child_type, - ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0); - } - } - ScopeDecls *container_scope = get_container_scope(child_type); - Tld *tld = find_container_decl(ira->codegen, container_scope, field_name); - if (tld) { - if (tld->visib_mod == VisibModPrivate && - tld->import != get_scope_import(field_ptr_instruction->base.base.scope)) - { - ErrorMsg *msg = ir_add_error(ira, &field_ptr_instruction->base.base, - buf_sprintf("'%s' is private", buf_ptr(field_name))); - add_error_note(ira->codegen, msg, tld->source_node, buf_sprintf("declared here")); - return ira->codegen->invalid_inst_gen; - } - return ir_analyze_decl_ref(ira, &field_ptr_instruction->base.base, tld); - } - if (child_type->id == ZigTypeIdUnion && - (child_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr || - child_type->data.unionation.decl_node->data.container_decl.auto_enum)) - { - if ((err = type_resolve(ira->codegen, child_type, ResolveStatusSizeKnown))) - return ira->codegen->invalid_inst_gen; - TypeUnionField *field = find_union_type_field(child_type, field_name); - if (field) { - ZigType *enum_type = child_type->data.unionation.tag_type; - bool ptr_is_const = true; - bool ptr_is_volatile = false; - return ir_get_const_ptr(ira, &field_ptr_instruction->base.base, - create_const_enum(ira->codegen, enum_type, &field->enum_field->value), enum_type, - ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0); - } - } - const char *container_name = (child_type == ira->codegen->root_import) ? - "root source file" : buf_ptr(buf_sprintf("container '%s'", buf_ptr(&child_type->name))); - ir_add_error(ira, &field_ptr_instruction->base.base, - buf_sprintf("%s has no member called '%s'", - container_name, buf_ptr(field_name))); - return ira->codegen->invalid_inst_gen; - } else if (child_type->id == ZigTypeIdErrorSet) { - ErrorTableEntry *err_entry; - ZigType *err_set_type; - if (type_is_global_error_set(child_type)) { - auto existing_entry = ira->codegen->error_table.maybe_get(field_name); - if (existing_entry) { - err_entry = existing_entry->value; - } else { - err_entry = heap::c_allocator.create(); - err_entry->decl_node = field_ptr_instruction->base.base.source_node; - buf_init_from_buf(&err_entry->name, field_name); - size_t error_value_count = ira->codegen->errors_by_index.length; - assert((uint32_t)error_value_count < (((uint32_t)1) << (uint32_t)ira->codegen->err_tag_type->data.integral.bit_count)); - err_entry->value = error_value_count; - ira->codegen->errors_by_index.append(err_entry); - ira->codegen->error_table.put(field_name, err_entry); - } - if (err_entry->set_with_only_this_in_it == nullptr) { - err_entry->set_with_only_this_in_it = make_err_set_with_one_item(ira->codegen, - field_ptr_instruction->base.base.scope, field_ptr_instruction->base.base.source_node, - err_entry); - } - err_set_type = err_entry->set_with_only_this_in_it; - } else { - if (!resolve_inferred_error_set(ira->codegen, child_type, field_ptr_instruction->base.base.source_node)) { - return ira->codegen->invalid_inst_gen; - } - err_entry = find_err_table_entry(child_type, field_name); - if (err_entry == nullptr) { - ir_add_error(ira, &field_ptr_instruction->base.base, - buf_sprintf("no error named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&child_type->name))); - return ira->codegen->invalid_inst_gen; - } - err_set_type = child_type; - } - ZigValue *const_val = ira->codegen->pass1_arena->create(); - const_val->special = ConstValSpecialStatic; - const_val->type = err_set_type; - const_val->data.x_err_set = err_entry; - - bool ptr_is_const = true; - bool ptr_is_volatile = false; - return ir_get_const_ptr(ira, &field_ptr_instruction->base.base, const_val, - err_set_type, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0); - } else { - ir_add_error(ira, &field_ptr_instruction->base.base, - buf_sprintf("type '%s' does not support field access", buf_ptr(&container_type->name))); - return ira->codegen->invalid_inst_gen; - } - } else if (field_ptr_instruction->initializing) { - ir_add_error(ira, &field_ptr_instruction->base.base, - buf_sprintf("type '%s' does not support struct initialization syntax", buf_ptr(&container_type->name))); - return ira->codegen->invalid_inst_gen; - } else { - ir_add_error_node(ira, field_ptr_instruction->base.base.source_node, - buf_sprintf("type '%s' does not support field access", buf_ptr(&container_type->name))); - return ira->codegen->invalid_inst_gen; - } -} - -static IrInstGen *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstSrcStorePtr *instruction) { - IrInstGen *ptr = instruction->ptr->child; - if (type_is_invalid(ptr->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *value = instruction->value->child; - if (type_is_invalid(value->value->type)) - return ira->codegen->invalid_inst_gen; - - return ir_analyze_store_ptr(ira, &instruction->base.base, ptr, value, instruction->allow_write_through_const); -} - -static IrInstGen *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstSrcLoadPtr *instruction) { - IrInstGen *ptr = instruction->ptr->child; - if (type_is_invalid(ptr->value->type)) - return ira->codegen->invalid_inst_gen; - return ir_get_deref(ira, &instruction->base.base, ptr, nullptr); -} - -static IrInstGen *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstSrcTypeOf *typeof_instruction) { - ZigType *type_entry; - - const size_t value_count = typeof_instruction->value_count; - - // Fast path for the common case of TypeOf with a single argument - if (value_count < 2) { - type_entry = typeof_instruction->value.scalar->child->value->type; - } else { - IrInstGen **args = heap::c_allocator.allocate(value_count); - for (size_t i = 0; i < value_count; i += 1) { - IrInstGen *value = typeof_instruction->value.list[i]->child; - if (type_is_invalid(value->value->type)) - return ira->codegen->invalid_inst_gen; - args[i] = value; - } - - type_entry = ir_resolve_peer_types(ira, typeof_instruction->base.base.source_node, - nullptr, args, value_count); - - heap::c_allocator.deallocate(args, value_count); - } - - if (type_is_invalid(type_entry)) - return ira->codegen->invalid_inst_gen; - - return ir_const_type(ira, &typeof_instruction->base.base, type_entry); -} - -static IrInstGen *ir_analyze_instruction_set_cold(IrAnalyze *ira, IrInstSrcSetCold *instruction) { - if (ira->new_irb.exec->is_inline) { - // ignore setCold when running functions at compile time - return ir_const_void(ira, &instruction->base.base); - } - - IrInstGen *is_cold_value = instruction->is_cold->child; - bool want_cold; - if (!ir_resolve_bool(ira, is_cold_value, &want_cold)) - return ira->codegen->invalid_inst_gen; - - ZigFn *fn_entry = scope_fn_entry(instruction->base.base.scope); - if (fn_entry == nullptr) { - ir_add_error(ira, &instruction->base.base, buf_sprintf("@setCold outside function")); - return ira->codegen->invalid_inst_gen; - } - - if (fn_entry->set_cold_node != nullptr) { - ErrorMsg *msg = ir_add_error(ira, &instruction->base.base, buf_sprintf("cold set twice in same function")); - add_error_note(ira->codegen, msg, fn_entry->set_cold_node, buf_sprintf("first set here")); - return ira->codegen->invalid_inst_gen; - } - - fn_entry->set_cold_node = instruction->base.base.source_node; - fn_entry->is_cold = want_cold; - - return ir_const_void(ira, &instruction->base.base); -} - -static IrInstGen *ir_analyze_instruction_set_runtime_safety(IrAnalyze *ira, - IrInstSrcSetRuntimeSafety *set_runtime_safety_instruction) -{ - if (ira->new_irb.exec->is_inline) { - // ignore setRuntimeSafety when running functions at compile time - return ir_const_void(ira, &set_runtime_safety_instruction->base.base); - } - - bool *safety_off_ptr; - AstNode **safety_set_node_ptr; - - Scope *scope = set_runtime_safety_instruction->base.base.scope; - while (scope != nullptr) { - if (scope->id == ScopeIdBlock) { - ScopeBlock *block_scope = (ScopeBlock *)scope; - safety_off_ptr = &block_scope->safety_off; - safety_set_node_ptr = &block_scope->safety_set_node; - break; - } else if (scope->id == ScopeIdFnDef) { - ScopeFnDef *def_scope = (ScopeFnDef *)scope; - ZigFn *target_fn = def_scope->fn_entry; - assert(target_fn->def_scope != nullptr); - safety_off_ptr = &target_fn->def_scope->safety_off; - safety_set_node_ptr = &target_fn->def_scope->safety_set_node; - break; - } else if (scope->id == ScopeIdDecls) { - ScopeDecls *decls_scope = (ScopeDecls *)scope; - safety_off_ptr = &decls_scope->safety_off; - safety_set_node_ptr = &decls_scope->safety_set_node; - break; - } else { - scope = scope->parent; - continue; - } - } - assert(scope != nullptr); - - IrInstGen *safety_on_value = set_runtime_safety_instruction->safety_on->child; - bool want_runtime_safety; - if (!ir_resolve_bool(ira, safety_on_value, &want_runtime_safety)) - return ira->codegen->invalid_inst_gen; - - AstNode *source_node = set_runtime_safety_instruction->base.base.source_node; - if (*safety_set_node_ptr) { - ErrorMsg *msg = ir_add_error_node(ira, source_node, - buf_sprintf("runtime safety set twice for same scope")); - add_error_note(ira->codegen, msg, *safety_set_node_ptr, buf_sprintf("first set here")); - return ira->codegen->invalid_inst_gen; - } - *safety_set_node_ptr = source_node; - *safety_off_ptr = !want_runtime_safety; - - return ir_const_void(ira, &set_runtime_safety_instruction->base.base); -} - -static IrInstGen *ir_analyze_instruction_set_float_mode(IrAnalyze *ira, - IrInstSrcSetFloatMode *instruction) -{ - if (ira->new_irb.exec->is_inline) { - // ignore setFloatMode when running functions at compile time - return ir_const_void(ira, &instruction->base.base); - } - - bool *fast_math_on_ptr; - AstNode **fast_math_set_node_ptr; - - Scope *scope = instruction->base.base.scope; - while (scope != nullptr) { - if (scope->id == ScopeIdBlock) { - ScopeBlock *block_scope = (ScopeBlock *)scope; - fast_math_on_ptr = &block_scope->fast_math_on; - fast_math_set_node_ptr = &block_scope->fast_math_set_node; - break; - } else if (scope->id == ScopeIdFnDef) { - ScopeFnDef *def_scope = (ScopeFnDef *)scope; - ZigFn *target_fn = def_scope->fn_entry; - assert(target_fn->def_scope != nullptr); - fast_math_on_ptr = &target_fn->def_scope->fast_math_on; - fast_math_set_node_ptr = &target_fn->def_scope->fast_math_set_node; - break; - } else if (scope->id == ScopeIdDecls) { - ScopeDecls *decls_scope = (ScopeDecls *)scope; - fast_math_on_ptr = &decls_scope->fast_math_on; - fast_math_set_node_ptr = &decls_scope->fast_math_set_node; - break; - } else { - scope = scope->parent; - continue; - } - } - assert(scope != nullptr); - - IrInstGen *float_mode_value = instruction->mode_value->child; - FloatMode float_mode_scalar; - if (!ir_resolve_float_mode(ira, float_mode_value, &float_mode_scalar)) - return ira->codegen->invalid_inst_gen; - - AstNode *source_node = instruction->base.base.source_node; - if (*fast_math_set_node_ptr) { - ErrorMsg *msg = ir_add_error_node(ira, source_node, - buf_sprintf("float mode set twice for same scope")); - add_error_note(ira->codegen, msg, *fast_math_set_node_ptr, buf_sprintf("first set here")); - return ira->codegen->invalid_inst_gen; - } - *fast_math_set_node_ptr = source_node; - *fast_math_on_ptr = (float_mode_scalar == FloatModeOptimized); - - return ir_const_void(ira, &instruction->base.base); -} - -static IrInstGen *ir_analyze_instruction_any_frame_type(IrAnalyze *ira, IrInstSrcAnyFrameType *instruction) { - ZigType *payload_type = nullptr; - if (instruction->payload_type != nullptr) { - payload_type = ir_resolve_type(ira, instruction->payload_type->child); - if (type_is_invalid(payload_type)) - return ira->codegen->invalid_inst_gen; - } - - ZigType *any_frame_type = get_any_frame_type(ira->codegen, payload_type); - return ir_const_type(ira, &instruction->base.base, any_frame_type); -} - -static IrInstGen *ir_analyze_instruction_slice_type(IrAnalyze *ira, IrInstSrcSliceType *slice_type_instruction) { - IrInstGen *result = ir_const(ira, &slice_type_instruction->base.base, ira->codegen->builtin_types.entry_type); - result->value->special = ConstValSpecialLazy; - - LazyValueSliceType *lazy_slice_type = heap::c_allocator.create(); - lazy_slice_type->ira = ira; ira_ref(ira); - result->value->data.x_lazy = &lazy_slice_type->base; - lazy_slice_type->base.id = LazyValueIdSliceType; - - if (slice_type_instruction->align_value != nullptr) { - lazy_slice_type->align_inst = slice_type_instruction->align_value->child; - if (ir_resolve_const(ira, lazy_slice_type->align_inst, LazyOk) == nullptr) - return ira->codegen->invalid_inst_gen; - } - - if (slice_type_instruction->sentinel != nullptr) { - lazy_slice_type->sentinel = slice_type_instruction->sentinel->child; - if (ir_resolve_const(ira, lazy_slice_type->sentinel, LazyOk) == nullptr) - return ira->codegen->invalid_inst_gen; - } - - lazy_slice_type->elem_type = slice_type_instruction->child_type->child; - if (ir_resolve_type_lazy(ira, lazy_slice_type->elem_type) == nullptr) - return ira->codegen->invalid_inst_gen; - - lazy_slice_type->is_const = slice_type_instruction->is_const; - lazy_slice_type->is_volatile = slice_type_instruction->is_volatile; - lazy_slice_type->is_allowzero = slice_type_instruction->is_allow_zero; - - return result; -} - -static IrInstGen *ir_analyze_instruction_asm(IrAnalyze *ira, IrInstSrcAsm *asm_instruction) { - Error err; - - assert(asm_instruction->base.base.source_node->type == NodeTypeAsmExpr); - - AstNode *node = asm_instruction->base.base.source_node; - AstNodeAsmExpr *asm_expr = &asm_instruction->base.base.source_node->data.asm_expr; - - Buf *template_buf = ir_resolve_str(ira, asm_instruction->asm_template->child); - if (template_buf == nullptr) - return ira->codegen->invalid_inst_gen; - - if (asm_instruction->is_global) { - buf_append_char(&ira->codegen->global_asm, '\n'); - buf_append_buf(&ira->codegen->global_asm, template_buf); - - return ir_const_void(ira, &asm_instruction->base.base); - } - - if (!ir_emit_global_runtime_side_effect(ira, &asm_instruction->base.base)) - return ira->codegen->invalid_inst_gen; - - ZigList tok_list = {}; - if ((err = parse_asm_template(ira, node, template_buf, &tok_list))) { - return ira->codegen->invalid_inst_gen; - } - - for (size_t token_i = 0; token_i < tok_list.length; token_i += 1) { - AsmToken asm_token = tok_list.at(token_i); - if (asm_token.id == AsmTokenIdVar) { - size_t index = find_asm_index(ira->codegen, node, &asm_token, template_buf); - if (index == SIZE_MAX) { - const char *ptr = buf_ptr(template_buf) + asm_token.start + 2; - uint32_t len = asm_token.end - asm_token.start - 2; - - add_node_error(ira->codegen, node, - buf_sprintf("could not find '%.*s' in the inputs or outputs", - len, ptr)); - return ira->codegen->invalid_inst_gen; - } - } - } - - // TODO validate the output types and variable types - - IrInstGen **input_list = heap::c_allocator.allocate(asm_expr->input_list.length); - IrInstGen **output_types = heap::c_allocator.allocate(asm_expr->output_list.length); - - ZigType *return_type = ira->codegen->builtin_types.entry_void; - for (size_t i = 0; i < asm_expr->output_list.length; i += 1) { - AsmOutput *asm_output = asm_expr->output_list.at(i); - if (asm_output->return_type) { - output_types[i] = asm_instruction->output_types[i]->child; - return_type = ir_resolve_type(ira, output_types[i]); - if (type_is_invalid(return_type)) - return ira->codegen->invalid_inst_gen; - } - } - - for (size_t i = 0; i < asm_expr->input_list.length; i += 1) { - IrInstGen *const input_value = asm_instruction->input_list[i]->child; - if (type_is_invalid(input_value->value->type)) - return ira->codegen->invalid_inst_gen; - - if (instr_is_comptime(input_value) && - (input_value->value->type->id == ZigTypeIdComptimeInt || - input_value->value->type->id == ZigTypeIdComptimeFloat)) { - ir_add_error(ira, &input_value->base, - buf_sprintf("expected sized integer or sized float, found %s", buf_ptr(&input_value->value->type->name))); - return ira->codegen->invalid_inst_gen; - } - - input_list[i] = input_value; - } - - return ir_build_asm_gen(ira, &asm_instruction->base.base, - template_buf, tok_list.items, tok_list.length, - input_list, output_types, asm_instruction->output_vars, asm_instruction->return_count, - asm_instruction->has_side_effects, return_type); -} - -static IrInstGen *ir_analyze_instruction_array_type(IrAnalyze *ira, IrInstSrcArrayType *array_type_instruction) { - IrInstGen *result = ir_const(ira, &array_type_instruction->base.base, ira->codegen->builtin_types.entry_type); - result->value->special = ConstValSpecialLazy; - - LazyValueArrayType *lazy_array_type = heap::c_allocator.create(); - lazy_array_type->ira = ira; ira_ref(ira); - result->value->data.x_lazy = &lazy_array_type->base; - lazy_array_type->base.id = LazyValueIdArrayType; - - lazy_array_type->elem_type = array_type_instruction->child_type->child; - if (ir_resolve_type_lazy(ira, lazy_array_type->elem_type) == nullptr) - return ira->codegen->invalid_inst_gen; - - if (!ir_resolve_usize(ira, array_type_instruction->size->child, &lazy_array_type->length)) - return ira->codegen->invalid_inst_gen; - - if (array_type_instruction->sentinel != nullptr) { - lazy_array_type->sentinel = array_type_instruction->sentinel->child; - if (ir_resolve_const(ira, lazy_array_type->sentinel, LazyOk) == nullptr) - return ira->codegen->invalid_inst_gen; - } - - return result; -} - -static IrInstGen *ir_analyze_instruction_size_of(IrAnalyze *ira, IrInstSrcSizeOf *instruction) { - IrInstGen *result = ir_const(ira, &instruction->base.base, ira->codegen->builtin_types.entry_num_lit_int); - result->value->special = ConstValSpecialLazy; - - LazyValueSizeOf *lazy_size_of = heap::c_allocator.create(); - lazy_size_of->ira = ira; ira_ref(ira); - result->value->data.x_lazy = &lazy_size_of->base; - lazy_size_of->base.id = LazyValueIdSizeOf; - lazy_size_of->bit_size = instruction->bit_size; - - lazy_size_of->target_type = instruction->type_value->child; - if (ir_resolve_type_lazy(ira, lazy_size_of->target_type) == nullptr) - return ira->codegen->invalid_inst_gen; - - return result; -} - -static IrInstGen *ir_analyze_test_non_null(IrAnalyze *ira, IrInst *source_inst, IrInstGen *value) { - ZigType *type_entry = value->value->type; - - if (type_entry->id == ZigTypeIdPointer && type_entry->data.pointer.allow_zero) { - if (instr_is_comptime(value)) { - ZigValue *c_ptr_val = ir_resolve_const(ira, value, UndefOk); - if (c_ptr_val == nullptr) - return ira->codegen->invalid_inst_gen; - if (c_ptr_val->special == ConstValSpecialUndef) - return ir_const_undef(ira, source_inst, ira->codegen->builtin_types.entry_bool); - bool is_null = c_ptr_val->data.x_ptr.special == ConstPtrSpecialNull || - (c_ptr_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr && - c_ptr_val->data.x_ptr.data.hard_coded_addr.addr == 0); - return ir_const_bool(ira, source_inst, !is_null); - } - - return ir_build_test_non_null_gen(ira, source_inst, value); - } else if (type_entry->id == ZigTypeIdOptional) { - if (instr_is_comptime(value)) { - ZigValue *maybe_val = ir_resolve_const(ira, value, UndefOk); - if (maybe_val == nullptr) - return ira->codegen->invalid_inst_gen; - if (maybe_val->special == ConstValSpecialUndef) - return ir_const_undef(ira, source_inst, ira->codegen->builtin_types.entry_bool); - - return ir_const_bool(ira, source_inst, !optional_value_is_null(maybe_val)); - } - - return ir_build_test_non_null_gen(ira, source_inst, value); - } else if (type_entry->id == ZigTypeIdNull) { - return ir_const_bool(ira, source_inst, false); - } else { - return ir_const_bool(ira, source_inst, true); - } -} - -static IrInstGen *ir_analyze_instruction_test_non_null(IrAnalyze *ira, IrInstSrcTestNonNull *instruction) { - IrInstGen *value = instruction->value->child; - if (type_is_invalid(value->value->type)) - return ira->codegen->invalid_inst_gen; - - return ir_analyze_test_non_null(ira, &instruction->base.base, value); -} - -static IrInstGen *ir_analyze_unwrap_optional_payload(IrAnalyze *ira, IrInst* source_instr, - IrInstGen *base_ptr, bool safety_check_on, bool initializing) -{ - Error err; - - ZigType *type_entry = get_ptr_elem_type(ira->codegen, base_ptr); - if (type_is_invalid(type_entry)) - return ira->codegen->invalid_inst_gen; - - if (type_entry->id == ZigTypeIdPointer && type_entry->data.pointer.ptr_len == PtrLenC) { - if (instr_is_comptime(base_ptr)) { - ZigValue *val = ir_resolve_const(ira, base_ptr, UndefBad); - if (!val) - return ira->codegen->invalid_inst_gen; - if (val->data.x_ptr.mut != ConstPtrMutRuntimeVar) { - ZigValue *c_ptr_val = const_ptr_pointee(ira, ira->codegen, val, source_instr->source_node); - if (c_ptr_val == nullptr) - return ira->codegen->invalid_inst_gen; - bool is_null = c_ptr_val->data.x_ptr.special == ConstPtrSpecialNull || - (c_ptr_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr && - c_ptr_val->data.x_ptr.data.hard_coded_addr.addr == 0); - if (is_null) { - ir_add_error(ira, source_instr, buf_sprintf("unable to unwrap null")); - return ira->codegen->invalid_inst_gen; - } - return base_ptr; - } - } - if (!safety_check_on) - return base_ptr; - IrInstGen *c_ptr_val = ir_get_deref(ira, source_instr, base_ptr, nullptr); - ir_build_assert_non_null(ira, source_instr, c_ptr_val); - return base_ptr; - } - - if (type_entry->id != ZigTypeIdOptional) { - ir_add_error(ira, &base_ptr->base, - buf_sprintf("expected optional type, found '%s'", buf_ptr(&type_entry->name))); - return ira->codegen->invalid_inst_gen; - } - - ZigType *child_type = type_entry->data.maybe.child_type; - ZigType *result_type = get_pointer_to_type_extra(ira->codegen, child_type, - base_ptr->value->type->data.pointer.is_const, base_ptr->value->type->data.pointer.is_volatile, - PtrLenSingle, 0, 0, 0, false); - - bool same_comptime_repr = types_have_same_zig_comptime_repr(ira->codegen, child_type, type_entry); - - if (instr_is_comptime(base_ptr)) { - ZigValue *ptr_val = ir_resolve_const(ira, base_ptr, UndefBad); - if (ptr_val == nullptr) - return ira->codegen->invalid_inst_gen; - if (ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar) { - ZigValue *optional_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node); - if (optional_val == nullptr) - return ira->codegen->invalid_inst_gen; - - if (initializing) { - switch (type_has_one_possible_value(ira->codegen, child_type)) { - case OnePossibleValueInvalid: - return ira->codegen->invalid_inst_gen; - case OnePossibleValueNo: - if (!same_comptime_repr) { - ZigValue *payload_val = ira->codegen->pass1_arena->create(); - payload_val->type = child_type; - payload_val->special = ConstValSpecialUndef; - payload_val->parent.id = ConstParentIdOptionalPayload; - payload_val->parent.data.p_optional_payload.optional_val = optional_val; - - optional_val->data.x_optional = payload_val; - optional_val->special = ConstValSpecialStatic; - } - break; - case OnePossibleValueYes: { - optional_val->special = ConstValSpecialStatic; - optional_val->data.x_optional = get_the_one_possible_value(ira->codegen, child_type); - break; - } - } - } else { - if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, - source_instr->source_node, optional_val, UndefBad))) - return ira->codegen->invalid_inst_gen; - if (optional_value_is_null(optional_val)) { - ir_add_error(ira, source_instr, buf_sprintf("unable to unwrap null")); - return ira->codegen->invalid_inst_gen; - } - } - - IrInstGen *result; - if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { - result = ir_build_optional_unwrap_ptr_gen(ira, source_instr, base_ptr, false, - initializing, result_type); - result->value->special = ConstValSpecialStatic; - } else { - result = ir_const(ira, source_instr, result_type); - } - ZigValue *result_val = result->value; - result_val->data.x_ptr.special = ConstPtrSpecialRef; - result_val->data.x_ptr.mut = ptr_val->data.x_ptr.mut; - switch (type_has_one_possible_value(ira->codegen, child_type)) { - case OnePossibleValueInvalid: - return ira->codegen->invalid_inst_gen; - case OnePossibleValueNo: - if (same_comptime_repr) { - result_val->data.x_ptr.data.ref.pointee = optional_val; - } else { - assert(optional_val->data.x_optional != nullptr); - result_val->data.x_ptr.data.ref.pointee = optional_val->data.x_optional; - } - break; - case OnePossibleValueYes: - assert(optional_val->data.x_optional != nullptr); - result_val->data.x_ptr.data.ref.pointee = optional_val->data.x_optional; - break; - } - return result; - } - } - - return ir_build_optional_unwrap_ptr_gen(ira, source_instr, base_ptr, safety_check_on, - initializing, result_type); -} - -static IrInstGen *ir_analyze_instruction_optional_unwrap_ptr(IrAnalyze *ira, - IrInstSrcOptionalUnwrapPtr *instruction) -{ - IrInstGen *base_ptr = instruction->base_ptr->child; - if (type_is_invalid(base_ptr->value->type)) - return ira->codegen->invalid_inst_gen; - - return ir_analyze_unwrap_optional_payload(ira, &instruction->base.base, base_ptr, - instruction->safety_check_on, false); -} - -static IrInstGen *ir_analyze_instruction_ctz(IrAnalyze *ira, IrInstSrcCtz *instruction) { - ZigType *int_type = ir_resolve_int_type(ira, instruction->type->child); - if (type_is_invalid(int_type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *op = ir_implicit_cast(ira, instruction->op->child, int_type); - if (type_is_invalid(op->value->type)) - return ira->codegen->invalid_inst_gen; - - if (int_type->data.integral.bit_count == 0) - return ir_const_unsigned(ira, &instruction->base.base, 0); - - if (instr_is_comptime(op)) { - ZigValue *val = ir_resolve_const(ira, op, UndefOk); - if (val == nullptr) - return ira->codegen->invalid_inst_gen; - if (val->special == ConstValSpecialUndef) - return ir_const_undef(ira, &instruction->base.base, ira->codegen->builtin_types.entry_num_lit_int); - size_t result_usize = bigint_ctz(&op->value->data.x_bigint, int_type->data.integral.bit_count); - return ir_const_unsigned(ira, &instruction->base.base, result_usize); - } - - ZigType *return_type = get_smallest_unsigned_int_type(ira->codegen, int_type->data.integral.bit_count); - return ir_build_ctz_gen(ira, &instruction->base.base, return_type, op); -} - -static IrInstGen *ir_analyze_instruction_clz(IrAnalyze *ira, IrInstSrcClz *instruction) { - ZigType *int_type = ir_resolve_int_type(ira, instruction->type->child); - if (type_is_invalid(int_type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *op = ir_implicit_cast(ira, instruction->op->child, int_type); - if (type_is_invalid(op->value->type)) - return ira->codegen->invalid_inst_gen; - - if (int_type->data.integral.bit_count == 0) - return ir_const_unsigned(ira, &instruction->base.base, 0); - - if (instr_is_comptime(op)) { - ZigValue *val = ir_resolve_const(ira, op, UndefOk); - if (val == nullptr) - return ira->codegen->invalid_inst_gen; - if (val->special == ConstValSpecialUndef) - return ir_const_undef(ira, &instruction->base.base, ira->codegen->builtin_types.entry_num_lit_int); - size_t result_usize = bigint_clz(&op->value->data.x_bigint, int_type->data.integral.bit_count); - return ir_const_unsigned(ira, &instruction->base.base, result_usize); - } - - ZigType *return_type = get_smallest_unsigned_int_type(ira->codegen, int_type->data.integral.bit_count); - return ir_build_clz_gen(ira, &instruction->base.base, return_type, op); -} - -static IrInstGen *ir_analyze_instruction_pop_count(IrAnalyze *ira, IrInstSrcPopCount *instruction) { - ZigType *int_type = ir_resolve_int_type(ira, instruction->type->child); - if (type_is_invalid(int_type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *op = ir_implicit_cast(ira, instruction->op->child, int_type); - if (type_is_invalid(op->value->type)) - return ira->codegen->invalid_inst_gen; - - if (int_type->data.integral.bit_count == 0) - return ir_const_unsigned(ira, &instruction->base.base, 0); - - if (instr_is_comptime(op)) { - ZigValue *val = ir_resolve_const(ira, op, UndefOk); - if (val == nullptr) - return ira->codegen->invalid_inst_gen; - if (val->special == ConstValSpecialUndef) - return ir_const_undef(ira, &instruction->base.base, ira->codegen->builtin_types.entry_num_lit_int); - - if (bigint_cmp_zero(&val->data.x_bigint) != CmpLT) { - size_t result = bigint_popcount_unsigned(&val->data.x_bigint); - return ir_const_unsigned(ira, &instruction->base.base, result); - } - size_t result = bigint_popcount_signed(&val->data.x_bigint, int_type->data.integral.bit_count); - return ir_const_unsigned(ira, &instruction->base.base, result); - } - - ZigType *return_type = get_smallest_unsigned_int_type(ira->codegen, int_type->data.integral.bit_count); - return ir_build_pop_count_gen(ira, &instruction->base.base, return_type, op); -} - -static IrInstGen *ir_analyze_union_tag(IrAnalyze *ira, IrInst* source_instr, IrInstGen *value, bool is_gen) { - if (type_is_invalid(value->value->type)) - return ira->codegen->invalid_inst_gen; - - if (value->value->type->id != ZigTypeIdUnion) { - ir_add_error(ira, &value->base, - buf_sprintf("expected enum or union type, found '%s'", buf_ptr(&value->value->type->name))); - return ira->codegen->invalid_inst_gen; - } - if (!value->value->type->data.unionation.have_explicit_tag_type && !is_gen) { - ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("union has no associated enum")); - if (value->value->type->data.unionation.decl_node != nullptr) { - add_error_note(ira->codegen, msg, value->value->type->data.unionation.decl_node, - buf_sprintf("declared here")); - } - return ira->codegen->invalid_inst_gen; - } - - ZigType *tag_type = value->value->type->data.unionation.tag_type; - assert(tag_type->id == ZigTypeIdEnum); - - if (instr_is_comptime(value)) { - ZigValue *val = ir_resolve_const(ira, value, UndefBad); - if (!val) - return ira->codegen->invalid_inst_gen; - - IrInstGenConst *const_instruction = ir_create_inst_gen(&ira->new_irb, - source_instr->scope, source_instr->source_node); - const_instruction->base.value->type = tag_type; - const_instruction->base.value->special = ConstValSpecialStatic; - bigint_init_bigint(&const_instruction->base.value->data.x_enum_tag, &val->data.x_union.tag); - return &const_instruction->base; - } - - return ir_build_union_tag(ira, source_instr, value, tag_type); -} - -static IrInstGen *ir_analyze_instruction_switch_br(IrAnalyze *ira, - IrInstSrcSwitchBr *switch_br_instruction) -{ - IrInstGen *target_value = switch_br_instruction->target_value->child; - if (type_is_invalid(target_value->value->type)) - return ir_unreach_error(ira); - - if (switch_br_instruction->switch_prongs_void != nullptr) { - if (type_is_invalid(switch_br_instruction->switch_prongs_void->child->value->type)) { - return ir_unreach_error(ira); - } - } - - - size_t case_count = switch_br_instruction->case_count; - - bool is_comptime; - if (!ir_resolve_comptime(ira, switch_br_instruction->is_comptime->child, &is_comptime)) - return ira->codegen->invalid_inst_gen; - - if (is_comptime || instr_is_comptime(target_value)) { - ZigValue *target_val = ir_resolve_const(ira, target_value, UndefBad); - if (!target_val) - return ir_unreach_error(ira); - - IrBasicBlockSrc *old_dest_block = switch_br_instruction->else_block; - for (size_t i = 0; i < case_count; i += 1) { - IrInstSrcSwitchBrCase *old_case = &switch_br_instruction->cases[i]; - IrInstGen *case_value = old_case->value->child; - if (type_is_invalid(case_value->value->type)) - return ir_unreach_error(ira); - - IrInstGen *casted_case_value = ir_implicit_cast(ira, case_value, target_value->value->type); - if (type_is_invalid(casted_case_value->value->type)) - return ir_unreach_error(ira); - - ZigValue *case_val = ir_resolve_const(ira, casted_case_value, UndefBad); - if (!case_val) - return ir_unreach_error(ira); - - if (const_values_equal(ira->codegen, target_val, case_val)) { - old_dest_block = old_case->block; - break; - } - } - - if (is_comptime || old_dest_block->ref_count == 1) { - return ir_inline_bb(ira, &switch_br_instruction->base.base, old_dest_block); - } else { - IrBasicBlockGen *new_dest_block = ir_get_new_bb(ira, old_dest_block, &switch_br_instruction->base.base); - IrInstGen *result = ir_build_br_gen(ira, &switch_br_instruction->base.base, new_dest_block); - return ir_finish_anal(ira, result); - } - } - - IrInstGenSwitchBrCase *cases = heap::c_allocator.allocate(case_count); - for (size_t i = 0; i < case_count; i += 1) { - IrInstSrcSwitchBrCase *old_case = &switch_br_instruction->cases[i]; - IrInstGenSwitchBrCase *new_case = &cases[i]; - new_case->block = ir_get_new_bb(ira, old_case->block, &switch_br_instruction->base.base); - new_case->value = ira->codegen->invalid_inst_gen; - - // Calling ir_get_new_bb set the ref_instruction on the new basic block. - // However a switch br may branch to the same basic block which would trigger an - // incorrect re-generation of the block. So we set it to null here and assign - // it back after the loop. - new_case->block->ref_instruction = nullptr; - - IrInstSrc *old_value = old_case->value; - IrInstGen *new_value = old_value->child; - if (type_is_invalid(new_value->value->type)) - continue; - - IrInstGen *casted_new_value = ir_implicit_cast(ira, new_value, target_value->value->type); - if (type_is_invalid(casted_new_value->value->type)) - continue; - - if (!ir_resolve_const(ira, casted_new_value, UndefBad)) - continue; - - new_case->value = casted_new_value; - } - - for (size_t i = 0; i < case_count; i += 1) { - IrInstGenSwitchBrCase *new_case = &cases[i]; - if (type_is_invalid(new_case->value->value->type)) - return ir_unreach_error(ira); - new_case->block->ref_instruction = &switch_br_instruction->base.base; - } - - IrBasicBlockGen *new_else_block = ir_get_new_bb(ira, switch_br_instruction->else_block, &switch_br_instruction->base.base); - IrInstGenSwitchBr *switch_br = ir_build_switch_br_gen(ira, &switch_br_instruction->base.base, - target_value, new_else_block, case_count, cases); - return ir_finish_anal(ira, &switch_br->base); -} - -static IrInstGen *ir_analyze_instruction_switch_target(IrAnalyze *ira, - IrInstSrcSwitchTarget *switch_target_instruction) -{ - Error err; - IrInstGen *target_value_ptr = switch_target_instruction->target_value_ptr->child; - if (type_is_invalid(target_value_ptr->value->type)) - return ira->codegen->invalid_inst_gen; - - if (target_value_ptr->value->type->id == ZigTypeIdMetaType) { - assert(instr_is_comptime(target_value_ptr)); - ZigType *ptr_type = target_value_ptr->value->data.x_type; - assert(ptr_type->id == ZigTypeIdPointer); - return ir_const_type(ira, &switch_target_instruction->base.base, ptr_type->data.pointer.child_type); - } - - ZigType *target_type = target_value_ptr->value->type->data.pointer.child_type; - ZigValue *pointee_val = nullptr; - if (instr_is_comptime(target_value_ptr) && target_value_ptr->value->data.x_ptr.mut != ConstPtrMutRuntimeVar) { - pointee_val = const_ptr_pointee(ira, ira->codegen, target_value_ptr->value, target_value_ptr->base.source_node); - if (pointee_val == nullptr) - return ira->codegen->invalid_inst_gen; - - if (pointee_val->special == ConstValSpecialRuntime) - pointee_val = nullptr; - } - if ((err = type_resolve(ira->codegen, target_type, ResolveStatusSizeKnown))) - return ira->codegen->invalid_inst_gen; - - switch (target_type->id) { - case ZigTypeIdInvalid: - zig_unreachable(); - case ZigTypeIdMetaType: - case ZigTypeIdVoid: - case ZigTypeIdBool: - case ZigTypeIdInt: - case ZigTypeIdFloat: - case ZigTypeIdComptimeFloat: - case ZigTypeIdComptimeInt: - case ZigTypeIdEnumLiteral: - case ZigTypeIdPointer: - case ZigTypeIdFn: - case ZigTypeIdErrorSet: { - if (pointee_val) { - IrInstGen *result = ir_const(ira, &switch_target_instruction->base.base, nullptr); - copy_const_val(ira->codegen, result->value, pointee_val); - result->value->type = target_type; - return result; - } - - IrInstGen *result = ir_get_deref(ira, &switch_target_instruction->base.base, target_value_ptr, nullptr); - result->value->type = target_type; - return result; - } - case ZigTypeIdUnion: { - AstNode *decl_node = target_type->data.unionation.decl_node; - if (!decl_node->data.container_decl.auto_enum && - decl_node->data.container_decl.init_arg_expr == nullptr) - { - ErrorMsg *msg = ir_add_error(ira, &target_value_ptr->base, - buf_sprintf("switch on union which has no attached enum")); - add_error_note(ira->codegen, msg, decl_node, - buf_sprintf("consider 'union(enum)' here")); - return ira->codegen->invalid_inst_gen; - } - ZigType *tag_type = target_type->data.unionation.tag_type; - assert(tag_type != nullptr); - assert(tag_type->id == ZigTypeIdEnum); - if (pointee_val) { - IrInstGen *result = ir_const(ira, &switch_target_instruction->base.base, tag_type); - bigint_init_bigint(&result->value->data.x_enum_tag, &pointee_val->data.x_union.tag); - return result; - } - if (tag_type->data.enumeration.src_field_count == 1 && !tag_type->data.enumeration.non_exhaustive) { - IrInstGen *result = ir_const(ira, &switch_target_instruction->base.base, tag_type); - TypeEnumField *only_field = &tag_type->data.enumeration.fields[0]; - bigint_init_bigint(&result->value->data.x_enum_tag, &only_field->value); - return result; - } - - IrInstGen *union_value = ir_get_deref(ira, &switch_target_instruction->base.base, target_value_ptr, nullptr); - union_value->value->type = target_type; - - return ir_build_union_tag(ira, &switch_target_instruction->base.base, union_value, tag_type); - } - case ZigTypeIdEnum: { - if ((err = type_resolve(ira->codegen, target_type, ResolveStatusZeroBitsKnown))) - return ira->codegen->invalid_inst_gen; - if (target_type->data.enumeration.src_field_count == 1 && !target_type->data.enumeration.non_exhaustive) { - TypeEnumField *only_field = &target_type->data.enumeration.fields[0]; - IrInstGen *result = ir_const(ira, &switch_target_instruction->base.base, target_type); - bigint_init_bigint(&result->value->data.x_enum_tag, &only_field->value); - return result; - } - - if (pointee_val) { - IrInstGen *result = ir_const(ira, &switch_target_instruction->base.base, target_type); - bigint_init_bigint(&result->value->data.x_enum_tag, &pointee_val->data.x_enum_tag); - return result; - } - - IrInstGen *enum_value = ir_get_deref(ira, &switch_target_instruction->base.base, target_value_ptr, nullptr); - enum_value->value->type = target_type; - return enum_value; - } - case ZigTypeIdErrorUnion: - case ZigTypeIdUnreachable: - case ZigTypeIdArray: - case ZigTypeIdStruct: - case ZigTypeIdUndefined: - case ZigTypeIdNull: - case ZigTypeIdOptional: - case ZigTypeIdBoundFn: - case ZigTypeIdOpaque: - case ZigTypeIdVector: - case ZigTypeIdFnFrame: - case ZigTypeIdAnyFrame: - ir_add_error(ira, &switch_target_instruction->base.base, - buf_sprintf("invalid switch target type '%s'", buf_ptr(&target_type->name))); - return ira->codegen->invalid_inst_gen; - } - zig_unreachable(); -} - -static IrInstGen *ir_analyze_instruction_switch_var(IrAnalyze *ira, IrInstSrcSwitchVar *instruction) { - IrInstGen *target_value_ptr = instruction->target_value_ptr->child; - if (type_is_invalid(target_value_ptr->value->type)) - return ira->codegen->invalid_inst_gen; - - ZigType *ref_type = target_value_ptr->value->type; - assert(ref_type->id == ZigTypeIdPointer); - ZigType *target_type = target_value_ptr->value->type->data.pointer.child_type; - if (target_type->id == ZigTypeIdUnion) { - ZigType *enum_type = target_type->data.unionation.tag_type; - assert(enum_type != nullptr); - assert(enum_type->id == ZigTypeIdEnum); - assert(instruction->prongs_len > 0); - - IrInstGen *first_prong_value = instruction->prongs_ptr[0]->child; - if (type_is_invalid(first_prong_value->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *first_casted_prong_value = ir_implicit_cast(ira, first_prong_value, enum_type); - if (type_is_invalid(first_casted_prong_value->value->type)) - return ira->codegen->invalid_inst_gen; - - ZigValue *first_prong_val = ir_resolve_const(ira, first_casted_prong_value, UndefBad); - if (first_prong_val == nullptr) - return ira->codegen->invalid_inst_gen; - - TypeUnionField *first_field = find_union_field_by_tag(target_type, &first_prong_val->data.x_enum_tag); - - ErrorMsg *invalid_payload_msg = nullptr; - for (size_t prong_i = 1; prong_i < instruction->prongs_len; prong_i += 1) { - IrInstGen *this_prong_inst = instruction->prongs_ptr[prong_i]->child; - if (type_is_invalid(this_prong_inst->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *this_casted_prong_value = ir_implicit_cast(ira, this_prong_inst, enum_type); - if (type_is_invalid(this_casted_prong_value->value->type)) - return ira->codegen->invalid_inst_gen; - - ZigValue *this_prong = ir_resolve_const(ira, this_casted_prong_value, UndefBad); - if (this_prong == nullptr) - return ira->codegen->invalid_inst_gen; - - TypeUnionField *payload_field = find_union_field_by_tag(target_type, &this_prong->data.x_enum_tag); - ZigType *payload_type = payload_field->type_entry; - if (first_field->type_entry != payload_type) { - if (invalid_payload_msg == nullptr) { - invalid_payload_msg = ir_add_error(ira, &instruction->base.base, - buf_sprintf("capture group with incompatible types")); - add_error_note(ira->codegen, invalid_payload_msg, first_prong_value->base.source_node, - buf_sprintf("type '%s' here", buf_ptr(&first_field->type_entry->name))); - } - add_error_note(ira->codegen, invalid_payload_msg, this_prong_inst->base.source_node, - buf_sprintf("type '%s' here", buf_ptr(&payload_field->type_entry->name))); - } - } - - if (invalid_payload_msg != nullptr) { - return ira->codegen->invalid_inst_gen; - } - - if (instr_is_comptime(target_value_ptr)) { - ZigValue *target_val_ptr = ir_resolve_const(ira, target_value_ptr, UndefBad); - if (!target_value_ptr) - return ira->codegen->invalid_inst_gen; - - ZigValue *pointee_val = const_ptr_pointee(ira, ira->codegen, target_val_ptr, instruction->base.base.source_node); - if (pointee_val == nullptr) - return ira->codegen->invalid_inst_gen; - - IrInstGen *result = ir_const(ira, &instruction->base.base, - get_pointer_to_type(ira->codegen, first_field->type_entry, - target_val_ptr->type->data.pointer.is_const)); - ZigValue *out_val = result->value; - out_val->data.x_ptr.special = ConstPtrSpecialRef; - out_val->data.x_ptr.mut = target_val_ptr->data.x_ptr.mut; - out_val->data.x_ptr.data.ref.pointee = pointee_val->data.x_union.payload; - return result; - } - - ZigType *result_type = get_pointer_to_type(ira->codegen, first_field->type_entry, - target_value_ptr->value->type->data.pointer.is_const); - return ir_build_union_field_ptr(ira, &instruction->base.base, target_value_ptr, first_field, - false, false, result_type); - } else if (target_type->id == ZigTypeIdErrorSet) { - // construct an error set from the prong values - ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet); - err_set_type->size_in_bits = ira->codegen->builtin_types.entry_global_error_set->size_in_bits; - err_set_type->abi_align = ira->codegen->builtin_types.entry_global_error_set->abi_align; - err_set_type->abi_size = ira->codegen->builtin_types.entry_global_error_set->abi_size; - ZigList error_list = {}; - buf_resize(&err_set_type->name, 0); - buf_appendf(&err_set_type->name, "error{"); - for (size_t i = 0; i < instruction->prongs_len; i += 1) { - ErrorTableEntry *err = ir_resolve_error(ira, instruction->prongs_ptr[i]->child); - if (err == nullptr) - return ira->codegen->invalid_inst_gen; - error_list.append(err); - buf_appendf(&err_set_type->name, "%s,", buf_ptr(&err->name)); - } - err_set_type->data.error_set.errors = error_list.items; - err_set_type->data.error_set.err_count = error_list.length; - buf_appendf(&err_set_type->name, "}"); - - - ZigType *new_target_value_ptr_type = get_pointer_to_type_extra(ira->codegen, - err_set_type, - ref_type->data.pointer.is_const, ref_type->data.pointer.is_volatile, - ref_type->data.pointer.ptr_len, - ref_type->data.pointer.explicit_alignment, - ref_type->data.pointer.bit_offset_in_host, ref_type->data.pointer.host_int_bytes, - ref_type->data.pointer.allow_zero); - return ir_analyze_ptr_cast(ira, &instruction->base.base, target_value_ptr, - &instruction->target_value_ptr->base, new_target_value_ptr_type, &instruction->base.base, false, false); - } else { - ir_add_error(ira, &instruction->base.base, - buf_sprintf("switch on type '%s' provides no expression parameter", buf_ptr(&target_type->name))); - return ira->codegen->invalid_inst_gen; - } -} - -static IrInstGen *ir_analyze_instruction_switch_else_var(IrAnalyze *ira, - IrInstSrcSwitchElseVar *instruction) -{ - IrInstGen *target_value_ptr = instruction->target_value_ptr->child; - if (type_is_invalid(target_value_ptr->value->type)) - return ira->codegen->invalid_inst_gen; - - ZigType *ref_type = target_value_ptr->value->type; - assert(ref_type->id == ZigTypeIdPointer); - ZigType *target_type = target_value_ptr->value->type->data.pointer.child_type; - if (target_type->id == ZigTypeIdErrorSet) { - // make a new set that has the other cases removed - if (!resolve_inferred_error_set(ira->codegen, target_type, instruction->base.base.source_node)) { - return ira->codegen->invalid_inst_gen; - } - if (type_is_global_error_set(target_type)) { - // the type of the else capture variable still has to be the global error set. - // once the runtime hint system is more sophisticated, we could add some hint information here. - return target_value_ptr; - } - // Make note of the errors handled by other cases - ErrorTableEntry **errors = heap::c_allocator.allocate(ira->codegen->errors_by_index.length); - // We may not have any case in the switch if this is a lone else - const size_t switch_cases = instruction->switch_br ? instruction->switch_br->case_count : 0; - for (size_t case_i = 0; case_i < switch_cases; case_i += 1) { - IrInstSrcSwitchBrCase *br_case = &instruction->switch_br->cases[case_i]; - IrInstGen *case_expr = br_case->value->child; - if (case_expr->value->type->id == ZigTypeIdErrorSet) { - ErrorTableEntry *err = ir_resolve_error(ira, case_expr); - if (err == nullptr) - return ira->codegen->invalid_inst_gen; - errors[err->value] = err; - } else if (case_expr->value->type->id == ZigTypeIdMetaType) { - ZigType *err_set_type = ir_resolve_type(ira, case_expr); - if (type_is_invalid(err_set_type)) - return ira->codegen->invalid_inst_gen; - populate_error_set_table(errors, err_set_type); - } else { - zig_unreachable(); - } - } - ZigList result_list = {}; - - ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet); - buf_resize(&err_set_type->name, 0); - buf_appendf(&err_set_type->name, "error{"); - - // Look at all the errors in the type switched on and add them to the result_list - // if they are not handled by cases. - for (uint32_t i = 0; i < target_type->data.error_set.err_count; i += 1) { - ErrorTableEntry *error_entry = target_type->data.error_set.errors[i]; - ErrorTableEntry *existing_entry = errors[error_entry->value]; - if (existing_entry == nullptr) { - result_list.append(error_entry); - buf_appendf(&err_set_type->name, "%s,", buf_ptr(&error_entry->name)); - } - } - heap::c_allocator.deallocate(errors, ira->codegen->errors_by_index.length); - - err_set_type->data.error_set.err_count = result_list.length; - err_set_type->data.error_set.errors = result_list.items; - err_set_type->size_in_bits = ira->codegen->builtin_types.entry_global_error_set->size_in_bits; - err_set_type->abi_align = ira->codegen->builtin_types.entry_global_error_set->abi_align; - err_set_type->abi_size = ira->codegen->builtin_types.entry_global_error_set->abi_size; - - buf_appendf(&err_set_type->name, "}"); - - ZigType *new_target_value_ptr_type = get_pointer_to_type_extra(ira->codegen, - err_set_type, - ref_type->data.pointer.is_const, ref_type->data.pointer.is_volatile, - ref_type->data.pointer.ptr_len, - ref_type->data.pointer.explicit_alignment, - ref_type->data.pointer.bit_offset_in_host, ref_type->data.pointer.host_int_bytes, - ref_type->data.pointer.allow_zero); - return ir_analyze_ptr_cast(ira, &instruction->base.base, target_value_ptr, - &instruction->target_value_ptr->base, new_target_value_ptr_type, &instruction->base.base, false, false); - } - - return target_value_ptr; -} - -static IrInstGen *ir_analyze_instruction_import(IrAnalyze *ira, IrInstSrcImport *import_instruction) { - Error err; - - IrInstGen *name_value = import_instruction->name->child; - Buf *import_target_str = ir_resolve_str(ira, name_value); - if (!import_target_str) - return ira->codegen->invalid_inst_gen; - - AstNode *source_node = import_instruction->base.base.source_node; - ZigType *import = source_node->owner; - - ZigType *target_import; - Buf *import_target_path; - Buf full_path = BUF_INIT; - if ((err = analyze_import(ira->codegen, import, import_target_str, &target_import, - &import_target_path, &full_path))) - { - if (err == ErrorImportOutsidePkgPath) { - ir_add_error_node(ira, source_node, - buf_sprintf("import of file outside package path: '%s'", - buf_ptr(import_target_path))); - return ira->codegen->invalid_inst_gen; - } else if (err == ErrorFileNotFound) { - ir_add_error_node(ira, source_node, - buf_sprintf("unable to find '%s'", buf_ptr(import_target_path))); - return ira->codegen->invalid_inst_gen; - } else { - ir_add_error_node(ira, source_node, - buf_sprintf("unable to open '%s': %s", buf_ptr(&full_path), err_str(err))); - return ira->codegen->invalid_inst_gen; - } - } - - return ir_const_type(ira, &import_instruction->base.base, target_import); -} - -static IrInstGen *ir_analyze_instruction_ref(IrAnalyze *ira, IrInstSrcRef *ref_instruction) { - IrInstGen *value = ref_instruction->value->child; - if (type_is_invalid(value->value->type)) - return ira->codegen->invalid_inst_gen; - - bool is_const = false; - bool is_volatile = false; - - ZigValue *child_value = value->value; - if (child_value->special == ConstValSpecialStatic) { - is_const = true; - } - - return ir_get_ref(ira, &ref_instruction->base.base, value, is_const, is_volatile); -} - -static IrInstGen *ir_analyze_union_init(IrAnalyze *ira, IrInst* source_instruction, - AstNode *field_source_node, ZigType *union_type, Buf *field_name, IrInstGen *field_result_loc, - IrInstGen *result_loc) -{ - Error err; - assert(union_type->id == ZigTypeIdUnion); - - if ((err = type_resolve(ira->codegen, union_type, ResolveStatusZeroBitsKnown))) - return ira->codegen->invalid_inst_gen; - - TypeUnionField *type_field = find_union_type_field(union_type, field_name); - if (type_field == nullptr) { - ir_add_error_node(ira, field_source_node, - buf_sprintf("no field named '%s' in union '%s'", - buf_ptr(field_name), buf_ptr(&union_type->name))); - return ira->codegen->invalid_inst_gen; - } - - if (type_is_invalid(type_field->type_entry)) - return ira->codegen->invalid_inst_gen; - - if (result_loc->value->data.x_ptr.mut == ConstPtrMutInfer) { - if (instr_is_comptime(field_result_loc) && - field_result_loc->value->data.x_ptr.mut != ConstPtrMutRuntimeVar) - { - // nothing - } else { - result_loc->value->special = ConstValSpecialRuntime; - } - } - - bool is_comptime = ir_should_inline(ira->old_irb.exec, source_instruction->scope) - || type_requires_comptime(ira->codegen, union_type) == ReqCompTimeYes; - - IrInstGen *result = ir_get_deref(ira, source_instruction, result_loc, nullptr); - if (is_comptime && !instr_is_comptime(result)) { - ir_add_error(ira, &field_result_loc->base, - buf_sprintf("unable to evaluate constant expression")); - return ira->codegen->invalid_inst_gen; - } - return result; -} - -static IrInstGen *ir_analyze_container_init_fields(IrAnalyze *ira, IrInst *source_instr, - ZigType *container_type, size_t instr_field_count, IrInstSrcContainerInitFieldsField *fields, - IrInstGen *result_loc) -{ - Error err; - if (container_type->id == ZigTypeIdUnion) { - if (instr_field_count != 1) { - ir_add_error(ira, source_instr, - buf_sprintf("union initialization expects exactly one field")); - return ira->codegen->invalid_inst_gen; - } - IrInstSrcContainerInitFieldsField *field = &fields[0]; - IrInstGen *field_result_loc = field->result_loc->child; - if (type_is_invalid(field_result_loc->value->type)) - return ira->codegen->invalid_inst_gen; - - return ir_analyze_union_init(ira, source_instr, field->source_node, container_type, field->name, - field_result_loc, result_loc); - } - if (container_type->id != ZigTypeIdStruct || is_slice(container_type)) { - ir_add_error(ira, source_instr, - buf_sprintf("type '%s' does not support struct initialization syntax", - buf_ptr(&container_type->name))); - return ira->codegen->invalid_inst_gen; - } - - if (container_type->data.structure.resolve_status == ResolveStatusBeingInferred) { - // We're now done inferring the type. - container_type->data.structure.resolve_status = ResolveStatusUnstarted; - } - - if ((err = type_resolve(ira->codegen, container_type, ResolveStatusSizeKnown))) - return ira->codegen->invalid_inst_gen; - - size_t actual_field_count = container_type->data.structure.src_field_count; - - IrInstGen *first_non_const_instruction = nullptr; - - AstNode **field_assign_nodes = heap::c_allocator.allocate(actual_field_count); - ZigList const_ptrs = {}; - - bool is_comptime = ir_should_inline(ira->old_irb.exec, source_instr->scope) - || type_requires_comptime(ira->codegen, container_type) == ReqCompTimeYes; - - - // Here we iterate over the fields that have been initialized, and emit - // compile errors for missing fields and duplicate fields. - // It is only now that we find out whether the struct initialization can be a comptime - // value, but we have already emitted runtime instructions for the fields that - // were initialized with runtime values, and have omitted instructions that would have - // initialized fields with comptime values. - // So now we must clean up this situation. If it turns out the struct initialization can - // be a comptime value, overwrite ConstPtrMutInfer with ConstPtrMutComptimeConst. - // Otherwise, we must emit instructions to runtime-initialize the fields that have - // comptime-known values. - - for (size_t i = 0; i < instr_field_count; i += 1) { - IrInstSrcContainerInitFieldsField *field = &fields[i]; - - IrInstGen *field_result_loc = field->result_loc->child; - if (type_is_invalid(field_result_loc->value->type)) - return ira->codegen->invalid_inst_gen; - - TypeStructField *type_field = find_struct_type_field(container_type, field->name); - if (!type_field) { - ir_add_error_node(ira, field->source_node, - buf_sprintf("no field named '%s' in struct '%s'", - buf_ptr(field->name), buf_ptr(&container_type->name))); - return ira->codegen->invalid_inst_gen; - } - - if (type_is_invalid(type_field->type_entry)) - return ira->codegen->invalid_inst_gen; - - size_t field_index = type_field->src_index; - AstNode *existing_assign_node = field_assign_nodes[field_index]; - if (existing_assign_node) { - ErrorMsg *msg = ir_add_error_node(ira, field->source_node, buf_sprintf("duplicate field")); - add_error_note(ira->codegen, msg, existing_assign_node, buf_sprintf("other field here")); - return ira->codegen->invalid_inst_gen; - } - field_assign_nodes[field_index] = field->source_node; - - if (instr_is_comptime(field_result_loc) && - field_result_loc->value->data.x_ptr.mut != ConstPtrMutRuntimeVar) - { - const_ptrs.append(field_result_loc); - } else { - first_non_const_instruction = field_result_loc; - } - } - - bool any_missing = false; - for (size_t i = 0; i < actual_field_count; i += 1) { - if (field_assign_nodes[i] != nullptr) continue; - - // look for a default field value - TypeStructField *field = container_type->data.structure.fields[i]; - memoize_field_init_val(ira->codegen, container_type, field); - if (field->init_val == nullptr) { - ir_add_error(ira, source_instr, - buf_sprintf("missing field: '%s'", buf_ptr(field->name))); - any_missing = true; - continue; - } - if (type_is_invalid(field->init_val->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *runtime_inst = ir_const(ira, source_instr, field->init_val->type); - copy_const_val(ira->codegen, runtime_inst->value, field->init_val); - - IrInstGen *field_ptr = ir_analyze_struct_field_ptr(ira, source_instr, field, result_loc, - container_type, true); - ir_analyze_store_ptr(ira, source_instr, field_ptr, runtime_inst, false); - if (instr_is_comptime(field_ptr) && field_ptr->value->data.x_ptr.mut != ConstPtrMutRuntimeVar) { - const_ptrs.append(field_ptr); - } else { - first_non_const_instruction = result_loc; - } - } - if (any_missing) - return ira->codegen->invalid_inst_gen; - - if (result_loc->value->data.x_ptr.mut == ConstPtrMutInfer) { - if (const_ptrs.length != actual_field_count) { - result_loc->value->special = ConstValSpecialRuntime; - for (size_t i = 0; i < const_ptrs.length; i += 1) { - IrInstGen *field_result_loc = const_ptrs.at(i); - IrInstGen *deref = ir_get_deref(ira, &field_result_loc->base, field_result_loc, nullptr); - field_result_loc->value->special = ConstValSpecialRuntime; - ir_analyze_store_ptr(ira, &field_result_loc->base, field_result_loc, deref, false); - } - } - } - - IrInstGen *result = ir_get_deref(ira, source_instr, result_loc, nullptr); - - if (is_comptime && !instr_is_comptime(result)) { - ir_add_error_node(ira, first_non_const_instruction->base.source_node, - buf_sprintf("unable to evaluate constant expression")); - return ira->codegen->invalid_inst_gen; - } - - return result; -} - -static IrInstGen *ir_analyze_instruction_container_init_list(IrAnalyze *ira, - IrInstSrcContainerInitList *instruction) -{ - ir_assert(instruction->result_loc != nullptr, &instruction->base.base); - IrInstGen *result_loc = instruction->result_loc->child; - if (type_is_invalid(result_loc->value->type)) - return result_loc; - - ir_assert(result_loc->value->type->id == ZigTypeIdPointer, &instruction->base.base); - if (result_loc->value->type->data.pointer.is_const) { - ir_add_error(ira, &instruction->base.base, buf_sprintf("cannot assign to constant")); - return ira->codegen->invalid_inst_gen; - } - - ZigType *container_type = result_loc->value->type->data.pointer.child_type; - size_t elem_count = instruction->item_count; - - if (is_slice(container_type)) { - ir_add_error_node(ira, instruction->init_array_type_source_node, - buf_sprintf("array literal requires address-of operator to coerce to slice type '%s'", - buf_ptr(&container_type->name))); - return ira->codegen->invalid_inst_gen; - } - - if (container_type->id == ZigTypeIdVoid) { - if (elem_count != 0) { - ir_add_error_node(ira, instruction->base.base.source_node, - buf_sprintf("void expression expects no arguments")); - return ira->codegen->invalid_inst_gen; - } - return ir_const_void(ira, &instruction->base.base); - } - - if (container_type->id == ZigTypeIdStruct && elem_count == 0) { - ir_assert(instruction->result_loc != nullptr, &instruction->base.base); - IrInstGen *result_loc = instruction->result_loc->child; - if (type_is_invalid(result_loc->value->type)) - return result_loc; - return ir_analyze_container_init_fields(ira, &instruction->base.base, container_type, 0, nullptr, result_loc); - } - - if (container_type->id == ZigTypeIdArray) { - ZigType *child_type = container_type->data.array.child_type; - if (container_type->data.array.len != elem_count) { - ZigType *literal_type = get_array_type(ira->codegen, child_type, elem_count, nullptr); - - ir_add_error(ira, &instruction->base.base, - buf_sprintf("expected %s literal, found %s literal", - buf_ptr(&container_type->name), buf_ptr(&literal_type->name))); - return ira->codegen->invalid_inst_gen; - } - } else if (container_type->id == ZigTypeIdStruct && - container_type->data.structure.resolve_status == ResolveStatusBeingInferred) - { - // We're now done inferring the type. - container_type->data.structure.resolve_status = ResolveStatusUnstarted; - } else if (container_type->id == ZigTypeIdVector) { - // OK - } else { - ir_add_error(ira, &instruction->base.base, - buf_sprintf("type '%s' does not support array initialization", - buf_ptr(&container_type->name))); - return ira->codegen->invalid_inst_gen; - } - - switch (type_has_one_possible_value(ira->codegen, container_type)) { - case OnePossibleValueInvalid: - return ira->codegen->invalid_inst_gen; - case OnePossibleValueYes: - return ir_const_move(ira, &instruction->base.base, - get_the_one_possible_value(ira->codegen, container_type)); - case OnePossibleValueNo: - break; - } - - bool is_comptime; - switch (type_requires_comptime(ira->codegen, container_type)) { - case ReqCompTimeInvalid: - return ira->codegen->invalid_inst_gen; - case ReqCompTimeNo: - is_comptime = ir_should_inline(ira->old_irb.exec, instruction->base.base.scope); - break; - case ReqCompTimeYes: - is_comptime = true; - break; - } - - IrInstGen *first_non_const_instruction = nullptr; - - // The Result Location Mechanism has already emitted runtime instructions to - // initialize runtime elements and has omitted instructions for the comptime - // elements. However it is only now that we find out whether the array initialization - // can be a comptime value. So we must clean up the situation. If it turns out - // array initialization can be a comptime value, overwrite ConstPtrMutInfer with - // ConstPtrMutComptimeConst. Otherwise, emit instructions to runtime-initialize the - // elements that have comptime-known values. - ZigList const_ptrs = {}; - - for (size_t i = 0; i < elem_count; i += 1) { - IrInstGen *elem_result_loc = instruction->elem_result_loc_list[i]->child; - if (type_is_invalid(elem_result_loc->value->type)) - return ira->codegen->invalid_inst_gen; - - assert(elem_result_loc->value->type->id == ZigTypeIdPointer); - - if (instr_is_comptime(elem_result_loc) && - elem_result_loc->value->data.x_ptr.mut != ConstPtrMutRuntimeVar) - { - const_ptrs.append(elem_result_loc); - } else { - first_non_const_instruction = elem_result_loc; - } - } - - if (result_loc->value->data.x_ptr.mut == ConstPtrMutInfer) { - if (const_ptrs.length != elem_count) { - result_loc->value->special = ConstValSpecialRuntime; - for (size_t i = 0; i < const_ptrs.length; i += 1) { - IrInstGen *elem_result_loc = const_ptrs.at(i); - assert(elem_result_loc->value->special == ConstValSpecialStatic); - if (elem_result_loc->value->type->data.pointer.inferred_struct_field != nullptr) { - // This field will be generated comptime; no need to do this. - continue; - } - IrInstGen *deref = ir_get_deref(ira, &elem_result_loc->base, elem_result_loc, nullptr); - elem_result_loc->value->special = ConstValSpecialRuntime; - ir_analyze_store_ptr(ira, &elem_result_loc->base, elem_result_loc, deref, false); - } - } - } - - const_ptrs.deinit(); - - IrInstGen *result = ir_get_deref(ira, &instruction->base.base, result_loc, nullptr); - // If the result is a tuple, we are allowed to return a struct that uses ConstValSpecialRuntime fields at comptime. - if (instr_is_comptime(result) || is_tuple(container_type)) - return result; - - if (is_comptime) { - ir_add_error(ira, &first_non_const_instruction->base, - buf_sprintf("unable to evaluate constant expression")); - return ira->codegen->invalid_inst_gen; - } - - ZigType *result_elem_type = result_loc->value->type->data.pointer.child_type; - if (is_slice(result_elem_type)) { - ErrorMsg *msg = ir_add_error(ira, &instruction->base.base, - buf_sprintf("runtime-initialized array cannot be casted to slice type '%s'", - buf_ptr(&result_elem_type->name))); - add_error_note(ira->codegen, msg, first_non_const_instruction->base.source_node, - buf_sprintf("this value is not comptime-known")); - return ira->codegen->invalid_inst_gen; - } - return result; -} - -static IrInstGen *ir_analyze_instruction_container_init_fields(IrAnalyze *ira, - IrInstSrcContainerInitFields *instruction) -{ - ir_assert(instruction->result_loc != nullptr, &instruction->base.base); - IrInstGen *result_loc = instruction->result_loc->child; - if (type_is_invalid(result_loc->value->type)) - return result_loc; - - ir_assert(result_loc->value->type->id == ZigTypeIdPointer, &instruction->base.base); - if (result_loc->value->type->data.pointer.is_const) { - ir_add_error(ira, &instruction->base.base, buf_sprintf("cannot assign to constant")); - return ira->codegen->invalid_inst_gen; - } - - ZigType *container_type = result_loc->value->type->data.pointer.child_type; - - return ir_analyze_container_init_fields(ira, &instruction->base.base, container_type, - instruction->field_count, instruction->fields, result_loc); -} - -static IrInstGen *ir_analyze_instruction_compile_err(IrAnalyze *ira, IrInstSrcCompileErr *instruction) { - IrInstGen *msg_value = instruction->msg->child; - Buf *msg_buf = ir_resolve_str(ira, msg_value); - if (!msg_buf) - return ira->codegen->invalid_inst_gen; - - ir_add_error(ira, &instruction->base.base, msg_buf); - - return ira->codegen->invalid_inst_gen; -} - -static IrInstGen *ir_analyze_instruction_compile_log(IrAnalyze *ira, IrInstSrcCompileLog *instruction) { - Buf buf = BUF_INIT; - fprintf(stderr, "| "); - for (size_t i = 0; i < instruction->msg_count; i += 1) { - IrInstGen *msg = instruction->msg_list[i]->child; - if (type_is_invalid(msg->value->type)) - return ira->codegen->invalid_inst_gen; - buf_resize(&buf, 0); - if (msg->value->special == ConstValSpecialLazy) { - // Resolve any lazy value that's passed, we need its value - if (ir_resolve_lazy(ira->codegen, msg->base.source_node, msg->value)) - return ira->codegen->invalid_inst_gen; - } - render_const_value(ira->codegen, &buf, msg->value); - const char *comma_str = (i != 0) ? ", " : ""; - fprintf(stderr, "%s%s", comma_str, buf_ptr(&buf)); - } - fprintf(stderr, "\n"); - - auto *expr = &instruction->base.base.source_node->data.fn_call_expr; - if (!expr->seen) { - // Here we bypass higher level functions such as ir_add_error because we do not want - // invalidate_exec to be called. - add_node_error(ira->codegen, instruction->base.base.source_node, buf_sprintf("found compile log statement")); - } - expr->seen = true; - - return ir_const_void(ira, &instruction->base.base); -} - -static IrInstGen *ir_analyze_instruction_err_name(IrAnalyze *ira, IrInstSrcErrName *instruction) { - IrInstGen *value = instruction->value->child; - if (type_is_invalid(value->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *casted_value = ir_implicit_cast(ira, value, ira->codegen->builtin_types.entry_global_error_set); - if (type_is_invalid(casted_value->value->type)) - return ira->codegen->invalid_inst_gen; - - ZigType *u8_ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, - true, false, PtrLenUnknown, 0, 0, 0, false); - ZigType *str_type = get_slice_type(ira->codegen, u8_ptr_type); - if (instr_is_comptime(casted_value)) { - ZigValue *val = ir_resolve_const(ira, casted_value, UndefBad); - if (val == nullptr) - return ira->codegen->invalid_inst_gen; - ErrorTableEntry *err = casted_value->value->data.x_err_set; - if (!err->cached_error_name_val) { - ZigValue *array_val = create_const_str_lit(ira->codegen, &err->name)->data.x_ptr.data.ref.pointee; - err->cached_error_name_val = create_const_slice(ira->codegen, array_val, 0, buf_len(&err->name), true); - } - IrInstGen *result = ir_const(ira, &instruction->base.base, nullptr); - copy_const_val(ira->codegen, result->value, err->cached_error_name_val); - result->value->type = str_type; - return result; - } - - ira->codegen->generate_error_name_table = true; - - return ir_build_err_name_gen(ira, &instruction->base.base, value, str_type); -} - -static IrInstGen *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrInstSrcTagName *instruction) { - Error err; - IrInstGen *target = instruction->target->child; - if (type_is_invalid(target->value->type)) - return ira->codegen->invalid_inst_gen; - - if (target->value->type->id == ZigTypeIdEnumLiteral) { - IrInstGen *result = ir_const(ira, &instruction->base.base, nullptr); - Buf *field_name = target->value->data.x_enum_literal; - ZigValue *array_val = create_const_str_lit(ira->codegen, field_name)->data.x_ptr.data.ref.pointee; - init_const_slice(ira->codegen, result->value, array_val, 0, buf_len(field_name), true); - return result; - } - - if (target->value->type->id == ZigTypeIdUnion) { - target = ir_analyze_union_tag(ira, &instruction->base.base, target, instruction->base.is_gen); - if (type_is_invalid(target->value->type)) - return ira->codegen->invalid_inst_gen; - } - - if (target->value->type->id != ZigTypeIdEnum) { - ir_add_error(ira, &target->base, - buf_sprintf("expected enum tag, found '%s'", buf_ptr(&target->value->type->name))); - return ira->codegen->invalid_inst_gen; - } - - if (target->value->type->data.enumeration.src_field_count == 1 && - !target->value->type->data.enumeration.non_exhaustive) { - TypeEnumField *only_field = &target->value->type->data.enumeration.fields[0]; - ZigValue *array_val = create_const_str_lit(ira->codegen, only_field->name)->data.x_ptr.data.ref.pointee; - IrInstGen *result = ir_const(ira, &instruction->base.base, nullptr); - init_const_slice(ira->codegen, result->value, array_val, 0, buf_len(only_field->name), true); - return result; - } - - if (instr_is_comptime(target)) { - if ((err = type_resolve(ira->codegen, target->value->type, ResolveStatusZeroBitsKnown))) - return ira->codegen->invalid_inst_gen; - TypeEnumField *field = find_enum_field_by_tag(target->value->type, &target->value->data.x_bigint); - if (field == nullptr) { - Buf *int_buf = buf_alloc(); - bigint_append_buf(int_buf, &target->value->data.x_bigint, 10); - - ir_add_error(ira, &target->base, - buf_sprintf("no tag by value %s", buf_ptr(int_buf))); - return ira->codegen->invalid_inst_gen; - } - ZigValue *array_val = create_const_str_lit(ira->codegen, field->name)->data.x_ptr.data.ref.pointee; - IrInstGen *result = ir_const(ira, &instruction->base.base, nullptr); - init_const_slice(ira->codegen, result->value, array_val, 0, buf_len(field->name), true); - return result; - } - - ZigType *u8_ptr_type = get_pointer_to_type_extra( - ira->codegen, ira->codegen->builtin_types.entry_u8, - true, false, PtrLenUnknown, - 0, 0, 0, false); - ZigType *result_type = get_slice_type(ira->codegen, u8_ptr_type); - return ir_build_tag_name_gen(ira, &instruction->base.base, target, result_type); -} - -static IrInstGen *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira, - IrInstSrcFieldParentPtr *instruction) -{ - Error err; - IrInstGen *type_value = instruction->type_value->child; - ZigType *container_type = ir_resolve_type(ira, type_value); - if (type_is_invalid(container_type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *field_name_value = instruction->field_name->child; - Buf *field_name = ir_resolve_str(ira, field_name_value); - if (!field_name) - return ira->codegen->invalid_inst_gen; - - IrInstGen *field_ptr = instruction->field_ptr->child; - if (type_is_invalid(field_ptr->value->type)) - return ira->codegen->invalid_inst_gen; - - if (container_type->id != ZigTypeIdStruct) { - ir_add_error(ira, &type_value->base, - buf_sprintf("expected struct type, found '%s'", buf_ptr(&container_type->name))); - return ira->codegen->invalid_inst_gen; - } - - if ((err = type_resolve(ira->codegen, container_type, ResolveStatusSizeKnown))) - return ira->codegen->invalid_inst_gen; - - TypeStructField *field = find_struct_type_field(container_type, field_name); - if (field == nullptr) { - ir_add_error(ira, &field_name_value->base, - buf_sprintf("struct '%s' has no field '%s'", - buf_ptr(&container_type->name), buf_ptr(field_name))); - return ira->codegen->invalid_inst_gen; - } - - if (field_ptr->value->type->id != ZigTypeIdPointer) { - ir_add_error(ira, &field_ptr->base, - buf_sprintf("expected pointer, found '%s'", buf_ptr(&field_ptr->value->type->name))); - return ira->codegen->invalid_inst_gen; - } - - bool is_packed = (container_type->data.structure.layout == ContainerLayoutPacked); - uint32_t field_ptr_align = is_packed ? 1 : get_abi_alignment(ira->codegen, field->type_entry); - uint32_t parent_ptr_align = is_packed ? 1 : get_abi_alignment(ira->codegen, container_type); - - ZigType *field_ptr_type = get_pointer_to_type_extra(ira->codegen, field->type_entry, - field_ptr->value->type->data.pointer.is_const, - field_ptr->value->type->data.pointer.is_volatile, - PtrLenSingle, - field_ptr_align, 0, 0, false); - IrInstGen *casted_field_ptr = ir_implicit_cast(ira, field_ptr, field_ptr_type); - if (type_is_invalid(casted_field_ptr->value->type)) - return ira->codegen->invalid_inst_gen; - - ZigType *result_type = get_pointer_to_type_extra(ira->codegen, container_type, - casted_field_ptr->value->type->data.pointer.is_const, - casted_field_ptr->value->type->data.pointer.is_volatile, - PtrLenSingle, - parent_ptr_align, 0, 0, false); - - if (instr_is_comptime(casted_field_ptr)) { - ZigValue *field_ptr_val = ir_resolve_const(ira, casted_field_ptr, UndefBad); - if (!field_ptr_val) - return ira->codegen->invalid_inst_gen; - - if (field_ptr_val->data.x_ptr.special != ConstPtrSpecialBaseStruct) { - ir_add_error(ira, &field_ptr->base, buf_sprintf("pointer value not based on parent struct")); - return ira->codegen->invalid_inst_gen; - } - - size_t ptr_field_index = field_ptr_val->data.x_ptr.data.base_struct.field_index; - if (ptr_field_index != field->src_index) { - ir_add_error(ira, &instruction->base.base, - buf_sprintf("field '%s' has index %" ZIG_PRI_usize " but pointer value is index %" ZIG_PRI_usize " of struct '%s'", - buf_ptr(field->name), field->src_index, - ptr_field_index, buf_ptr(&container_type->name))); - return ira->codegen->invalid_inst_gen; - } - - IrInstGen *result = ir_const(ira, &instruction->base.base, result_type); - ZigValue *out_val = result->value; - out_val->data.x_ptr.special = ConstPtrSpecialRef; - out_val->data.x_ptr.data.ref.pointee = field_ptr_val->data.x_ptr.data.base_struct.struct_val; - out_val->data.x_ptr.mut = field_ptr_val->data.x_ptr.mut; - return result; - } - - return ir_build_field_parent_ptr_gen(ira, &instruction->base.base, casted_field_ptr, field, result_type); -} - -static TypeStructField *validate_byte_offset(IrAnalyze *ira, - IrInstGen *type_value, - IrInstGen *field_name_value, - size_t *byte_offset) -{ - ZigType *container_type = ir_resolve_type(ira, type_value); - if (type_is_invalid(container_type)) - return nullptr; - - Error err; - if ((err = type_resolve(ira->codegen, container_type, ResolveStatusSizeKnown))) - return nullptr; - - Buf *field_name = ir_resolve_str(ira, field_name_value); - if (!field_name) - return nullptr; - - if (container_type->id != ZigTypeIdStruct) { - ir_add_error(ira, &type_value->base, - buf_sprintf("expected struct type, found '%s'", buf_ptr(&container_type->name))); - return nullptr; - } - - TypeStructField *field = find_struct_type_field(container_type, field_name); - if (field == nullptr) { - ir_add_error(ira, &field_name_value->base, - buf_sprintf("struct '%s' has no field '%s'", - buf_ptr(&container_type->name), buf_ptr(field_name))); - return nullptr; - } - - if (!type_has_bits(ira->codegen, field->type_entry)) { - ir_add_error(ira, &field_name_value->base, - buf_sprintf("zero-bit field '%s' in struct '%s' has no offset", - buf_ptr(field_name), buf_ptr(&container_type->name))); - return nullptr; - } - - *byte_offset = field->offset; - return field; -} - -static IrInstGen *ir_analyze_instruction_byte_offset_of(IrAnalyze *ira, IrInstSrcByteOffsetOf *instruction) { - IrInstGen *type_value = instruction->type_value->child; - if (type_is_invalid(type_value->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *field_name_value = instruction->field_name->child; - size_t byte_offset = 0; - if (!validate_byte_offset(ira, type_value, field_name_value, &byte_offset)) - return ira->codegen->invalid_inst_gen; - - - return ir_const_unsigned(ira, &instruction->base.base, byte_offset); -} - -static IrInstGen *ir_analyze_instruction_bit_offset_of(IrAnalyze *ira, IrInstSrcBitOffsetOf *instruction) { - IrInstGen *type_value = instruction->type_value->child; - if (type_is_invalid(type_value->value->type)) - return ira->codegen->invalid_inst_gen; - IrInstGen *field_name_value = instruction->field_name->child; - size_t byte_offset = 0; - TypeStructField *field = nullptr; - if (!(field = validate_byte_offset(ira, type_value, field_name_value, &byte_offset))) - return ira->codegen->invalid_inst_gen; - - size_t bit_offset = byte_offset * 8 + field->bit_offset_in_host; - return ir_const_unsigned(ira, &instruction->base.base, bit_offset); -} - -static void ensure_field_index(ZigType *type, const char *field_name, size_t index) { - Buf *field_name_buf; - - assert(type != nullptr && !type_is_invalid(type)); - field_name_buf = buf_create_from_str(field_name); - TypeStructField *field = find_struct_type_field(type, field_name_buf); - buf_deinit(field_name_buf); - - if (field == nullptr || field->src_index != index) - zig_panic("reference to unknown field %s", field_name); -} - -static ZigType *ir_type_info_get_type(IrAnalyze *ira, const char *type_name, ZigType *root) { - Error err; - ZigType *type_info_type = get_builtin_type(ira->codegen, "TypeInfo"); - assert(type_info_type->id == ZigTypeIdUnion); - if ((err = type_resolve(ira->codegen, type_info_type, ResolveStatusSizeKnown))) { - zig_unreachable(); - } - - if (type_name == nullptr && root == nullptr) - return type_info_type; - else if (type_name == nullptr) - return root; - - ZigType *root_type = (root == nullptr) ? type_info_type : root; - - ScopeDecls *type_info_scope = get_container_scope(root_type); - assert(type_info_scope != nullptr); - - Buf field_name = BUF_INIT; - buf_init_from_str(&field_name, type_name); - auto entry = type_info_scope->decl_table.get(&field_name); - buf_deinit(&field_name); - - TldVar *tld = (TldVar *)entry; - assert(tld->base.id == TldIdVar); - - ZigVar *var = tld->var; - - assert(var->const_value->type->id == ZigTypeIdMetaType); - - return ir_resolve_const_type(ira->codegen, ira->new_irb.exec, nullptr, var->const_value); -} - -static Error ir_make_type_info_decls(IrAnalyze *ira, IrInst* source_instr, ZigValue *out_val, - ScopeDecls *decls_scope, bool resolve_types) -{ - Error err; - ZigType *type_info_declaration_type = ir_type_info_get_type(ira, "Declaration", nullptr); - if ((err = type_resolve(ira->codegen, type_info_declaration_type, ResolveStatusSizeKnown))) - return err; - - ensure_field_index(type_info_declaration_type, "name", 0); - ensure_field_index(type_info_declaration_type, "is_pub", 1); - ensure_field_index(type_info_declaration_type, "data", 2); - - if (!resolve_types) { - ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, type_info_declaration_type, - false, false, PtrLenUnknown, 0, 0, 0, false); - - out_val->special = ConstValSpecialLazy; - out_val->type = get_slice_type(ira->codegen, ptr_type); - - LazyValueTypeInfoDecls *lazy_type_info_decls = heap::c_allocator.create(); - lazy_type_info_decls->ira = ira; ira_ref(ira); - out_val->data.x_lazy = &lazy_type_info_decls->base; - lazy_type_info_decls->base.id = LazyValueIdTypeInfoDecls; - - lazy_type_info_decls->source_instr = source_instr; - lazy_type_info_decls->decls_scope = decls_scope; - - return ErrorNone; - } - - ZigType *type_info_declaration_data_type = ir_type_info_get_type(ira, "Data", type_info_declaration_type); - if ((err = type_resolve(ira->codegen, type_info_declaration_data_type, ResolveStatusSizeKnown))) - return err; - - ZigType *type_info_fn_decl_type = ir_type_info_get_type(ira, "FnDecl", type_info_declaration_data_type); - if ((err = type_resolve(ira->codegen, type_info_fn_decl_type, ResolveStatusSizeKnown))) - return err; - - ZigType *type_info_fn_decl_inline_type = ir_type_info_get_type(ira, "Inline", type_info_fn_decl_type); - if ((err = type_resolve(ira->codegen, type_info_fn_decl_inline_type, ResolveStatusSizeKnown))) - return err; - - // The unresolved declarations are collected in a separate queue to avoid - // modifying decl_table while iterating over it - ZigList resolve_decl_queue{}; - - auto decl_it = decls_scope->decl_table.entry_iterator(); - decltype(decls_scope->decl_table)::Entry *curr_entry = nullptr; - while ((curr_entry = decl_it.next()) != nullptr) { - if (curr_entry->value->resolution == TldResolutionInvalid) { - return ErrorSemanticAnalyzeFail; - } - - if (curr_entry->value->resolution == TldResolutionResolving) { - ir_error_dependency_loop(ira, source_instr); - return ErrorSemanticAnalyzeFail; - } - - // If the declaration is unresolved, force it to be resolved again. - if (curr_entry->value->resolution == TldResolutionUnresolved) - resolve_decl_queue.append(curr_entry->value); - } - - for (size_t i = 0; i < resolve_decl_queue.length; i++) { - Tld *decl = resolve_decl_queue.at(i); - resolve_top_level_decl(ira->codegen, decl, decl->source_node, false); - if (decl->resolution == TldResolutionInvalid) { - return ErrorSemanticAnalyzeFail; - } - } - - resolve_decl_queue.deinit(); - - // Loop through our declarations once to figure out how many declarations we will generate info for. - int declaration_count = 0; - decl_it = decls_scope->decl_table.entry_iterator(); - while ((curr_entry = decl_it.next()) != nullptr) { - // Skip comptime blocks and test functions. - if (curr_entry->value->id == TldIdCompTime) - continue; - - if (curr_entry->value->id == TldIdFn) { - ZigFn *fn_entry = ((TldFn *)curr_entry->value)->fn_entry; - if (fn_entry->is_test) - continue; - } - - declaration_count += 1; - } - - ZigValue *declaration_array = ira->codegen->pass1_arena->create(); - declaration_array->special = ConstValSpecialStatic; - declaration_array->type = get_array_type(ira->codegen, type_info_declaration_type, declaration_count, nullptr); - declaration_array->data.x_array.special = ConstArraySpecialNone; - declaration_array->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(declaration_count); - init_const_slice(ira->codegen, out_val, declaration_array, 0, declaration_count, false); - - // Loop through the declarations and generate info. - decl_it = decls_scope->decl_table.entry_iterator(); - curr_entry = nullptr; - int declaration_index = 0; - while ((curr_entry = decl_it.next()) != nullptr) { - // Skip comptime blocks and test functions. - if (curr_entry->value->id == TldIdCompTime) { - continue; - } else if (curr_entry->value->id == TldIdFn) { - ZigFn *fn_entry = ((TldFn *)curr_entry->value)->fn_entry; - if (fn_entry->is_test) - continue; - } - - ZigValue *declaration_val = &declaration_array->data.x_array.data.s_none.elements[declaration_index]; - - declaration_val->special = ConstValSpecialStatic; - declaration_val->type = type_info_declaration_type; - - ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 3); - ZigValue *name = create_const_str_lit(ira->codegen, curr_entry->key)->data.x_ptr.data.ref.pointee; - init_const_slice(ira->codegen, inner_fields[0], name, 0, buf_len(curr_entry->key), true); - inner_fields[1]->special = ConstValSpecialStatic; - inner_fields[1]->type = ira->codegen->builtin_types.entry_bool; - inner_fields[1]->data.x_bool = curr_entry->value->visib_mod == VisibModPub; - inner_fields[2]->special = ConstValSpecialStatic; - inner_fields[2]->type = type_info_declaration_data_type; - inner_fields[2]->parent.id = ConstParentIdStruct; - inner_fields[2]->parent.data.p_struct.struct_val = declaration_val; - inner_fields[2]->parent.data.p_struct.field_index = 1; - - switch (curr_entry->value->id) { - case TldIdVar: - { - ZigVar *var = ((TldVar *)curr_entry->value)->var; - assert(var != nullptr); - - if ((err = type_resolve(ira->codegen, var->const_value->type, ResolveStatusSizeKnown))) - return ErrorSemanticAnalyzeFail; - - if (var->const_value->type->id == ZigTypeIdMetaType) { - // We have a variable of type 'type', so it's actually a type declaration. - // 0: Data.Type: type - bigint_init_unsigned(&inner_fields[2]->data.x_union.tag, 0); - inner_fields[2]->data.x_union.payload = var->const_value; - } else { - // We have a variable of another type, so we store the type of the variable. - // 1: Data.Var: type - bigint_init_unsigned(&inner_fields[2]->data.x_union.tag, 1); - - ZigValue *payload = ira->codegen->pass1_arena->create(); - payload->special = ConstValSpecialStatic; - payload->type = ira->codegen->builtin_types.entry_type; - payload->data.x_type = var->const_value->type; - - inner_fields[2]->data.x_union.payload = payload; - } - - break; - } - case TldIdFn: - { - // 2: Data.Fn: Data.FnDecl - bigint_init_unsigned(&inner_fields[2]->data.x_union.tag, 2); - - ZigFn *fn_entry = ((TldFn *)curr_entry->value)->fn_entry; - assert(!fn_entry->is_test); - assert(fn_entry->type_entry != nullptr); - - AstNodeFnProto *fn_node = &fn_entry->proto_node->data.fn_proto; - - ZigValue *fn_decl_val = ira->codegen->pass1_arena->create(); - fn_decl_val->special = ConstValSpecialStatic; - fn_decl_val->type = type_info_fn_decl_type; - fn_decl_val->parent.id = ConstParentIdUnion; - fn_decl_val->parent.data.p_union.union_val = inner_fields[2]; - - ZigValue **fn_decl_fields = alloc_const_vals_ptrs(ira->codegen, 9); - fn_decl_val->data.x_struct.fields = fn_decl_fields; - - // fn_type: type - ensure_field_index(fn_decl_val->type, "fn_type", 0); - fn_decl_fields[0]->special = ConstValSpecialStatic; - fn_decl_fields[0]->type = ira->codegen->builtin_types.entry_type; - fn_decl_fields[0]->data.x_type = fn_entry->type_entry; - // inline_type: Data.FnDecl.Inline - ensure_field_index(fn_decl_val->type, "inline_type", 1); - fn_decl_fields[1]->special = ConstValSpecialStatic; - fn_decl_fields[1]->type = type_info_fn_decl_inline_type; - bigint_init_unsigned(&fn_decl_fields[1]->data.x_enum_tag, fn_entry->fn_inline); - // is_var_args: bool - ensure_field_index(fn_decl_val->type, "is_var_args", 2); - bool is_varargs = fn_node->is_var_args; - fn_decl_fields[2]->special = ConstValSpecialStatic; - fn_decl_fields[2]->type = ira->codegen->builtin_types.entry_bool; - fn_decl_fields[2]->data.x_bool = is_varargs; - // is_extern: bool - ensure_field_index(fn_decl_val->type, "is_extern", 3); - fn_decl_fields[3]->special = ConstValSpecialStatic; - fn_decl_fields[3]->type = ira->codegen->builtin_types.entry_bool; - fn_decl_fields[3]->data.x_bool = fn_node->is_extern; - // is_export: bool - ensure_field_index(fn_decl_val->type, "is_export", 4); - fn_decl_fields[4]->special = ConstValSpecialStatic; - fn_decl_fields[4]->type = ira->codegen->builtin_types.entry_bool; - fn_decl_fields[4]->data.x_bool = fn_node->is_export; - // lib_name: ?[]const u8 - ensure_field_index(fn_decl_val->type, "lib_name", 5); - fn_decl_fields[5]->special = ConstValSpecialStatic; - ZigType *u8_ptr = get_pointer_to_type_extra( - ira->codegen, ira->codegen->builtin_types.entry_u8, - true, false, PtrLenUnknown, - 0, 0, 0, false); - fn_decl_fields[5]->type = get_optional_type(ira->codegen, get_slice_type(ira->codegen, u8_ptr)); - if (fn_node->is_extern && fn_node->lib_name != nullptr && buf_len(fn_node->lib_name) > 0) { - fn_decl_fields[5]->data.x_optional = ira->codegen->pass1_arena->create(); - ZigValue *lib_name = create_const_str_lit(ira->codegen, fn_node->lib_name)->data.x_ptr.data.ref.pointee; - init_const_slice(ira->codegen, fn_decl_fields[5]->data.x_optional, lib_name, 0, - buf_len(fn_node->lib_name), true); - } else { - fn_decl_fields[5]->data.x_optional = nullptr; - } - // return_type: type - ensure_field_index(fn_decl_val->type, "return_type", 6); - fn_decl_fields[6]->special = ConstValSpecialStatic; - fn_decl_fields[6]->type = ira->codegen->builtin_types.entry_type; - fn_decl_fields[6]->data.x_type = fn_entry->type_entry->data.fn.fn_type_id.return_type; - // arg_names: [][] const u8 - ensure_field_index(fn_decl_val->type, "arg_names", 7); - size_t fn_arg_count = fn_entry->variable_list.length; - ZigValue *fn_arg_name_array = ira->codegen->pass1_arena->create(); - fn_arg_name_array->special = ConstValSpecialStatic; - fn_arg_name_array->type = get_array_type(ira->codegen, - get_slice_type(ira->codegen, u8_ptr), fn_arg_count, nullptr); - fn_arg_name_array->data.x_array.special = ConstArraySpecialNone; - fn_arg_name_array->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(fn_arg_count); - - init_const_slice(ira->codegen, fn_decl_fields[7], fn_arg_name_array, 0, fn_arg_count, false); - - for (size_t fn_arg_index = 0; fn_arg_index < fn_arg_count; fn_arg_index++) { - ZigVar *arg_var = fn_entry->variable_list.at(fn_arg_index); - ZigValue *fn_arg_name_val = &fn_arg_name_array->data.x_array.data.s_none.elements[fn_arg_index]; - ZigValue *arg_name = create_const_str_lit(ira->codegen, - buf_create_from_str(arg_var->name))->data.x_ptr.data.ref.pointee; - init_const_slice(ira->codegen, fn_arg_name_val, arg_name, 0, strlen(arg_var->name), true); - fn_arg_name_val->parent.id = ConstParentIdArray; - fn_arg_name_val->parent.data.p_array.array_val = fn_arg_name_array; - fn_arg_name_val->parent.data.p_array.elem_index = fn_arg_index; - } - - inner_fields[2]->data.x_union.payload = fn_decl_val; - break; - } - case TldIdContainer: - { - ZigType *type_entry = ((TldContainer *)curr_entry->value)->type_entry; - if ((err = type_resolve(ira->codegen, type_entry, ResolveStatusSizeKnown))) - return ErrorSemanticAnalyzeFail; - - // This is a type. - bigint_init_unsigned(&inner_fields[2]->data.x_union.tag, 0); - - ZigValue *payload = ira->codegen->pass1_arena->create(); - payload->special = ConstValSpecialStatic; - payload->type = ira->codegen->builtin_types.entry_type; - payload->data.x_type = type_entry; - - inner_fields[2]->data.x_union.payload = payload; - - break; - } - default: - zig_unreachable(); - } - - declaration_val->data.x_struct.fields = inner_fields; - declaration_index += 1; - } - - assert(declaration_index == declaration_count); - return ErrorNone; -} - -static BuiltinPtrSize ptr_len_to_size_enum_index(PtrLen ptr_len) { - switch (ptr_len) { - case PtrLenSingle: - return BuiltinPtrSizeOne; - case PtrLenUnknown: - return BuiltinPtrSizeMany; - case PtrLenC: - return BuiltinPtrSizeC; - } - zig_unreachable(); -} - -static PtrLen size_enum_index_to_ptr_len(BuiltinPtrSize size_enum_index) { - switch (size_enum_index) { - case BuiltinPtrSizeOne: - return PtrLenSingle; - case BuiltinPtrSizeMany: - case BuiltinPtrSizeSlice: - return PtrLenUnknown; - case BuiltinPtrSizeC: - return PtrLenC; - } - zig_unreachable(); -} - -static ZigValue *create_ptr_like_type_info(IrAnalyze *ira, IrInst *source_instr, ZigType *ptr_type_entry) { - ZigType *attrs_type; - BuiltinPtrSize size_enum_index; - if (is_slice(ptr_type_entry)) { - TypeStructField *ptr_field = ptr_type_entry->data.structure.fields[slice_ptr_index]; - attrs_type = resolve_struct_field_type(ira->codegen, ptr_field); - size_enum_index = BuiltinPtrSizeSlice; - } else if (ptr_type_entry->id == ZigTypeIdPointer) { - attrs_type = ptr_type_entry; - size_enum_index = ptr_len_to_size_enum_index(ptr_type_entry->data.pointer.ptr_len); - } else { - zig_unreachable(); - } - - ZigType *type_info_pointer_type = ir_type_info_get_type(ira, "Pointer", nullptr); - assertNoError(type_resolve(ira->codegen, type_info_pointer_type, ResolveStatusSizeKnown)); - - ZigValue *result = ira->codegen->pass1_arena->create(); - result->special = ConstValSpecialStatic; - result->type = type_info_pointer_type; - - ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 7); - result->data.x_struct.fields = fields; - - // size: Size - ensure_field_index(result->type, "size", 0); - ZigType *type_info_pointer_size_type = ir_type_info_get_type(ira, "Size", type_info_pointer_type); - assertNoError(type_resolve(ira->codegen, type_info_pointer_size_type, ResolveStatusSizeKnown)); - fields[0]->special = ConstValSpecialStatic; - fields[0]->type = type_info_pointer_size_type; - bigint_init_unsigned(&fields[0]->data.x_enum_tag, size_enum_index); - - // is_const: bool - ensure_field_index(result->type, "is_const", 1); - fields[1]->special = ConstValSpecialStatic; - fields[1]->type = ira->codegen->builtin_types.entry_bool; - fields[1]->data.x_bool = attrs_type->data.pointer.is_const; - // is_volatile: bool - ensure_field_index(result->type, "is_volatile", 2); - fields[2]->special = ConstValSpecialStatic; - fields[2]->type = ira->codegen->builtin_types.entry_bool; - fields[2]->data.x_bool = attrs_type->data.pointer.is_volatile; - // alignment: u32 - ensure_field_index(result->type, "alignment", 3); - fields[3]->type = ira->codegen->builtin_types.entry_num_lit_int; - if (attrs_type->data.pointer.explicit_alignment != 0) { - fields[3]->special = ConstValSpecialStatic; - bigint_init_unsigned(&fields[3]->data.x_bigint, attrs_type->data.pointer.explicit_alignment); - } else { - LazyValueAlignOf *lazy_align_of = heap::c_allocator.create(); - lazy_align_of->ira = ira; ira_ref(ira); - fields[3]->special = ConstValSpecialLazy; - fields[3]->data.x_lazy = &lazy_align_of->base; - lazy_align_of->base.id = LazyValueIdAlignOf; - lazy_align_of->target_type = ir_const_type(ira, source_instr, attrs_type->data.pointer.child_type); - } - // child: type - ensure_field_index(result->type, "child", 4); - fields[4]->special = ConstValSpecialStatic; - fields[4]->type = ira->codegen->builtin_types.entry_type; - fields[4]->data.x_type = attrs_type->data.pointer.child_type; - // is_allowzero: bool - ensure_field_index(result->type, "is_allowzero", 5); - fields[5]->special = ConstValSpecialStatic; - fields[5]->type = ira->codegen->builtin_types.entry_bool; - fields[5]->data.x_bool = attrs_type->data.pointer.allow_zero; - // sentinel: anytype - ensure_field_index(result->type, "sentinel", 6); - fields[6]->special = ConstValSpecialStatic; - if (attrs_type->data.pointer.sentinel != nullptr) { - fields[6]->type = get_optional_type(ira->codegen, attrs_type->data.pointer.child_type); - set_optional_payload(fields[6], attrs_type->data.pointer.sentinel); - } else { - fields[6]->type = ira->codegen->builtin_types.entry_null; - } - - return result; -}; - -static void make_enum_field_val(IrAnalyze *ira, ZigValue *enum_field_val, TypeEnumField *enum_field, - ZigType *type_info_enum_field_type) -{ - enum_field_val->special = ConstValSpecialStatic; - enum_field_val->type = type_info_enum_field_type; - - ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 2); - inner_fields[1]->special = ConstValSpecialStatic; - inner_fields[1]->type = ira->codegen->builtin_types.entry_num_lit_int; - - ZigValue *name = create_const_str_lit(ira->codegen, enum_field->name)->data.x_ptr.data.ref.pointee; - init_const_slice(ira->codegen, inner_fields[0], name, 0, buf_len(enum_field->name), true); - - bigint_init_bigint(&inner_fields[1]->data.x_bigint, &enum_field->value); - - enum_field_val->data.x_struct.fields = inner_fields; -} - -static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigType *type_entry, - ZigValue **out) -{ - Error err; - assert(type_entry != nullptr); - assert(!type_is_invalid(type_entry)); - - auto entry = ira->codegen->type_info_cache.maybe_get(type_entry); - if (entry != nullptr) { - *out = entry->value; - return ErrorNone; - } - - ZigValue *result = nullptr; - switch (type_entry->id) { - case ZigTypeIdInvalid: - zig_unreachable(); - case ZigTypeIdMetaType: - case ZigTypeIdVoid: - case ZigTypeIdBool: - case ZigTypeIdUnreachable: - case ZigTypeIdComptimeFloat: - case ZigTypeIdComptimeInt: - case ZigTypeIdEnumLiteral: - case ZigTypeIdUndefined: - case ZigTypeIdNull: - case ZigTypeIdOpaque: - result = ira->codegen->intern.for_void(); - break; - case ZigTypeIdInt: - { - result = ira->codegen->pass1_arena->create(); - result->special = ConstValSpecialStatic; - result->type = ir_type_info_get_type(ira, "Int", nullptr); - - ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 2); - result->data.x_struct.fields = fields; - - // is_signed: bool - ensure_field_index(result->type, "is_signed", 0); - fields[0]->special = ConstValSpecialStatic; - fields[0]->type = ira->codegen->builtin_types.entry_bool; - fields[0]->data.x_bool = type_entry->data.integral.is_signed; - // bits: u8 - ensure_field_index(result->type, "bits", 1); - fields[1]->special = ConstValSpecialStatic; - fields[1]->type = ira->codegen->builtin_types.entry_num_lit_int; - bigint_init_unsigned(&fields[1]->data.x_bigint, type_entry->data.integral.bit_count); - - break; - } - case ZigTypeIdFloat: - { - result = ira->codegen->pass1_arena->create(); - result->special = ConstValSpecialStatic; - result->type = ir_type_info_get_type(ira, "Float", nullptr); - - ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 1); - result->data.x_struct.fields = fields; - - // bits: u8 - ensure_field_index(result->type, "bits", 0); - fields[0]->special = ConstValSpecialStatic; - fields[0]->type = ira->codegen->builtin_types.entry_num_lit_int; - bigint_init_unsigned(&fields[0]->data.x_bigint, type_entry->data.floating.bit_count); - - break; - } - case ZigTypeIdPointer: - { - result = create_ptr_like_type_info(ira, source_instr, type_entry); - if (result == nullptr) - return ErrorSemanticAnalyzeFail; - break; - } - case ZigTypeIdArray: - { - result = ira->codegen->pass1_arena->create(); - result->special = ConstValSpecialStatic; - result->type = ir_type_info_get_type(ira, "Array", nullptr); - - ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 3); - result->data.x_struct.fields = fields; - - // len: usize - ensure_field_index(result->type, "len", 0); - fields[0]->special = ConstValSpecialStatic; - fields[0]->type = ira->codegen->builtin_types.entry_num_lit_int; - bigint_init_unsigned(&fields[0]->data.x_bigint, type_entry->data.array.len); - // child: type - ensure_field_index(result->type, "child", 1); - fields[1]->special = ConstValSpecialStatic; - fields[1]->type = ira->codegen->builtin_types.entry_type; - fields[1]->data.x_type = type_entry->data.array.child_type; - // sentinel: anytype - fields[2]->special = ConstValSpecialStatic; - fields[2]->type = get_optional_type(ira->codegen, type_entry->data.array.child_type); - fields[2]->data.x_optional = type_entry->data.array.sentinel; - break; - } - case ZigTypeIdVector: { - result = ira->codegen->pass1_arena->create(); - result->special = ConstValSpecialStatic; - result->type = ir_type_info_get_type(ira, "Vector", nullptr); - - ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 2); - result->data.x_struct.fields = fields; - - // len: usize - ensure_field_index(result->type, "len", 0); - fields[0]->special = ConstValSpecialStatic; - fields[0]->type = ira->codegen->builtin_types.entry_num_lit_int; - bigint_init_unsigned(&fields[0]->data.x_bigint, type_entry->data.vector.len); - // child: type - ensure_field_index(result->type, "child", 1); - fields[1]->special = ConstValSpecialStatic; - fields[1]->type = ira->codegen->builtin_types.entry_type; - fields[1]->data.x_type = type_entry->data.vector.elem_type; - - break; - } - case ZigTypeIdOptional: - { - result = ira->codegen->pass1_arena->create(); - result->special = ConstValSpecialStatic; - result->type = ir_type_info_get_type(ira, "Optional", nullptr); - - ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 1); - result->data.x_struct.fields = fields; - - // child: type - ensure_field_index(result->type, "child", 0); - fields[0]->special = ConstValSpecialStatic; - fields[0]->type = ira->codegen->builtin_types.entry_type; - fields[0]->data.x_type = type_entry->data.maybe.child_type; - - break; - } - case ZigTypeIdAnyFrame: { - result = ira->codegen->pass1_arena->create(); - result->special = ConstValSpecialStatic; - result->type = ir_type_info_get_type(ira, "AnyFrame", nullptr); - - ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 1); - result->data.x_struct.fields = fields; - - // child: ?type - ensure_field_index(result->type, "child", 0); - fields[0]->special = ConstValSpecialStatic; - fields[0]->type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type); - fields[0]->data.x_optional = (type_entry->data.any_frame.result_type == nullptr) ? nullptr : - create_const_type(ira->codegen, type_entry->data.any_frame.result_type); - break; - } - case ZigTypeIdEnum: - { - if ((err = type_resolve(ira->codegen, type_entry, ResolveStatusSizeKnown))) - return err; - - result = ira->codegen->pass1_arena->create(); - result->special = ConstValSpecialStatic; - result->type = ir_type_info_get_type(ira, "Enum", nullptr); - - ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 5); - result->data.x_struct.fields = fields; - - // layout: ContainerLayout - ensure_field_index(result->type, "layout", 0); - fields[0]->special = ConstValSpecialStatic; - fields[0]->type = ir_type_info_get_type(ira, "ContainerLayout", nullptr); - bigint_init_unsigned(&fields[0]->data.x_enum_tag, type_entry->data.enumeration.layout); - // tag_type: type - ensure_field_index(result->type, "tag_type", 1); - fields[1]->special = ConstValSpecialStatic; - fields[1]->type = ira->codegen->builtin_types.entry_type; - fields[1]->data.x_type = type_entry->data.enumeration.tag_int_type; - // fields: []TypeInfo.EnumField - ensure_field_index(result->type, "fields", 2); - - ZigType *type_info_enum_field_type = ir_type_info_get_type(ira, "EnumField", nullptr); - if ((err = type_resolve(ira->codegen, type_info_enum_field_type, ResolveStatusSizeKnown))) { - zig_unreachable(); - } - uint32_t enum_field_count = type_entry->data.enumeration.src_field_count; - - ZigValue *enum_field_array = ira->codegen->pass1_arena->create(); - enum_field_array->special = ConstValSpecialStatic; - enum_field_array->type = get_array_type(ira->codegen, type_info_enum_field_type, enum_field_count, nullptr); - enum_field_array->data.x_array.special = ConstArraySpecialNone; - enum_field_array->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(enum_field_count); - - init_const_slice(ira->codegen, fields[2], enum_field_array, 0, enum_field_count, false); - - for (uint32_t enum_field_index = 0; enum_field_index < enum_field_count; enum_field_index++) - { - TypeEnumField *enum_field = &type_entry->data.enumeration.fields[enum_field_index]; - ZigValue *enum_field_val = &enum_field_array->data.x_array.data.s_none.elements[enum_field_index]; - make_enum_field_val(ira, enum_field_val, enum_field, type_info_enum_field_type); - enum_field_val->parent.id = ConstParentIdArray; - enum_field_val->parent.data.p_array.array_val = enum_field_array; - enum_field_val->parent.data.p_array.elem_index = enum_field_index; - } - // decls: []TypeInfo.Declaration - ensure_field_index(result->type, "decls", 3); - if ((err = ir_make_type_info_decls(ira, source_instr, fields[3], - type_entry->data.enumeration.decls_scope, false))) - { - return err; - } - // is_exhaustive: bool - ensure_field_index(result->type, "is_exhaustive", 4); - fields[4]->special = ConstValSpecialStatic; - fields[4]->type = ira->codegen->builtin_types.entry_bool; - fields[4]->data.x_bool = !type_entry->data.enumeration.non_exhaustive; - - break; - } - case ZigTypeIdErrorSet: - { - result = ira->codegen->pass1_arena->create(); - result->special = ConstValSpecialStatic; - result->type = ir_type_info_get_type(ira, "ErrorSet", nullptr); - - ZigType *type_info_error_type = ir_type_info_get_type(ira, "Error", nullptr); - if (!resolve_inferred_error_set(ira->codegen, type_entry, source_instr->source_node)) { - return ErrorSemanticAnalyzeFail; - } - if (type_is_global_error_set(type_entry)) { - result->data.x_optional = nullptr; - break; - } - if ((err = type_resolve(ira->codegen, type_info_error_type, ResolveStatusSizeKnown))) { - zig_unreachable(); - } - ZigValue *slice_val = ira->codegen->pass1_arena->create(); - result->data.x_optional = slice_val; - - uint32_t error_count = type_entry->data.error_set.err_count; - ZigValue *error_array = ira->codegen->pass1_arena->create(); - error_array->special = ConstValSpecialStatic; - error_array->type = get_array_type(ira->codegen, type_info_error_type, error_count, nullptr); - error_array->data.x_array.special = ConstArraySpecialNone; - error_array->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(error_count); - - init_const_slice(ira->codegen, slice_val, error_array, 0, error_count, false); - for (uint32_t error_index = 0; error_index < error_count; error_index++) { - ErrorTableEntry *error = type_entry->data.error_set.errors[error_index]; - ZigValue *error_val = &error_array->data.x_array.data.s_none.elements[error_index]; - - error_val->special = ConstValSpecialStatic; - error_val->type = type_info_error_type; - - ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 1); - - ZigValue *name = nullptr; - if (error->cached_error_name_val != nullptr) - name = error->cached_error_name_val; - if (name == nullptr) - name = create_const_str_lit(ira->codegen, &error->name)->data.x_ptr.data.ref.pointee; - init_const_slice(ira->codegen, inner_fields[0], name, 0, buf_len(&error->name), true); - - error_val->data.x_struct.fields = inner_fields; - error_val->parent.id = ConstParentIdArray; - error_val->parent.data.p_array.array_val = error_array; - error_val->parent.data.p_array.elem_index = error_index; - } - - break; - } - case ZigTypeIdErrorUnion: - { - result = ira->codegen->pass1_arena->create(); - result->special = ConstValSpecialStatic; - result->type = ir_type_info_get_type(ira, "ErrorUnion", nullptr); - - ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 2); - result->data.x_struct.fields = fields; - - // error_set: type - ensure_field_index(result->type, "error_set", 0); - fields[0]->special = ConstValSpecialStatic; - fields[0]->type = ira->codegen->builtin_types.entry_type; - fields[0]->data.x_type = type_entry->data.error_union.err_set_type; - - // payload: type - ensure_field_index(result->type, "payload", 1); - fields[1]->special = ConstValSpecialStatic; - fields[1]->type = ira->codegen->builtin_types.entry_type; - fields[1]->data.x_type = type_entry->data.error_union.payload_type; - - break; - } - case ZigTypeIdUnion: - { - if ((err = type_resolve(ira->codegen, type_entry, ResolveStatusSizeKnown))) - return err; - - result = ira->codegen->pass1_arena->create(); - result->special = ConstValSpecialStatic; - result->type = ir_type_info_get_type(ira, "Union", nullptr); - - ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 4); - result->data.x_struct.fields = fields; - - // layout: ContainerLayout - ensure_field_index(result->type, "layout", 0); - fields[0]->special = ConstValSpecialStatic; - fields[0]->type = ir_type_info_get_type(ira, "ContainerLayout", nullptr); - bigint_init_unsigned(&fields[0]->data.x_enum_tag, type_entry->data.unionation.layout); - // tag_type: ?type - ensure_field_index(result->type, "tag_type", 1); - fields[1]->special = ConstValSpecialStatic; - fields[1]->type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type); - - AstNode *union_decl_node = type_entry->data.unionation.decl_node; - if (union_decl_node->data.container_decl.auto_enum || - union_decl_node->data.container_decl.init_arg_expr != nullptr) - { - ZigValue *tag_type = ira->codegen->pass1_arena->create(); - tag_type->special = ConstValSpecialStatic; - tag_type->type = ira->codegen->builtin_types.entry_type; - tag_type->data.x_type = type_entry->data.unionation.tag_type; - fields[1]->data.x_optional = tag_type; - } else { - fields[1]->data.x_optional = nullptr; - } - // fields: []TypeInfo.UnionField - ensure_field_index(result->type, "fields", 2); - - ZigType *type_info_union_field_type = ir_type_info_get_type(ira, "UnionField", nullptr); - if ((err = type_resolve(ira->codegen, type_info_union_field_type, ResolveStatusSizeKnown))) - zig_unreachable(); - uint32_t union_field_count = type_entry->data.unionation.src_field_count; - - ZigValue *union_field_array = ira->codegen->pass1_arena->create(); - union_field_array->special = ConstValSpecialStatic; - union_field_array->type = get_array_type(ira->codegen, type_info_union_field_type, union_field_count, nullptr); - union_field_array->data.x_array.special = ConstArraySpecialNone; - union_field_array->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(union_field_count); - - init_const_slice(ira->codegen, fields[2], union_field_array, 0, union_field_count, false); - - for (uint32_t union_field_index = 0; union_field_index < union_field_count; union_field_index++) { - TypeUnionField *union_field = &type_entry->data.unionation.fields[union_field_index]; - ZigValue *union_field_val = &union_field_array->data.x_array.data.s_none.elements[union_field_index]; - - union_field_val->special = ConstValSpecialStatic; - union_field_val->type = type_info_union_field_type; - - ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 2); - inner_fields[1]->special = ConstValSpecialStatic; - inner_fields[1]->type = ira->codegen->builtin_types.entry_type; - inner_fields[1]->data.x_type = union_field->type_entry; - - ZigValue *name = create_const_str_lit(ira->codegen, union_field->name)->data.x_ptr.data.ref.pointee; - init_const_slice(ira->codegen, inner_fields[0], name, 0, buf_len(union_field->name), true); - - union_field_val->data.x_struct.fields = inner_fields; - union_field_val->parent.id = ConstParentIdArray; - union_field_val->parent.data.p_array.array_val = union_field_array; - union_field_val->parent.data.p_array.elem_index = union_field_index; - } - // decls: []TypeInfo.Declaration - ensure_field_index(result->type, "decls", 3); - if ((err = ir_make_type_info_decls(ira, source_instr, fields[3], - type_entry->data.unionation.decls_scope, false))) - { - return err; - } - - break; - } - case ZigTypeIdStruct: - { - if (type_entry->data.structure.special == StructSpecialSlice) { - result = create_ptr_like_type_info(ira, source_instr, type_entry); - if (result == nullptr) - return ErrorSemanticAnalyzeFail; - break; - } - - if ((err = type_resolve(ira->codegen, type_entry, ResolveStatusSizeKnown))) - return err; - - result = ira->codegen->pass1_arena->create(); - result->special = ConstValSpecialStatic; - result->type = ir_type_info_get_type(ira, "Struct", nullptr); - - ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 4); - result->data.x_struct.fields = fields; - - // layout: ContainerLayout - ensure_field_index(result->type, "layout", 0); - fields[0]->special = ConstValSpecialStatic; - fields[0]->type = ir_type_info_get_type(ira, "ContainerLayout", nullptr); - bigint_init_unsigned(&fields[0]->data.x_enum_tag, type_entry->data.structure.layout); - // fields: []TypeInfo.StructField - ensure_field_index(result->type, "fields", 1); - - ZigType *type_info_struct_field_type = ir_type_info_get_type(ira, "StructField", nullptr); - if ((err = type_resolve(ira->codegen, type_info_struct_field_type, ResolveStatusSizeKnown))) { - zig_unreachable(); - } - uint32_t struct_field_count = type_entry->data.structure.src_field_count; - - ZigValue *struct_field_array = ira->codegen->pass1_arena->create(); - struct_field_array->special = ConstValSpecialStatic; - struct_field_array->type = get_array_type(ira->codegen, type_info_struct_field_type, struct_field_count, nullptr); - struct_field_array->data.x_array.special = ConstArraySpecialNone; - struct_field_array->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(struct_field_count); - - init_const_slice(ira->codegen, fields[1], struct_field_array, 0, struct_field_count, false); - - for (uint32_t struct_field_index = 0; struct_field_index < struct_field_count; struct_field_index++) { - TypeStructField *struct_field = type_entry->data.structure.fields[struct_field_index]; - ZigValue *struct_field_val = &struct_field_array->data.x_array.data.s_none.elements[struct_field_index]; - - struct_field_val->special = ConstValSpecialStatic; - struct_field_val->type = type_info_struct_field_type; - - ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 4); - - inner_fields[1]->special = ConstValSpecialStatic; - inner_fields[1]->type = ira->codegen->builtin_types.entry_type; - inner_fields[1]->data.x_type = struct_field->type_entry; - - // default_value: anytype - inner_fields[2]->special = ConstValSpecialStatic; - inner_fields[2]->type = get_optional_type2(ira->codegen, struct_field->type_entry); - if (inner_fields[2]->type == nullptr) return ErrorSemanticAnalyzeFail; - memoize_field_init_val(ira->codegen, type_entry, struct_field); - if(struct_field->init_val != nullptr && type_is_invalid(struct_field->init_val->type)){ - return ErrorSemanticAnalyzeFail; - } - set_optional_payload(inner_fields[2], struct_field->init_val); - - inner_fields[3]->special = ConstValSpecialStatic; - inner_fields[3]->type = ira->codegen->builtin_types.entry_bool; - inner_fields[3]->data.x_bool = struct_field->is_comptime; - - ZigValue *name = create_const_str_lit(ira->codegen, struct_field->name)->data.x_ptr.data.ref.pointee; - init_const_slice(ira->codegen, inner_fields[0], name, 0, buf_len(struct_field->name), true); - - struct_field_val->data.x_struct.fields = inner_fields; - struct_field_val->parent.id = ConstParentIdArray; - struct_field_val->parent.data.p_array.array_val = struct_field_array; - struct_field_val->parent.data.p_array.elem_index = struct_field_index; - } - // decls: []TypeInfo.Declaration - ensure_field_index(result->type, "decls", 2); - if ((err = ir_make_type_info_decls(ira, source_instr, fields[2], - type_entry->data.structure.decls_scope, false))) - { - return err; - } - - // is_tuple: bool - ensure_field_index(result->type, "is_tuple", 3); - fields[3]->special = ConstValSpecialStatic; - fields[3]->type = ira->codegen->builtin_types.entry_bool; - fields[3]->data.x_bool = is_tuple(type_entry); - - break; - } - case ZigTypeIdFn: - { - result = ira->codegen->pass1_arena->create(); - result->special = ConstValSpecialStatic; - result->type = ir_type_info_get_type(ira, "Fn", nullptr); - - ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 5); - result->data.x_struct.fields = fields; - - // calling_convention: TypeInfo.CallingConvention - ensure_field_index(result->type, "calling_convention", 0); - fields[0]->special = ConstValSpecialStatic; - fields[0]->type = get_builtin_type(ira->codegen, "CallingConvention"); - bigint_init_unsigned(&fields[0]->data.x_enum_tag, type_entry->data.fn.fn_type_id.cc); - // is_generic: bool - ensure_field_index(result->type, "is_generic", 1); - bool is_generic = type_entry->data.fn.is_generic; - fields[1]->special = ConstValSpecialStatic; - fields[1]->type = ira->codegen->builtin_types.entry_bool; - fields[1]->data.x_bool = is_generic; - // is_varargs: bool - ensure_field_index(result->type, "is_var_args", 2); - bool is_varargs = type_entry->data.fn.fn_type_id.is_var_args; - fields[2]->special = ConstValSpecialStatic; - fields[2]->type = ira->codegen->builtin_types.entry_bool; - fields[2]->data.x_bool = type_entry->data.fn.fn_type_id.is_var_args; - // return_type: ?type - ensure_field_index(result->type, "return_type", 3); - fields[3]->special = ConstValSpecialStatic; - fields[3]->type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type); - if (type_entry->data.fn.fn_type_id.return_type == nullptr) - fields[3]->data.x_optional = nullptr; - else { - ZigValue *return_type = ira->codegen->pass1_arena->create(); - return_type->special = ConstValSpecialStatic; - return_type->type = ira->codegen->builtin_types.entry_type; - return_type->data.x_type = type_entry->data.fn.fn_type_id.return_type; - fields[3]->data.x_optional = return_type; - } - // args: []TypeInfo.FnArg - ZigType *type_info_fn_arg_type = ir_type_info_get_type(ira, "FnArg", nullptr); - if ((err = type_resolve(ira->codegen, type_info_fn_arg_type, ResolveStatusSizeKnown))) { - zig_unreachable(); - } - size_t fn_arg_count = type_entry->data.fn.fn_type_id.param_count - - (is_varargs && type_entry->data.fn.fn_type_id.cc != CallingConventionC); - - ZigValue *fn_arg_array = ira->codegen->pass1_arena->create(); - fn_arg_array->special = ConstValSpecialStatic; - fn_arg_array->type = get_array_type(ira->codegen, type_info_fn_arg_type, fn_arg_count, nullptr); - fn_arg_array->data.x_array.special = ConstArraySpecialNone; - fn_arg_array->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(fn_arg_count); - - init_const_slice(ira->codegen, fields[4], fn_arg_array, 0, fn_arg_count, false); - - for (size_t fn_arg_index = 0; fn_arg_index < fn_arg_count; fn_arg_index++) { - FnTypeParamInfo *fn_param_info = &type_entry->data.fn.fn_type_id.param_info[fn_arg_index]; - ZigValue *fn_arg_val = &fn_arg_array->data.x_array.data.s_none.elements[fn_arg_index]; - - fn_arg_val->special = ConstValSpecialStatic; - fn_arg_val->type = type_info_fn_arg_type; - - bool arg_is_generic = fn_param_info->type == nullptr; - if (arg_is_generic) assert(is_generic); - - ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 3); - inner_fields[0]->special = ConstValSpecialStatic; - inner_fields[0]->type = ira->codegen->builtin_types.entry_bool; - inner_fields[0]->data.x_bool = arg_is_generic; - inner_fields[1]->special = ConstValSpecialStatic; - inner_fields[1]->type = ira->codegen->builtin_types.entry_bool; - inner_fields[1]->data.x_bool = fn_param_info->is_noalias; - inner_fields[2]->special = ConstValSpecialStatic; - inner_fields[2]->type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type); - - if (arg_is_generic) - inner_fields[2]->data.x_optional = nullptr; - else { - ZigValue *arg_type = ira->codegen->pass1_arena->create(); - arg_type->special = ConstValSpecialStatic; - arg_type->type = ira->codegen->builtin_types.entry_type; - arg_type->data.x_type = fn_param_info->type; - inner_fields[2]->data.x_optional = arg_type; - } - - fn_arg_val->data.x_struct.fields = inner_fields; - fn_arg_val->parent.id = ConstParentIdArray; - fn_arg_val->parent.data.p_array.array_val = fn_arg_array; - fn_arg_val->parent.data.p_array.elem_index = fn_arg_index; - } - - break; - } - case ZigTypeIdBoundFn: - { - ZigType *fn_type = type_entry->data.bound_fn.fn_type; - assert(fn_type->id == ZigTypeIdFn); - if ((err = ir_make_type_info_value(ira, source_instr, fn_type, &result))) - return err; - - break; - } - case ZigTypeIdFnFrame: - { - result = ira->codegen->pass1_arena->create(); - result->special = ConstValSpecialStatic; - result->type = ir_type_info_get_type(ira, "Frame", nullptr); - ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 1); - result->data.x_struct.fields = fields; - ZigFn *fn = type_entry->data.frame.fn; - // function: anytype - ensure_field_index(result->type, "function", 0); - fields[0] = create_const_fn(ira->codegen, fn); - break; - } - } - - assert(result != nullptr); - ira->codegen->type_info_cache.put(type_entry, result); - *out = result; - return ErrorNone; -} - -static IrInstGen *ir_analyze_instruction_type_info(IrAnalyze *ira, IrInstSrcTypeInfo *instruction) { - Error err; - IrInstGen *type_value = instruction->type_value->child; - ZigType *type_entry = ir_resolve_type(ira, type_value); - if (type_is_invalid(type_entry)) - return ira->codegen->invalid_inst_gen; - - ZigType *result_type = ir_type_info_get_type(ira, nullptr, nullptr); - - ZigValue *payload; - if ((err = ir_make_type_info_value(ira, &instruction->base.base, type_entry, &payload))) - return ira->codegen->invalid_inst_gen; - - IrInstGen *result = ir_const(ira, &instruction->base.base, result_type); - ZigValue *out_val = result->value; - bigint_init_unsigned(&out_val->data.x_union.tag, type_id_index(type_entry)); - out_val->data.x_union.payload = payload; - - if (payload != nullptr) { - payload->parent.id = ConstParentIdUnion; - payload->parent.data.p_union.union_val = out_val; - } - - return result; -} - -static ZigValue *get_const_field(IrAnalyze *ira, AstNode *source_node, ZigValue *struct_value, - const char *name, size_t field_index) -{ - Error err; - ensure_field_index(struct_value->type, name, field_index); - ZigValue *val = struct_value->data.x_struct.fields[field_index]; - if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, source_node, val, UndefBad))) - return nullptr; - return val; -} - -static Error get_const_field_sentinel(IrAnalyze *ira, IrInst* source_instr, ZigValue *struct_value, - const char *name, size_t field_index, ZigType *elem_type, ZigValue **result) -{ - ZigValue *field_val = get_const_field(ira, source_instr->source_node, struct_value, name, field_index); - if (field_val == nullptr) - return ErrorSemanticAnalyzeFail; - - IrInstGen *field_inst = ir_const_move(ira, source_instr, field_val); - IrInstGen *casted_field_inst = ir_implicit_cast(ira, field_inst, - get_optional_type(ira->codegen, elem_type)); - if (type_is_invalid(casted_field_inst->value->type)) - return ErrorSemanticAnalyzeFail; - - if (optional_value_is_null(casted_field_inst->value)) { - *result = nullptr; - } else { - assert(type_has_optional_repr(casted_field_inst->value->type)); - *result = casted_field_inst->value->data.x_optional; - } - - return ErrorNone; -} - -static Error get_const_field_bool(IrAnalyze *ira, AstNode *source_node, ZigValue *struct_value, - const char *name, size_t field_index, bool *out) -{ - ZigValue *value = get_const_field(ira, source_node, struct_value, name, field_index); - if (value == nullptr) - return ErrorSemanticAnalyzeFail; - assert(value->type == ira->codegen->builtin_types.entry_bool); - *out = value->data.x_bool; - return ErrorNone; -} - -static BigInt *get_const_field_lit_int(IrAnalyze *ira, AstNode *source_node, ZigValue *struct_value, const char *name, size_t field_index) -{ - ZigValue *value = get_const_field(ira, source_node, struct_value, name, field_index); - if (value == nullptr) - return nullptr; - assert(value->type == ira->codegen->builtin_types.entry_num_lit_int); - return &value->data.x_bigint; -} - -static ZigType *get_const_field_meta_type(IrAnalyze *ira, AstNode *source_node, ZigValue *struct_value, const char *name, size_t field_index) -{ - ZigValue *value = get_const_field(ira, source_node, struct_value, name, field_index); - if (value == nullptr) - return ira->codegen->invalid_inst_gen->value->type; - assert(value->type == ira->codegen->builtin_types.entry_type); - return value->data.x_type; -} - -static ZigType *get_const_field_meta_type_optional(IrAnalyze *ira, AstNode *source_node, - ZigValue *struct_value, const char *name, size_t field_index) -{ - ZigValue *value = get_const_field(ira, source_node, struct_value, name, field_index); - if (value == nullptr) - return ira->codegen->invalid_inst_gen->value->type; - assert(value->type->id == ZigTypeIdOptional); - assert(value->type->data.maybe.child_type == ira->codegen->builtin_types.entry_type); - if (value->data.x_optional == nullptr) - return nullptr; - return value->data.x_optional->data.x_type; -} - -static Error get_const_field_buf(IrAnalyze *ira, AstNode *source_node, ZigValue *struct_value, - const char *name, size_t field_index, Buf *out) -{ - ZigValue *slice = get_const_field(ira, source_node, struct_value, name, field_index); - ZigValue *ptr = slice->data.x_struct.fields[slice_ptr_index]; - ZigValue *len = slice->data.x_struct.fields[slice_len_index]; - assert(ptr->data.x_ptr.special == ConstPtrSpecialBaseArray); - assert(ptr->data.x_ptr.data.base_array.elem_index == 0); - ZigValue *arr = ptr->data.x_ptr.data.base_array.array_val; - assert(arr->special == ConstValSpecialStatic); - switch (arr->data.x_array.special) { - case ConstArraySpecialUndef: - return ErrorSemanticAnalyzeFail; - case ConstArraySpecialNone: { - buf_resize(out, 0); - size_t count = bigint_as_usize(&len->data.x_bigint); - for (size_t j = 0; j < count; j++) { - ZigValue *ch_val = &arr->data.x_array.data.s_none.elements[j]; - unsigned ch = bigint_as_u32(&ch_val->data.x_bigint); - buf_append_char(out, ch); - } - break; - } - case ConstArraySpecialBuf: - buf_init_from_buf(out, arr->data.x_array.data.s_buf); - break; - } - return ErrorNone; -} - -static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeId tagTypeId, ZigValue *payload) { - Error err; - switch (tagTypeId) { - case ZigTypeIdInvalid: - zig_unreachable(); - case ZigTypeIdMetaType: - return ira->codegen->builtin_types.entry_type; - case ZigTypeIdVoid: - return ira->codegen->builtin_types.entry_void; - case ZigTypeIdBool: - return ira->codegen->builtin_types.entry_bool; - case ZigTypeIdUnreachable: - return ira->codegen->builtin_types.entry_unreachable; - case ZigTypeIdInt: { - assert(payload->special == ConstValSpecialStatic); - assert(payload->type == ir_type_info_get_type(ira, "Int", nullptr)); - BigInt *bi = get_const_field_lit_int(ira, source_instr->source_node, payload, "bits", 1); - if (bi == nullptr) - return ira->codegen->invalid_inst_gen->value->type; - bool is_signed; - if ((err = get_const_field_bool(ira, source_instr->source_node, payload, "is_signed", 0, &is_signed))) - return ira->codegen->invalid_inst_gen->value->type; - return get_int_type(ira->codegen, is_signed, bigint_as_u32(bi)); - } - case ZigTypeIdFloat: - { - assert(payload->special == ConstValSpecialStatic); - assert(payload->type == ir_type_info_get_type(ira, "Float", nullptr)); - BigInt *bi = get_const_field_lit_int(ira, source_instr->source_node, payload, "bits", 0); - if (bi == nullptr) - return ira->codegen->invalid_inst_gen->value->type; - uint32_t bits = bigint_as_u32(bi); - switch (bits) { - case 16: return ira->codegen->builtin_types.entry_f16; - case 32: return ira->codegen->builtin_types.entry_f32; - case 64: return ira->codegen->builtin_types.entry_f64; - case 128: return ira->codegen->builtin_types.entry_f128; - } - ir_add_error(ira, source_instr, buf_sprintf("%d-bit float unsupported", bits)); - return ira->codegen->invalid_inst_gen->value->type; - } - case ZigTypeIdPointer: - { - ZigType *type_info_pointer_type = ir_type_info_get_type(ira, "Pointer", nullptr); - assert(payload->special == ConstValSpecialStatic); - assert(payload->type == type_info_pointer_type); - ZigValue *size_value = get_const_field(ira, source_instr->source_node, payload, "size", 0); - if (size_value == nullptr) - return ira->codegen->invalid_inst_gen->value->type; - - assert(size_value->type == ir_type_info_get_type(ira, "Size", type_info_pointer_type)); - BuiltinPtrSize size_enum_index = (BuiltinPtrSize)bigint_as_u32(&size_value->data.x_enum_tag); - PtrLen ptr_len = size_enum_index_to_ptr_len(size_enum_index); - ZigType *elem_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "child", 4); - if (type_is_invalid(elem_type)) - return ira->codegen->invalid_inst_gen->value->type; - ZigValue *sentinel; - if ((err = get_const_field_sentinel(ira, source_instr, payload, "sentinel", 6, - elem_type, &sentinel))) - { - return ira->codegen->invalid_inst_gen->value->type; - } - if (sentinel != nullptr && (size_enum_index == BuiltinPtrSizeOne || size_enum_index == BuiltinPtrSizeC)) { - ir_add_error(ira, source_instr, - buf_sprintf("sentinels are only allowed on slices and unknown-length pointers")); - return ira->codegen->invalid_inst_gen->value->type; - } - BigInt *bi = get_const_field_lit_int(ira, source_instr->source_node, payload, "alignment", 3); - if (bi == nullptr) - return ira->codegen->invalid_inst_gen->value->type; - - bool is_const; - if ((err = get_const_field_bool(ira, source_instr->source_node, payload, "is_const", 1, &is_const))) - return ira->codegen->invalid_inst_gen->value->type; - - bool is_volatile; - if ((err = get_const_field_bool(ira, source_instr->source_node, payload, "is_volatile", 2, - &is_volatile))) - { - return ira->codegen->invalid_inst_gen->value->type; - } - - bool is_allowzero; - if ((err = get_const_field_bool(ira, source_instr->source_node, payload, "is_allowzero", 5, - &is_allowzero))) - { - return ira->codegen->invalid_inst_gen->value->type; - } - - - ZigType *ptr_type = get_pointer_to_type_extra2(ira->codegen, - elem_type, - is_const, - is_volatile, - ptr_len, - bigint_as_u32(bi), - 0, // bit_offset_in_host - 0, // host_int_bytes - is_allowzero, - VECTOR_INDEX_NONE, nullptr, sentinel); - if (size_enum_index != BuiltinPtrSizeSlice) - return ptr_type; - return get_slice_type(ira->codegen, ptr_type); - } - case ZigTypeIdArray: { - assert(payload->special == ConstValSpecialStatic); - assert(payload->type == ir_type_info_get_type(ira, "Array", nullptr)); - ZigType *elem_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "child", 1); - if (type_is_invalid(elem_type)) - return ira->codegen->invalid_inst_gen->value->type; - ZigValue *sentinel; - if ((err = get_const_field_sentinel(ira, source_instr, payload, "sentinel", 2, - elem_type, &sentinel))) - { - return ira->codegen->invalid_inst_gen->value->type; - } - BigInt *bi = get_const_field_lit_int(ira, source_instr->source_node, payload, "len", 0); - if (bi == nullptr) - return ira->codegen->invalid_inst_gen->value->type; - return get_array_type(ira->codegen, elem_type, bigint_as_u64(bi), sentinel); - } - case ZigTypeIdComptimeFloat: - return ira->codegen->builtin_types.entry_num_lit_float; - case ZigTypeIdComptimeInt: - return ira->codegen->builtin_types.entry_num_lit_int; - case ZigTypeIdUndefined: - return ira->codegen->builtin_types.entry_undef; - case ZigTypeIdNull: - return ira->codegen->builtin_types.entry_null; - case ZigTypeIdOptional: { - assert(payload->special == ConstValSpecialStatic); - assert(payload->type == ir_type_info_get_type(ira, "Optional", nullptr)); - ZigType *child_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "child", 0); - if (type_is_invalid(child_type)) - return ira->codegen->invalid_inst_gen->value->type; - return get_optional_type(ira->codegen, child_type); - } - case ZigTypeIdErrorUnion: { - assert(payload->special == ConstValSpecialStatic); - assert(payload->type == ir_type_info_get_type(ira, "ErrorUnion", nullptr)); - ZigType *err_set_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "error_set", 0); - if (type_is_invalid(err_set_type)) - return ira->codegen->invalid_inst_gen->value->type; - - ZigType *payload_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "payload", 1); - if (type_is_invalid(payload_type)) - return ira->codegen->invalid_inst_gen->value->type; - - return get_error_union_type(ira->codegen, err_set_type, payload_type); - } - case ZigTypeIdOpaque: { - Buf *bare_name = buf_alloc(); - Buf *full_name = get_anon_type_name(ira->codegen, - ira->old_irb.exec, "opaque", source_instr->scope, source_instr->source_node, bare_name); - return get_opaque_type(ira->codegen, - source_instr->scope, source_instr->source_node, buf_ptr(full_name), bare_name); - } - case ZigTypeIdVector: { - assert(payload->special == ConstValSpecialStatic); - assert(payload->type == ir_type_info_get_type(ira, "Vector", nullptr)); - BigInt *len = get_const_field_lit_int(ira, source_instr->source_node, payload, "len", 0); - if (len == nullptr) - return ira->codegen->invalid_inst_gen->value->type; - - ZigType *child_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "child", 1); - if ((err = ir_validate_vector_elem_type(ira, source_instr->source_node, child_type))) { - return ira->codegen->invalid_inst_gen->value->type; - } - return get_vector_type(ira->codegen, bigint_as_u32(len), child_type); - } - case ZigTypeIdAnyFrame: { - assert(payload->special == ConstValSpecialStatic); - assert(payload->type == ir_type_info_get_type(ira, "AnyFrame", nullptr)); - ZigType *child_type = get_const_field_meta_type_optional(ira, source_instr->source_node, payload, "child", 0); - if (child_type != nullptr && type_is_invalid(child_type)) - return ira->codegen->invalid_inst_gen->value->type; - - return get_any_frame_type(ira->codegen, child_type); - } - case ZigTypeIdEnumLiteral: - return ira->codegen->builtin_types.entry_enum_literal; - case ZigTypeIdFnFrame: { - assert(payload->special == ConstValSpecialStatic); - assert(payload->type == ir_type_info_get_type(ira, "Frame", nullptr)); - ZigValue *function = get_const_field(ira, source_instr->source_node, payload, "function", 0); - if (function == nullptr) - return ira->codegen->invalid_inst_gen->value->type; - - assert(function->type->id == ZigTypeIdFn); - ZigFn *fn = function->data.x_ptr.data.fn.fn_entry; - return get_fn_frame_type(ira->codegen, fn); - } - case ZigTypeIdErrorSet: { - assert(payload->special == ConstValSpecialStatic); - assert(payload->type->id == ZigTypeIdOptional); - ZigValue *slice = payload->data.x_optional; - if (slice == nullptr) - return ira->codegen->builtin_types.entry_global_error_set; - assert(slice->special == ConstValSpecialStatic); - assert(is_slice(slice->type)); - ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet); - Buf bare_name = BUF_INIT; - buf_init_from_buf(&err_set_type->name, get_anon_type_name(ira->codegen, ira->old_irb.exec, "error", source_instr->scope, source_instr->source_node, &bare_name)); - err_set_type->size_in_bits = ira->codegen->builtin_types.entry_global_error_set->size_in_bits; - err_set_type->abi_align = ira->codegen->builtin_types.entry_global_error_set->abi_align; - err_set_type->abi_size = ira->codegen->builtin_types.entry_global_error_set->abi_size; - ZigValue *ptr = slice->data.x_struct.fields[slice_ptr_index]; - assert(ptr->data.x_ptr.special == ConstPtrSpecialBaseArray);; - assert(ptr->data.x_ptr.data.base_array.elem_index == 0); - ZigValue *arr = ptr->data.x_ptr.data.base_array.array_val; - assert(arr->special == ConstValSpecialStatic); - assert(arr->data.x_array.special == ConstArraySpecialNone); - ZigValue *len = slice->data.x_struct.fields[slice_len_index]; - size_t count = bigint_as_usize(&len->data.x_bigint); - err_set_type->data.error_set.err_count = count; - err_set_type->data.error_set.errors = heap::c_allocator.allocate(count); - bool *already_set = heap::c_allocator.allocate(ira->codegen->errors_by_index.length + count); - for (size_t i = 0; i < count; i++) { - ZigValue *error = &arr->data.x_array.data.s_none.elements[i]; - assert(error->type == ir_type_info_get_type(ira, "Error", nullptr)); - ErrorTableEntry *err_entry = heap::c_allocator.create(); - err_entry->decl_node = source_instr->source_node; - if ((err = get_const_field_buf(ira, source_instr->source_node, error, "name", 0, &err_entry->name))) - return ira->codegen->invalid_inst_gen->value->type; - auto existing_entry = ira->codegen->error_table.put_unique(&err_entry->name, err_entry); - if (existing_entry) { - err_entry->value = existing_entry->value->value; - } else { - size_t error_value_count = ira->codegen->errors_by_index.length; - assert((uint32_t)error_value_count < (((uint32_t)1) << (uint32_t)ira->codegen->err_tag_type->data.integral.bit_count)); - err_entry->value = error_value_count; - ira->codegen->errors_by_index.append(err_entry); - } - if (already_set[err_entry->value]) { - ir_add_error(ira, source_instr, buf_sprintf("duplicate error: %s", buf_ptr(&err_entry->name))); - return ira->codegen->invalid_inst_gen->value->type; - } else { - already_set[err_entry->value] = true; - } - err_set_type->data.error_set.errors[i] = err_entry; - } - return err_set_type; - } - case ZigTypeIdStruct: { - assert(payload->special == ConstValSpecialStatic); - assert(payload->type == ir_type_info_get_type(ira, "Struct", nullptr)); - - ZigValue *layout_value = get_const_field(ira, source_instr->source_node, payload, "layout", 0); - if (layout_value == nullptr) - return ira->codegen->invalid_inst_gen->value->type; - assert(layout_value->special == ConstValSpecialStatic); - assert(layout_value->type == ir_type_info_get_type(ira, "ContainerLayout", nullptr)); - ContainerLayout layout = (ContainerLayout)bigint_as_u32(&layout_value->data.x_enum_tag); - - ZigValue *fields_value = get_const_field(ira, source_instr->source_node, payload, "fields", 1); - if (fields_value == nullptr) - return ira->codegen->invalid_inst_gen->value->type; - assert(fields_value->special == ConstValSpecialStatic); - assert(is_slice(fields_value->type)); - ZigValue *fields_ptr = fields_value->data.x_struct.fields[slice_ptr_index]; - ZigValue *fields_len_value = fields_value->data.x_struct.fields[slice_len_index]; - size_t fields_len = bigint_as_usize(&fields_len_value->data.x_bigint); - - ZigValue *decls_value = get_const_field(ira, source_instr->source_node, payload, "decls", 2); - if (decls_value == nullptr) - return ira->codegen->invalid_inst_gen->value->type; - assert(decls_value->special == ConstValSpecialStatic); - assert(is_slice(decls_value->type)); - ZigValue *decls_len_value = decls_value->data.x_struct.fields[slice_len_index]; - size_t decls_len = bigint_as_usize(&decls_len_value->data.x_bigint); - if (decls_len != 0) { - ir_add_error(ira, source_instr, buf_create_from_str("TypeInfo.Struct.decls must be empty for @Type")); - return ira->codegen->invalid_inst_gen->value->type; - } - - bool is_tuple; - if ((err = get_const_field_bool(ira, source_instr->source_node, payload, "is_tuple", 3, &is_tuple))) - return ira->codegen->invalid_inst_gen->value->type; - - ZigType *entry = new_type_table_entry(ZigTypeIdStruct); - buf_init_from_buf(&entry->name, - get_anon_type_name(ira->codegen, ira->old_irb.exec, "struct", source_instr->scope, source_instr->source_node, &entry->name)); - entry->data.structure.decl_node = source_instr->source_node; - entry->data.structure.fields = alloc_type_struct_fields(fields_len); - entry->data.structure.fields_by_name.init(fields_len); - entry->data.structure.src_field_count = fields_len; - entry->data.structure.layout = layout; - entry->data.structure.special = is_tuple ? StructSpecialInferredTuple : StructSpecialNone; - entry->data.structure.created_by_at_type = true; - entry->data.structure.decls_scope = create_decls_scope( - ira->codegen, source_instr->source_node, source_instr->scope, entry, get_scope_import(source_instr->scope), &entry->name); - - assert(fields_ptr->data.x_ptr.special == ConstPtrSpecialBaseArray); - assert(fields_ptr->data.x_ptr.data.base_array.elem_index == 0); - ZigValue *fields_arr = fields_ptr->data.x_ptr.data.base_array.array_val; - assert(fields_arr->special == ConstValSpecialStatic); - assert(fields_arr->data.x_array.special == ConstArraySpecialNone); - for (size_t i = 0; i < fields_len; i++) { - ZigValue *field_value = &fields_arr->data.x_array.data.s_none.elements[i]; - assert(field_value->type == ir_type_info_get_type(ira, "StructField", nullptr)); - TypeStructField *field = entry->data.structure.fields[i]; - field->name = buf_alloc(); - if ((err = get_const_field_buf(ira, source_instr->source_node, field_value, "name", 0, field->name))) - return ira->codegen->invalid_inst_gen->value->type; - field->decl_node = source_instr->source_node; - ZigValue *type_value = get_const_field(ira, source_instr->source_node, field_value, "field_type", 1); - if (type_value == nullptr) - return ira->codegen->invalid_inst_gen->value->type; - field->type_val = type_value; - field->type_entry = type_value->data.x_type; - if (entry->data.structure.fields_by_name.put_unique(field->name, field) != nullptr) { - ir_add_error(ira, source_instr, buf_sprintf("duplicate struct field '%s'", buf_ptr(field->name))); - return ira->codegen->invalid_inst_gen->value->type; - } - ZigValue *default_value = get_const_field(ira, source_instr->source_node, field_value, "default_value", 2); - if (default_value == nullptr) - return ira->codegen->invalid_inst_gen->value->type; - if (default_value->type->id == ZigTypeIdNull) { - field->init_val = nullptr; - } else if (default_value->type->id == ZigTypeIdOptional && default_value->type->data.maybe.child_type == field->type_entry) { - field->init_val = default_value->data.x_optional; - } else if (default_value->type == field->type_entry) { - field->init_val = default_value; - } else { - ir_add_error(ira, source_instr, - buf_sprintf("default_value of field '%s' is of type '%s', expected '%s' or '?%s'", - buf_ptr(field->name), buf_ptr(&default_value->type->name), - buf_ptr(&field->type_entry->name), buf_ptr(&field->type_entry->name))); - return ira->codegen->invalid_inst_gen->value->type; - } - if ((err = get_const_field_bool(ira, source_instr->source_node, field_value, "is_comptime", 3, &field->is_comptime))) - return ira->codegen->invalid_inst_gen->value->type; - } - - return entry; - } - case ZigTypeIdEnum: { - assert(payload->special == ConstValSpecialStatic); - assert(payload->type == ir_type_info_get_type(ira, "Enum", nullptr)); - - ZigValue *layout_value = get_const_field(ira, source_instr->source_node, payload, "layout", 0); - if (layout_value == nullptr) - return ira->codegen->invalid_inst_gen->value->type; - - assert(layout_value->special == ConstValSpecialStatic); - assert(layout_value->type == ir_type_info_get_type(ira, "ContainerLayout", nullptr)); - ContainerLayout layout = (ContainerLayout)bigint_as_u32(&layout_value->data.x_enum_tag); - - ZigType *tag_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "tag_type", 1); - - ZigValue *fields_value = get_const_field(ira, source_instr->source_node, payload, "fields", 2); - if (fields_value == nullptr) - return ira->codegen->invalid_inst_gen->value->type; - - assert(fields_value->special == ConstValSpecialStatic); - assert(is_slice(fields_value->type)); - ZigValue *fields_ptr = fields_value->data.x_struct.fields[slice_ptr_index]; - ZigValue *fields_len_value = fields_value->data.x_struct.fields[slice_len_index]; - size_t fields_len = bigint_as_usize(&fields_len_value->data.x_bigint); - - ZigValue *decls_value = get_const_field(ira, source_instr->source_node, payload, "decls", 3); - if (decls_value == nullptr) - return ira->codegen->invalid_inst_gen->value->type; - - assert(decls_value->special == ConstValSpecialStatic); - assert(is_slice(decls_value->type)); - ZigValue *decls_len_value = decls_value->data.x_struct.fields[slice_len_index]; - size_t decls_len = bigint_as_usize(&decls_len_value->data.x_bigint); - if (decls_len != 0) { - ir_add_error(ira, source_instr, buf_create_from_str("TypeInfo.Enum.decls must be empty for @Type")); - return ira->codegen->invalid_inst_gen->value->type; - } - - Error err; - bool is_exhaustive; - if ((err = get_const_field_bool(ira, source_instr->source_node, payload, "is_exhaustive", 4, &is_exhaustive))) - return ira->codegen->invalid_inst_gen->value->type; - - ZigType *entry = new_type_table_entry(ZigTypeIdEnum); - buf_init_from_buf(&entry->name, - get_anon_type_name(ira->codegen, ira->old_irb.exec, "enum", source_instr->scope, source_instr->source_node, &entry->name)); - entry->data.enumeration.decl_node = source_instr->source_node; - entry->data.enumeration.tag_int_type = tag_type; - entry->data.enumeration.decls_scope = create_decls_scope( - ira->codegen, source_instr->source_node, source_instr->scope, entry, get_scope_import(source_instr->scope), &entry->name); - entry->data.enumeration.fields = heap::c_allocator.allocate(fields_len); - entry->data.enumeration.fields_by_name.init(fields_len); - entry->data.enumeration.src_field_count = fields_len; - entry->data.enumeration.layout = layout; - entry->data.enumeration.non_exhaustive = !is_exhaustive; - - assert(fields_ptr->data.x_ptr.special == ConstPtrSpecialBaseArray); - assert(fields_ptr->data.x_ptr.data.base_array.elem_index == 0); - ZigValue *fields_arr = fields_ptr->data.x_ptr.data.base_array.array_val; - assert(fields_arr->special == ConstValSpecialStatic); - assert(fields_arr->data.x_array.special == ConstArraySpecialNone); - for (size_t i = 0; i < fields_len; i++) { - ZigValue *field_value = &fields_arr->data.x_array.data.s_none.elements[i]; - assert(field_value->type == ir_type_info_get_type(ira, "EnumField", nullptr)); - TypeEnumField *field = &entry->data.enumeration.fields[i]; - field->name = buf_alloc(); - if ((err = get_const_field_buf(ira, source_instr->source_node, field_value, "name", 0, field->name))) - return ira->codegen->invalid_inst_gen->value->type; - field->decl_index = i; - field->decl_node = source_instr->source_node; - if (entry->data.enumeration.fields_by_name.put_unique(field->name, field) != nullptr) { - ir_add_error(ira, source_instr, buf_sprintf("duplicate enum field '%s'", buf_ptr(field->name))); - return ira->codegen->invalid_inst_gen->value->type; - } - BigInt *field_int_value = get_const_field_lit_int(ira, source_instr->source_node, field_value, "value", 1); - if (field_int_value == nullptr) - return ira->codegen->invalid_inst_gen->value->type; - field->value = *field_int_value; - } - return entry; - } - case ZigTypeIdUnion: { - assert(payload->special == ConstValSpecialStatic); - assert(payload->type == ir_type_info_get_type(ira, "Union", nullptr)); - - ZigValue *layout_value = get_const_field(ira, source_instr->source_node, payload, "layout", 0); - if (layout_value == nullptr) - return ira->codegen->invalid_inst_gen->value->type; - assert(layout_value->special == ConstValSpecialStatic); - assert(layout_value->type == ir_type_info_get_type(ira, "ContainerLayout", nullptr)); - ContainerLayout layout = (ContainerLayout)bigint_as_u32(&layout_value->data.x_enum_tag); - - ZigType *tag_type = get_const_field_meta_type_optional(ira, source_instr->source_node, payload, "tag_type", 1); - if (tag_type != nullptr && type_is_invalid(tag_type)) { - return ira->codegen->invalid_inst_gen->value->type; - } - if (tag_type != nullptr && tag_type->id != ZigTypeIdEnum) { - ir_add_error(ira, source_instr, buf_sprintf( - "expected enum type, found '%s'", type_id_name(tag_type->id))); - return ira->codegen->invalid_inst_gen->value->type; - } - - ZigValue *fields_value = get_const_field(ira, source_instr->source_node, payload, "fields", 2); - if (fields_value == nullptr) - return ira->codegen->invalid_inst_gen->value->type; - - assert(fields_value->special == ConstValSpecialStatic); - assert(is_slice(fields_value->type)); - ZigValue *fields_ptr = fields_value->data.x_struct.fields[slice_ptr_index]; - ZigValue *fields_len_value = fields_value->data.x_struct.fields[slice_len_index]; - size_t fields_len = bigint_as_usize(&fields_len_value->data.x_bigint); - - ZigValue *decls_value = get_const_field(ira, source_instr->source_node, payload, "decls", 3); - if (decls_value == nullptr) - return ira->codegen->invalid_inst_gen->value->type; - - assert(decls_value->special == ConstValSpecialStatic); - assert(is_slice(decls_value->type)); - ZigValue *decls_len_value = decls_value->data.x_struct.fields[slice_len_index]; - size_t decls_len = bigint_as_usize(&decls_len_value->data.x_bigint); - if (decls_len != 0) { - ir_add_error(ira, source_instr, buf_create_from_str("TypeInfo.Union.decls must be empty for @Type")); - return ira->codegen->invalid_inst_gen->value->type; - } - - ZigType *entry = new_type_table_entry(ZigTypeIdUnion); - buf_init_from_buf(&entry->name, - get_anon_type_name(ira->codegen, ira->old_irb.exec, "union", source_instr->scope, source_instr->source_node, &entry->name)); - entry->data.unionation.decl_node = source_instr->source_node; - entry->data.unionation.fields = heap::c_allocator.allocate(fields_len); - entry->data.unionation.fields_by_name.init(fields_len); - entry->data.unionation.decls_scope = create_decls_scope( - ira->codegen, source_instr->source_node, source_instr->scope, entry, get_scope_import(source_instr->scope), &entry->name); - entry->data.unionation.tag_type = tag_type; - entry->data.unionation.src_field_count = fields_len; - entry->data.unionation.layout = layout; - - assert(fields_ptr->data.x_ptr.special == ConstPtrSpecialBaseArray); - assert(fields_ptr->data.x_ptr.data.base_array.elem_index == 0); - ZigValue *fields_arr = fields_ptr->data.x_ptr.data.base_array.array_val; - assert(fields_arr->special == ConstValSpecialStatic); - assert(fields_arr->data.x_array.special == ConstArraySpecialNone); - for (size_t i = 0; i < fields_len; i++) { - ZigValue *field_value = &fields_arr->data.x_array.data.s_none.elements[i]; - assert(field_value->type == ir_type_info_get_type(ira, "UnionField", nullptr)); - TypeUnionField *field = &entry->data.unionation.fields[i]; - field->name = buf_alloc(); - if ((err = get_const_field_buf(ira, source_instr->source_node, field_value, "name", 0, field->name))) - return ira->codegen->invalid_inst_gen->value->type; - if (entry->data.unionation.fields_by_name.put_unique(field->name, field) != nullptr) { - ir_add_error(ira, source_instr, buf_sprintf("duplicate union field '%s'", buf_ptr(field->name))); - return ira->codegen->invalid_inst_gen->value->type; - } - field->decl_node = source_instr->source_node; - ZigValue *type_value = get_const_field(ira, source_instr->source_node, field_value, "field_type", 1); - if (type_value == nullptr) - return ira->codegen->invalid_inst_gen->value->type; - field->type_val = type_value; - field->type_entry = type_value->data.x_type; - } - return entry; - } - case ZigTypeIdFn: - case ZigTypeIdBoundFn: - ir_add_error(ira, source_instr, buf_sprintf( - "@Type not available for 'TypeInfo.%s'", type_id_name(tagTypeId))); - return ira->codegen->invalid_inst_gen->value->type; - } - zig_unreachable(); -} - -static IrInstGen *ir_analyze_instruction_type(IrAnalyze *ira, IrInstSrcType *instruction) { - IrInstGen *uncasted_type_info = instruction->type_info->child; - if (type_is_invalid(uncasted_type_info->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *type_info = ir_implicit_cast(ira, uncasted_type_info, ir_type_info_get_type(ira, nullptr, nullptr)); - if (type_is_invalid(type_info->value->type)) - return ira->codegen->invalid_inst_gen; - - ZigValue *type_info_val = ir_resolve_const(ira, type_info, UndefBad); - if (type_info_val == nullptr) - return ira->codegen->invalid_inst_gen; - ZigTypeId type_id_tag = type_id_at_index(bigint_as_usize(&type_info_val->data.x_union.tag)); - ZigType *type = type_info_to_type(ira, &uncasted_type_info->base, type_id_tag, - type_info_val->data.x_union.payload); - if (type_is_invalid(type)) - return ira->codegen->invalid_inst_gen; - return ir_const_type(ira, &instruction->base.base, type); -} - -static IrInstGen *ir_analyze_instruction_set_eval_branch_quota(IrAnalyze *ira, - IrInstSrcSetEvalBranchQuota *instruction) -{ - uint64_t new_quota; - if (!ir_resolve_usize(ira, instruction->new_quota->child, &new_quota)) - return ira->codegen->invalid_inst_gen; - - if (new_quota > *ira->new_irb.exec->backward_branch_quota) { - *ira->new_irb.exec->backward_branch_quota = new_quota; - } - - return ir_const_void(ira, &instruction->base.base); -} - -static IrInstGen *ir_analyze_instruction_type_name(IrAnalyze *ira, IrInstSrcTypeName *instruction) { - IrInstGen *type_value = instruction->type_value->child; - ZigType *type_entry = ir_resolve_type(ira, type_value); - if (type_is_invalid(type_entry)) - return ira->codegen->invalid_inst_gen; - - if (!type_entry->cached_const_name_val) { - type_entry->cached_const_name_val = create_const_str_lit(ira->codegen, type_bare_name(type_entry)); - } - IrInstGen *result = ir_const(ira, &instruction->base.base, nullptr); - copy_const_val(ira->codegen, result->value, type_entry->cached_const_name_val); - return result; -} - -static void ir_cimport_cache_paths(Buf *cache_dir, Buf *tmp_c_file_digest, Buf *out_zig_dir, Buf *out_zig_path) { - buf_resize(out_zig_dir, 0); - buf_resize(out_zig_path, 0); - buf_appendf(out_zig_dir, "%s" OS_SEP "o" OS_SEP "%s", - buf_ptr(cache_dir), buf_ptr(tmp_c_file_digest)); - buf_appendf(out_zig_path, "%s" OS_SEP "cimport.zig", buf_ptr(out_zig_dir)); -} -static IrInstGen *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstSrcCImport *instruction) { - Error err; - AstNode *node = instruction->base.base.source_node; - assert(node->type == NodeTypeFnCallExpr); - AstNode *block_node = node->data.fn_call_expr.params.at(0); - - ScopeCImport *cimport_scope = create_cimport_scope(ira->codegen, node, instruction->base.base.scope); - - // Execute the C import block like an inline function - ZigType *void_type = ira->codegen->builtin_types.entry_void; - ZigValue *cimport_result; - ZigValue *result_ptr; - create_result_ptr(ira->codegen, void_type, &cimport_result, &result_ptr); - if ((err = ir_eval_const_value(ira->codegen, &cimport_scope->base, block_node, result_ptr, - ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, nullptr, - &cimport_scope->buf, block_node, nullptr, nullptr, nullptr, UndefBad))) - { - return ira->codegen->invalid_inst_gen; - } - if (type_is_invalid(cimport_result->type)) - return ira->codegen->invalid_inst_gen; - - ZigPackage *cur_scope_pkg = scope_package(instruction->base.base.scope); - Buf *namespace_name = buf_sprintf("%s.cimport:%" ZIG_PRI_usize ":%" ZIG_PRI_usize, - buf_ptr(&cur_scope_pkg->pkg_path), node->line + 1, node->column + 1); - - ZigPackage *cimport_pkg = new_anonymous_package(); - cimport_pkg->package_table.put(buf_create_from_str("builtin"), ira->codegen->compile_var_package); - cimport_pkg->package_table.put(buf_create_from_str("std"), ira->codegen->std_package); - buf_init_from_buf(&cimport_pkg->pkg_path, namespace_name); - - CacheHash *cache_hash; - if ((err = create_c_object_cache(ira->codegen, &cache_hash, false))) { - ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to create cache: %s", err_str(err))); - return ira->codegen->invalid_inst_gen; - } - cache_buf(cache_hash, &cimport_scope->buf); - - // Set this because we're not adding any files before checking for a hit. - cache_hash->force_check_manifest = true; - - Buf tmp_c_file_digest = BUF_INIT; - buf_resize(&tmp_c_file_digest, 0); - if ((err = cache_hit(cache_hash, &tmp_c_file_digest))) { - if (err != ErrorInvalidFormat) { - ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to check cache: %s", err_str(err))); - return ira->codegen->invalid_inst_gen; - } - } - ira->codegen->caches_to_release.append(cache_hash); - - Buf *out_zig_dir = buf_alloc(); - Buf *out_zig_path = buf_alloc(); - if (buf_len(&tmp_c_file_digest) == 0 || cache_hash->files.length == 0) { - // Cache Miss - Buf *tmp_c_file_dir = buf_sprintf("%s" OS_SEP "o" OS_SEP "%s", - buf_ptr(ira->codegen->cache_dir), buf_ptr(&cache_hash->b64_digest)); - Buf *resolve_paths[] = { - tmp_c_file_dir, - buf_create_from_str("cimport.h"), - }; - Buf tmp_c_file_path = os_path_resolve(resolve_paths, 2); - - if ((err = os_make_path(tmp_c_file_dir))) { - ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to make dir: %s", err_str(err))); - return ira->codegen->invalid_inst_gen; - } - - if ((err = os_write_file(&tmp_c_file_path, &cimport_scope->buf))) { - ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to write .h file: %s", err_str(err))); - return ira->codegen->invalid_inst_gen; - } - if (ira->codegen->verbose_cimport) { - fprintf(stderr, "@cImport source: %s\n", buf_ptr(&tmp_c_file_path)); - } - - Buf *tmp_dep_file = buf_sprintf("%s.d", buf_ptr(&tmp_c_file_path)); - - ZigList clang_argv = {0}; - - add_cc_args(ira->codegen, clang_argv, buf_ptr(tmp_dep_file), true, FileExtC); - - clang_argv.append(buf_ptr(&tmp_c_file_path)); - - if (ira->codegen->verbose_cc) { - fprintf(stderr, "clang"); - for (size_t i = 0; i < clang_argv.length; i += 1) { - fprintf(stderr, " %s", clang_argv.at(i)); - } - fprintf(stderr, "\n"); - } - - clang_argv.append(nullptr); // to make the [start...end] argument work - - Stage2ErrorMsg *errors_ptr; - size_t errors_len; - Stage2Ast *ast; - - const char *resources_path = buf_ptr(ira->codegen->zig_c_headers_dir); - - if ((err = stage2_translate_c(&ast, &errors_ptr, &errors_len, - &clang_argv.at(0), &clang_argv.last(), resources_path))) - { - if (err != ErrorCCompileErrors) { - ir_add_error_node(ira, node, buf_sprintf("C import failed: %s", err_str(err))); - return ira->codegen->invalid_inst_gen; - } - - ErrorMsg *parent_err_msg = ir_add_error_node(ira, node, buf_sprintf("C import failed")); - if (ira->codegen->libc_link_lib == nullptr) { - add_error_note(ira->codegen, parent_err_msg, node, - buf_sprintf("libc headers not available; compilation does not link against libc")); - } - for (size_t i = 0; i < errors_len; i += 1) { - Stage2ErrorMsg *clang_err = &errors_ptr[i]; - // Clang can emit "too many errors, stopping now", in which case `source` and `filename_ptr` are null - if (clang_err->source && clang_err->filename_ptr) { - ErrorMsg *err_msg = err_msg_create_with_offset( - clang_err->filename_ptr ? - buf_create_from_mem(clang_err->filename_ptr, clang_err->filename_len) : buf_alloc(), - clang_err->line, clang_err->column, clang_err->offset, clang_err->source, - buf_create_from_mem(clang_err->msg_ptr, clang_err->msg_len)); - err_msg_add_note(parent_err_msg, err_msg); - } - } - - return ira->codegen->invalid_inst_gen; - } - if (ira->codegen->verbose_cimport) { - fprintf(stderr, "@cImport .d file: %s\n", buf_ptr(tmp_dep_file)); - } - - if ((err = cache_add_dep_file(cache_hash, tmp_dep_file, false))) { - ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to parse .d file: %s", err_str(err))); - return ira->codegen->invalid_inst_gen; - } - if ((err = cache_final(cache_hash, &tmp_c_file_digest))) { - ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to finalize cache: %s", err_str(err))); - return ira->codegen->invalid_inst_gen; - } - - ir_cimport_cache_paths(ira->codegen->cache_dir, &tmp_c_file_digest, out_zig_dir, out_zig_path); - if ((err = os_make_path(out_zig_dir))) { - ir_add_error_node(ira, node, buf_sprintf("C import failed: unable to make output dir: %s", err_str(err))); - return ira->codegen->invalid_inst_gen; - } - FILE *out_file = fopen(buf_ptr(out_zig_path), "wb"); - if (out_file == nullptr) { - ir_add_error_node(ira, node, - buf_sprintf("C import failed: unable to open output file: %s", strerror(errno))); - return ira->codegen->invalid_inst_gen; - } - stage2_render_ast(ast, out_file); - if (fclose(out_file) != 0) { - ir_add_error_node(ira, node, - buf_sprintf("C import failed: unable to write to output file: %s", strerror(errno))); - return ira->codegen->invalid_inst_gen; - } - - if (ira->codegen->verbose_cimport) { - fprintf(stderr, "@cImport output: %s\n", buf_ptr(out_zig_path)); - } - - } else { - // Cache Hit - ir_cimport_cache_paths(ira->codegen->cache_dir, &tmp_c_file_digest, out_zig_dir, out_zig_path); - if (ira->codegen->verbose_cimport) { - fprintf(stderr, "@cImport cache hit: %s\n", buf_ptr(out_zig_path)); - } - } - - Buf *import_code = buf_alloc(); - if ((err = file_fetch(ira->codegen, out_zig_path, import_code))) { - ir_add_error_node(ira, node, - buf_sprintf("unable to open '%s': %s", buf_ptr(out_zig_path), err_str(err))); - return ira->codegen->invalid_inst_gen; - } - ZigType *child_import = add_source_file(ira->codegen, cimport_pkg, out_zig_path, - import_code, SourceKindCImport); - return ir_const_type(ira, &instruction->base.base, child_import); -} - -static IrInstGen *ir_analyze_instruction_c_include(IrAnalyze *ira, IrInstSrcCInclude *instruction) { - IrInstGen *name_value = instruction->name->child; - if (type_is_invalid(name_value->value->type)) - return ira->codegen->invalid_inst_gen; - - Buf *include_name = ir_resolve_str(ira, name_value); - if (!include_name) - return ira->codegen->invalid_inst_gen; - - Buf *c_import_buf = ira->new_irb.exec->c_import_buf; - // We check for this error in pass1 - assert(c_import_buf); - - buf_appendf(c_import_buf, "#include <%s>\n", buf_ptr(include_name)); - - return ir_const_void(ira, &instruction->base.base); -} - -static IrInstGen *ir_analyze_instruction_c_define(IrAnalyze *ira, IrInstSrcCDefine *instruction) { - IrInstGen *name = instruction->name->child; - if (type_is_invalid(name->value->type)) - return ira->codegen->invalid_inst_gen; - - Buf *define_name = ir_resolve_str(ira, name); - if (!define_name) - return ira->codegen->invalid_inst_gen; - - IrInstGen *value = instruction->value->child; - if (type_is_invalid(value->value->type)) - return ira->codegen->invalid_inst_gen; - - Buf *define_value = nullptr; - // The second parameter is either a string or void (equivalent to "") - if (value->value->type->id != ZigTypeIdVoid) { - define_value = ir_resolve_str(ira, value); - if (!define_value) - return ira->codegen->invalid_inst_gen; - } - - Buf *c_import_buf = ira->new_irb.exec->c_import_buf; - // We check for this error in pass1 - assert(c_import_buf); - - buf_appendf(c_import_buf, "#define %s %s\n", buf_ptr(define_name), - define_value ? buf_ptr(define_value) : ""); - - return ir_const_void(ira, &instruction->base.base); -} - -static IrInstGen *ir_analyze_instruction_c_undef(IrAnalyze *ira, IrInstSrcCUndef *instruction) { - IrInstGen *name = instruction->name->child; - if (type_is_invalid(name->value->type)) - return ira->codegen->invalid_inst_gen; - - Buf *undef_name = ir_resolve_str(ira, name); - if (!undef_name) - return ira->codegen->invalid_inst_gen; - - Buf *c_import_buf = ira->new_irb.exec->c_import_buf; - // We check for this error in pass1 - assert(c_import_buf); - - buf_appendf(c_import_buf, "#undef %s\n", buf_ptr(undef_name)); - - return ir_const_void(ira, &instruction->base.base); -} - -static IrInstGen *ir_analyze_instruction_embed_file(IrAnalyze *ira, IrInstSrcEmbedFile *instruction) { - IrInstGen *name = instruction->name->child; - if (type_is_invalid(name->value->type)) - return ira->codegen->invalid_inst_gen; - - Buf *rel_file_path = ir_resolve_str(ira, name); - if (!rel_file_path) - return ira->codegen->invalid_inst_gen; - - ZigType *import = get_scope_import(instruction->base.base.scope); - // figure out absolute path to resource - Buf source_dir_path = BUF_INIT; - os_path_dirname(import->data.structure.root_struct->path, &source_dir_path); - - Buf *resolve_paths[] = { - &source_dir_path, - rel_file_path, - }; - Buf *file_path = buf_alloc(); - *file_path = os_path_resolve(resolve_paths, 2); - - // load from file system into const expr - Buf *file_contents = buf_alloc(); - Error err; - if ((err = file_fetch(ira->codegen, file_path, file_contents))) { - if (err == ErrorFileNotFound) { - ir_add_error(ira, &instruction->name->base, - buf_sprintf("unable to find '%s'", buf_ptr(file_path))); - return ira->codegen->invalid_inst_gen; - } else { - ir_add_error(ira, &instruction->name->base, - buf_sprintf("unable to open '%s': %s", buf_ptr(file_path), err_str(err))); - return ira->codegen->invalid_inst_gen; - } - } - - IrInstGen *result = ir_const(ira, &instruction->base.base, nullptr); - init_const_str_lit(ira->codegen, result->value, file_contents); - return result; -} - -static IrInstGen *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstSrcCmpxchg *instruction) { - ZigType *operand_type = ir_resolve_atomic_operand_type(ira, instruction->type_value->child); - if (type_is_invalid(operand_type)) - return ira->codegen->invalid_inst_gen; - - if (operand_type->id == ZigTypeIdFloat) { - ir_add_error(ira, &instruction->type_value->child->base, - buf_sprintf("expected bool, integer, enum or pointer type, found '%s'", buf_ptr(&operand_type->name))); - return ira->codegen->invalid_inst_gen; - } - - IrInstGen *ptr = instruction->ptr->child; - if (type_is_invalid(ptr->value->type)) - return ira->codegen->invalid_inst_gen; - - // TODO let this be volatile - ZigType *ptr_type = get_pointer_to_type(ira->codegen, operand_type, false); - IrInstGen *casted_ptr = ir_implicit_cast2(ira, &instruction->ptr->base, ptr, ptr_type); - if (type_is_invalid(casted_ptr->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *cmp_value = instruction->cmp_value->child; - if (type_is_invalid(cmp_value->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *new_value = instruction->new_value->child; - if (type_is_invalid(new_value->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *success_order_value = instruction->success_order_value->child; - if (type_is_invalid(success_order_value->value->type)) - return ira->codegen->invalid_inst_gen; - - AtomicOrder success_order; - if (!ir_resolve_atomic_order(ira, success_order_value, &success_order)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *failure_order_value = instruction->failure_order_value->child; - if (type_is_invalid(failure_order_value->value->type)) - return ira->codegen->invalid_inst_gen; - - AtomicOrder failure_order; - if (!ir_resolve_atomic_order(ira, failure_order_value, &failure_order)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *casted_cmp_value = ir_implicit_cast2(ira, &instruction->cmp_value->base, cmp_value, operand_type); - if (type_is_invalid(casted_cmp_value->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *casted_new_value = ir_implicit_cast2(ira, &instruction->new_value->base, new_value, operand_type); - if (type_is_invalid(casted_new_value->value->type)) - return ira->codegen->invalid_inst_gen; - - if (success_order < AtomicOrderMonotonic) { - ir_add_error(ira, &success_order_value->base, - buf_sprintf("success atomic ordering must be Monotonic or stricter")); - return ira->codegen->invalid_inst_gen; - } - if (failure_order < AtomicOrderMonotonic) { - ir_add_error(ira, &failure_order_value->base, - buf_sprintf("failure atomic ordering must be Monotonic or stricter")); - return ira->codegen->invalid_inst_gen; - } - if (failure_order > success_order) { - ir_add_error(ira, &failure_order_value->base, - buf_sprintf("failure atomic ordering must be no stricter than success")); - return ira->codegen->invalid_inst_gen; - } - if (failure_order == AtomicOrderRelease || failure_order == AtomicOrderAcqRel) { - ir_add_error(ira, &failure_order_value->base, - buf_sprintf("failure atomic ordering must not be Release or AcqRel")); - return ira->codegen->invalid_inst_gen; - } - - ZigType *result_type = get_optional_type(ira->codegen, operand_type); - - // special case zero bit types - switch (type_has_one_possible_value(ira->codegen, operand_type)) { - case OnePossibleValueInvalid: - return ira->codegen->invalid_inst_gen; - case OnePossibleValueYes: { - IrInstGen *result = ir_const(ira, &instruction->base.base, result_type); - set_optional_value_to_null(result->value); - return result; - } - case OnePossibleValueNo: - break; - } - - if (instr_is_comptime(casted_ptr) && casted_ptr->value->data.x_ptr.mut != ConstPtrMutRuntimeVar && - instr_is_comptime(casted_cmp_value) && instr_is_comptime(casted_new_value)) { - ZigValue *ptr_val = ir_resolve_const(ira, casted_ptr, UndefBad); - if (ptr_val == nullptr) - return ira->codegen->invalid_inst_gen; - - ZigValue *stored_val = const_ptr_pointee(ira, ira->codegen, ptr_val, instruction->base.base.source_node); - if (stored_val == nullptr) - return ira->codegen->invalid_inst_gen; - - ZigValue *expected_val = ir_resolve_const(ira, casted_cmp_value, UndefBad); - if (expected_val == nullptr) - return ira->codegen->invalid_inst_gen; - - ZigValue *new_val = ir_resolve_const(ira, casted_new_value, UndefBad); - if (new_val == nullptr) - return ira->codegen->invalid_inst_gen; - - bool eql = const_values_equal(ira->codegen, stored_val, expected_val); - IrInstGen *result = ir_const(ira, &instruction->base.base, result_type); - if (eql) { - copy_const_val(ira->codegen, stored_val, new_val); - set_optional_value_to_null(result->value); - } else { - set_optional_payload(result->value, stored_val); - } - return result; - } - - IrInstGen *result_loc; - if (handle_is_ptr(ira->codegen, result_type)) { - result_loc = ir_resolve_result(ira, &instruction->base.base, instruction->result_loc, - result_type, nullptr, true, true); - if (type_is_invalid(result_loc->value->type) || result_loc->value->type->id == ZigTypeIdUnreachable) { - return result_loc; - } - } else { - result_loc = nullptr; - } - - return ir_build_cmpxchg_gen(ira, &instruction->base.base, result_type, - casted_ptr, casted_cmp_value, casted_new_value, - success_order, failure_order, instruction->is_weak, result_loc); -} - -static IrInstGen *ir_analyze_instruction_fence(IrAnalyze *ira, IrInstSrcFence *instruction) { - IrInstGen *order_inst = instruction->order->child; - if (type_is_invalid(order_inst->value->type)) - return ira->codegen->invalid_inst_gen; - - AtomicOrder order; - if (!ir_resolve_atomic_order(ira, order_inst, &order)) - return ira->codegen->invalid_inst_gen; - - if (order < AtomicOrderAcquire) { - ir_add_error(ira, &order_inst->base, - buf_sprintf("atomic ordering must be Acquire or stricter")); - return ira->codegen->invalid_inst_gen; - } - - return ir_build_fence_gen(ira, &instruction->base.base, order); -} - -static IrInstGen *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstSrcTruncate *instruction) { - IrInstGen *dest_type_value = instruction->dest_type->child; - ZigType *dest_type = ir_resolve_type(ira, dest_type_value); - if (type_is_invalid(dest_type)) - return ira->codegen->invalid_inst_gen; - - if (dest_type->id != ZigTypeIdInt && - dest_type->id != ZigTypeIdComptimeInt) - { - ir_add_error(ira, &dest_type_value->base, buf_sprintf("expected integer type, found '%s'", buf_ptr(&dest_type->name))); - return ira->codegen->invalid_inst_gen; - } - - IrInstGen *target = instruction->target->child; - ZigType *src_type = target->value->type; - if (type_is_invalid(src_type)) - return ira->codegen->invalid_inst_gen; - - if (src_type->id != ZigTypeIdInt && - src_type->id != ZigTypeIdComptimeInt) - { - ir_add_error(ira, &target->base, buf_sprintf("expected integer type, found '%s'", buf_ptr(&src_type->name))); - return ira->codegen->invalid_inst_gen; - } - - if (dest_type->id == ZigTypeIdComptimeInt) { - return ir_implicit_cast2(ira, &instruction->target->base, target, dest_type); - } - - if (instr_is_comptime(target)) { - ZigValue *val = ir_resolve_const(ira, target, UndefBad); - if (val == nullptr) - return ira->codegen->invalid_inst_gen; - - IrInstGen *result = ir_const(ira, &instruction->base.base, dest_type); - bigint_truncate(&result->value->data.x_bigint, &val->data.x_bigint, - dest_type->data.integral.bit_count, dest_type->data.integral.is_signed); - return result; - } - - if (src_type->data.integral.bit_count == 0 || dest_type->data.integral.bit_count == 0) { - IrInstGen *result = ir_const(ira, &instruction->base.base, dest_type); - bigint_init_unsigned(&result->value->data.x_bigint, 0); - return result; - } - - if (src_type->data.integral.is_signed != dest_type->data.integral.is_signed) { - const char *sign_str = dest_type->data.integral.is_signed ? "signed" : "unsigned"; - ir_add_error(ira, &target->base, buf_sprintf("expected %s integer type, found '%s'", sign_str, buf_ptr(&src_type->name))); - return ira->codegen->invalid_inst_gen; - } else if (src_type->data.integral.bit_count < dest_type->data.integral.bit_count) { - ir_add_error(ira, &target->base, buf_sprintf("type '%s' has fewer bits than destination type '%s'", - buf_ptr(&src_type->name), buf_ptr(&dest_type->name))); - return ira->codegen->invalid_inst_gen; - } - - return ir_build_truncate_gen(ira, &instruction->base.base, dest_type, target); -} - -static IrInstGen *ir_analyze_instruction_int_cast(IrAnalyze *ira, IrInstSrcIntCast *instruction) { - ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child); - if (type_is_invalid(dest_type)) - return ira->codegen->invalid_inst_gen; - - if (dest_type->id != ZigTypeIdInt && dest_type->id != ZigTypeIdComptimeInt) { - ir_add_error(ira, &instruction->dest_type->base, buf_sprintf("expected integer type, found '%s'", buf_ptr(&dest_type->name))); - return ira->codegen->invalid_inst_gen; - } - - IrInstGen *target = instruction->target->child; - if (type_is_invalid(target->value->type)) - return ira->codegen->invalid_inst_gen; - - if (target->value->type->id != ZigTypeIdInt && target->value->type->id != ZigTypeIdComptimeInt) { - ir_add_error(ira, &instruction->target->base, buf_sprintf("expected integer type, found '%s'", - buf_ptr(&target->value->type->name))); - return ira->codegen->invalid_inst_gen; - } - - if (instr_is_comptime(target) || dest_type->id == ZigTypeIdComptimeInt) { - ZigValue *val = ir_resolve_const(ira, target, UndefBad); - if (val == nullptr) - return ira->codegen->invalid_inst_gen; - - return ir_implicit_cast2(ira, &instruction->target->base, target, dest_type); - } - - return ir_analyze_widen_or_shorten(ira, &instruction->base.base, target, dest_type); -} - -static IrInstGen *ir_analyze_instruction_float_cast(IrAnalyze *ira, IrInstSrcFloatCast *instruction) { - ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child); - if (type_is_invalid(dest_type)) - return ira->codegen->invalid_inst_gen; - - if (dest_type->id != ZigTypeIdFloat && dest_type->id != ZigTypeIdComptimeFloat) { - ir_add_error(ira, &instruction->dest_type->base, - buf_sprintf("expected float type, found '%s'", buf_ptr(&dest_type->name))); - return ira->codegen->invalid_inst_gen; - } - - IrInstGen *target = instruction->target->child; - if (type_is_invalid(target->value->type)) - return ira->codegen->invalid_inst_gen; - - if (target->value->type->id == ZigTypeIdComptimeInt || - target->value->type->id == ZigTypeIdComptimeFloat) - { - if (ir_num_lit_fits_in_other_type(ira, target, dest_type, true)) { - CastOp op; - if (target->value->type->id == ZigTypeIdComptimeInt) { - op = CastOpIntToFloat; - } else { - op = CastOpNumLitToConcrete; - } - return ir_resolve_cast(ira, &instruction->base.base, target, dest_type, op); - } else { - return ira->codegen->invalid_inst_gen; - } - } - - if (target->value->type->id != ZigTypeIdFloat) { - ir_add_error(ira, &instruction->target->base, buf_sprintf("expected float type, found '%s'", - buf_ptr(&target->value->type->name))); - return ira->codegen->invalid_inst_gen; - } - - if (instr_is_comptime(target) || dest_type->id == ZigTypeIdComptimeFloat) { - ZigValue *val = ir_resolve_const(ira, target, UndefBad); - if (val == nullptr) - return ira->codegen->invalid_inst_gen; - - return ir_analyze_widen_or_shorten(ira, &instruction->target->base, target, dest_type); - } - - return ir_analyze_widen_or_shorten(ira, &instruction->base.base, target, dest_type); -} - -static IrInstGen *ir_analyze_instruction_err_set_cast(IrAnalyze *ira, IrInstSrcErrSetCast *instruction) { - ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child); - if (type_is_invalid(dest_type)) - return ira->codegen->invalid_inst_gen; - - if (dest_type->id != ZigTypeIdErrorSet) { - ir_add_error(ira, &instruction->dest_type->base, - buf_sprintf("expected error set type, found '%s'", buf_ptr(&dest_type->name))); - return ira->codegen->invalid_inst_gen; - } - - IrInstGen *target = instruction->target->child; - if (type_is_invalid(target->value->type)) - return ira->codegen->invalid_inst_gen; - - if (target->value->type->id != ZigTypeIdErrorSet) { - ir_add_error(ira, &instruction->target->base, - buf_sprintf("expected error set type, found '%s'", buf_ptr(&target->value->type->name))); - return ira->codegen->invalid_inst_gen; - } - - return ir_analyze_err_set_cast(ira, &instruction->base.base, target, dest_type); -} - -static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align) { - Error err; - - ZigType *ptr_type; - if (is_slice(ty)) { - TypeStructField *ptr_field = ty->data.structure.fields[slice_ptr_index]; - ptr_type = resolve_struct_field_type(ira->codegen, ptr_field); - } else { - ptr_type = get_src_ptr_type(ty); - } - assert(ptr_type != nullptr); - if (ptr_type->id == ZigTypeIdPointer) { - if ((err = type_resolve(ira->codegen, ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) - return err; - } else if (is_slice(ptr_type)) { - TypeStructField *ptr_field = ptr_type->data.structure.fields[slice_ptr_index]; - ZigType *slice_ptr_type = resolve_struct_field_type(ira->codegen, ptr_field); - if ((err = type_resolve(ira->codegen, slice_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) - return err; - } - - *result_align = get_ptr_align(ira->codegen, ty); - return ErrorNone; -} - -static IrInstGen *ir_analyze_instruction_int_to_float(IrAnalyze *ira, IrInstSrcIntToFloat *instruction) { - ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child); - if (type_is_invalid(dest_type)) - return ira->codegen->invalid_inst_gen; - - if (dest_type->id != ZigTypeIdFloat && dest_type->id != ZigTypeIdComptimeFloat) { - ir_add_error(ira, &instruction->dest_type->base, - buf_sprintf("expected float type, found '%s'", buf_ptr(&dest_type->name))); - return ira->codegen->invalid_inst_gen; - } - - IrInstGen *target = instruction->target->child; - if (type_is_invalid(target->value->type)) - return ira->codegen->invalid_inst_gen; - - if (target->value->type->id != ZigTypeIdInt && target->value->type->id != ZigTypeIdComptimeInt) { - ir_add_error(ira, &instruction->target->base, buf_sprintf("expected int type, found '%s'", - buf_ptr(&target->value->type->name))); - return ira->codegen->invalid_inst_gen; - } - - return ir_resolve_cast(ira, &instruction->base.base, target, dest_type, CastOpIntToFloat); -} - -static IrInstGen *ir_analyze_instruction_float_to_int(IrAnalyze *ira, IrInstSrcFloatToInt *instruction) { - ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child); - if (type_is_invalid(dest_type)) - return ira->codegen->invalid_inst_gen; - - if (dest_type->id != ZigTypeIdInt && dest_type->id != ZigTypeIdComptimeInt) { - ir_add_error(ira, &instruction->dest_type->base, buf_sprintf("expected integer type, found '%s'", buf_ptr(&dest_type->name))); - return ira->codegen->invalid_inst_gen; - } - - IrInstGen *target = instruction->target->child; - if (type_is_invalid(target->value->type)) - return ira->codegen->invalid_inst_gen; - - if (target->value->type->id == ZigTypeIdComptimeInt) { - return ir_implicit_cast(ira, target, dest_type); - } - - if (target->value->type->id != ZigTypeIdFloat && target->value->type->id != ZigTypeIdComptimeFloat) { - ir_add_error_node(ira, target->base.source_node, buf_sprintf("expected float type, found '%s'", - buf_ptr(&target->value->type->name))); - return ira->codegen->invalid_inst_gen; - } - - return ir_resolve_cast(ira, &instruction->base.base, target, dest_type, CastOpFloatToInt); -} - -static IrInstGen *ir_analyze_instruction_err_to_int(IrAnalyze *ira, IrInstSrcErrToInt *instruction) { - IrInstGen *target = instruction->target->child; - if (type_is_invalid(target->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *casted_target; - if (target->value->type->id == ZigTypeIdErrorSet) { - casted_target = target; - } else { - casted_target = ir_implicit_cast(ira, target, ira->codegen->builtin_types.entry_global_error_set); - if (type_is_invalid(casted_target->value->type)) - return ira->codegen->invalid_inst_gen; - } - - return ir_analyze_err_to_int(ira, &instruction->base.base, casted_target, ira->codegen->err_tag_type); -} - -static IrInstGen *ir_analyze_instruction_int_to_err(IrAnalyze *ira, IrInstSrcIntToErr *instruction) { - IrInstGen *target = instruction->target->child; - if (type_is_invalid(target->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *casted_target = ir_implicit_cast(ira, target, ira->codegen->err_tag_type); - if (type_is_invalid(casted_target->value->type)) - return ira->codegen->invalid_inst_gen; - - return ir_analyze_int_to_err(ira, &instruction->base.base, casted_target, ira->codegen->builtin_types.entry_global_error_set); -} - -static IrInstGen *ir_analyze_instruction_bool_to_int(IrAnalyze *ira, IrInstSrcBoolToInt *instruction) { - IrInstGen *target = instruction->target->child; - if (type_is_invalid(target->value->type)) - return ira->codegen->invalid_inst_gen; - - if (target->value->type->id != ZigTypeIdBool) { - ir_add_error(ira, &instruction->target->base, buf_sprintf("expected bool, found '%s'", - buf_ptr(&target->value->type->name))); - return ira->codegen->invalid_inst_gen; - } - - if (instr_is_comptime(target)) { - bool is_true; - if (!ir_resolve_bool(ira, target, &is_true)) - return ira->codegen->invalid_inst_gen; - - return ir_const_unsigned(ira, &instruction->base.base, is_true ? 1 : 0); - } - - ZigType *u1_type = get_int_type(ira->codegen, false, 1); - return ir_resolve_cast(ira, &instruction->base.base, target, u1_type, CastOpBoolToInt); -} - -static IrInstGen *ir_analyze_instruction_vector_type(IrAnalyze *ira, IrInstSrcVectorType *instruction) { - uint64_t len; - if (!ir_resolve_unsigned(ira, instruction->len->child, ira->codegen->builtin_types.entry_u32, &len)) - return ira->codegen->invalid_inst_gen; - - ZigType *elem_type = ir_resolve_vector_elem_type(ira, instruction->elem_type->child); - if (type_is_invalid(elem_type)) - return ira->codegen->invalid_inst_gen; - - ZigType *vector_type = get_vector_type(ira->codegen, len, elem_type); - - return ir_const_type(ira, &instruction->base.base, vector_type); -} - -static IrInstGen *ir_analyze_shuffle_vector(IrAnalyze *ira, IrInst* source_instr, - ZigType *scalar_type, IrInstGen *a, IrInstGen *b, IrInstGen *mask) -{ - Error err; - ir_assert(source_instr && scalar_type && a && b && mask, source_instr); - - if ((err = ir_validate_vector_elem_type(ira, source_instr->source_node, scalar_type))) - return ira->codegen->invalid_inst_gen; - - uint32_t len_mask; - if (mask->value->type->id == ZigTypeIdVector) { - len_mask = mask->value->type->data.vector.len; - } else if (mask->value->type->id == ZigTypeIdArray) { - len_mask = mask->value->type->data.array.len; - } else { - ir_add_error(ira, &mask->base, - buf_sprintf("expected vector or array, found '%s'", - buf_ptr(&mask->value->type->name))); - return ira->codegen->invalid_inst_gen; - } - mask = ir_implicit_cast(ira, mask, get_vector_type(ira->codegen, len_mask, - ira->codegen->builtin_types.entry_i32)); - if (type_is_invalid(mask->value->type)) - return ira->codegen->invalid_inst_gen; - - uint32_t len_a; - if (a->value->type->id == ZigTypeIdVector) { - len_a = a->value->type->data.vector.len; - } else if (a->value->type->id == ZigTypeIdArray) { - len_a = a->value->type->data.array.len; - } else if (a->value->type->id == ZigTypeIdUndefined) { - len_a = UINT32_MAX; - } else { - ir_add_error(ira, &a->base, - buf_sprintf("expected vector or array with element type '%s', found '%s'", - buf_ptr(&scalar_type->name), - buf_ptr(&a->value->type->name))); - return ira->codegen->invalid_inst_gen; - } - - uint32_t len_b; - if (b->value->type->id == ZigTypeIdVector) { - len_b = b->value->type->data.vector.len; - } else if (b->value->type->id == ZigTypeIdArray) { - len_b = b->value->type->data.array.len; - } else if (b->value->type->id == ZigTypeIdUndefined) { - len_b = UINT32_MAX; - } else { - ir_add_error(ira, &b->base, - buf_sprintf("expected vector or array with element type '%s', found '%s'", - buf_ptr(&scalar_type->name), - buf_ptr(&b->value->type->name))); - return ira->codegen->invalid_inst_gen; - } - - if (len_a == UINT32_MAX && len_b == UINT32_MAX) { - return ir_const_undef(ira, &a->base, get_vector_type(ira->codegen, len_mask, scalar_type)); - } - - if (len_a == UINT32_MAX) { - len_a = len_b; - a = ir_const_undef(ira, &a->base, get_vector_type(ira->codegen, len_a, scalar_type)); - } else { - a = ir_implicit_cast(ira, a, get_vector_type(ira->codegen, len_a, scalar_type)); - if (type_is_invalid(a->value->type)) - return ira->codegen->invalid_inst_gen; - } - - if (len_b == UINT32_MAX) { - len_b = len_a; - b = ir_const_undef(ira, &b->base, get_vector_type(ira->codegen, len_b, scalar_type)); - } else { - b = ir_implicit_cast(ira, b, get_vector_type(ira->codegen, len_b, scalar_type)); - if (type_is_invalid(b->value->type)) - return ira->codegen->invalid_inst_gen; - } - - ZigValue *mask_val = ir_resolve_const(ira, mask, UndefOk); - if (mask_val == nullptr) - return ira->codegen->invalid_inst_gen; - - expand_undef_array(ira->codegen, mask_val); - - for (uint32_t i = 0; i < len_mask; i += 1) { - ZigValue *mask_elem_val = &mask_val->data.x_array.data.s_none.elements[i]; - if (mask_elem_val->special == ConstValSpecialUndef) - continue; - int32_t v_i32 = bigint_as_signed(&mask_elem_val->data.x_bigint); - uint32_t v; - IrInstGen *chosen_operand; - if (v_i32 >= 0) { - v = (uint32_t)v_i32; - chosen_operand = a; - } else { - v = (uint32_t)~v_i32; - chosen_operand = b; - } - if (v >= chosen_operand->value->type->data.vector.len) { - ErrorMsg *msg = ir_add_error(ira, &mask->base, - buf_sprintf("mask index '%u' has out-of-bounds selection", i)); - add_error_note(ira->codegen, msg, chosen_operand->base.source_node, - buf_sprintf("selected index '%u' out of bounds of %s", v, - buf_ptr(&chosen_operand->value->type->name))); - if (chosen_operand == a && v < len_a + len_b) { - add_error_note(ira->codegen, msg, b->base.source_node, - buf_create_from_str("selections from the second vector are specified with negative numbers")); - } - return ira->codegen->invalid_inst_gen; - } - } - - ZigType *result_type = get_vector_type(ira->codegen, len_mask, scalar_type); - if (instr_is_comptime(a) && instr_is_comptime(b)) { - ZigValue *a_val = ir_resolve_const(ira, a, UndefOk); - if (a_val == nullptr) - return ira->codegen->invalid_inst_gen; - - ZigValue *b_val = ir_resolve_const(ira, b, UndefOk); - if (b_val == nullptr) - return ira->codegen->invalid_inst_gen; - - expand_undef_array(ira->codegen, a_val); - expand_undef_array(ira->codegen, b_val); - - IrInstGen *result = ir_const(ira, source_instr, result_type); - result->value->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(len_mask); - for (uint32_t i = 0; i < mask_val->type->data.vector.len; i += 1) { - ZigValue *mask_elem_val = &mask_val->data.x_array.data.s_none.elements[i]; - ZigValue *result_elem_val = &result->value->data.x_array.data.s_none.elements[i]; - if (mask_elem_val->special == ConstValSpecialUndef) { - result_elem_val->special = ConstValSpecialUndef; - continue; - } - int32_t v = bigint_as_signed(&mask_elem_val->data.x_bigint); - // We've already checked for and emitted compile errors for index out of bounds here. - ZigValue *src_elem_val = (v >= 0) ? - &a->value->data.x_array.data.s_none.elements[v] : - &b->value->data.x_array.data.s_none.elements[~v]; - copy_const_val(ira->codegen, result_elem_val, src_elem_val); - - ir_assert(result_elem_val->special == ConstValSpecialStatic, source_instr); - } - result->value->special = ConstValSpecialStatic; - return result; - } - - // All static analysis passed, and not comptime. - // For runtime codegen, vectors a and b must be the same length. Here we - // recursively @shuffle the smaller vector to append undefined elements - // to it up to the length of the longer vector. This recursion terminates - // in 1 call because these calls to ir_analyze_shuffle_vector guarantee - // len_a == len_b. - if (len_a != len_b) { - uint32_t len_min = min(len_a, len_b); - uint32_t len_max = max(len_a, len_b); - - IrInstGen *expand_mask = ir_const(ira, &mask->base, - get_vector_type(ira->codegen, len_max, ira->codegen->builtin_types.entry_i32)); - expand_mask->value->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(len_max); - uint32_t i = 0; - for (; i < len_min; i += 1) - bigint_init_unsigned(&expand_mask->value->data.x_array.data.s_none.elements[i].data.x_bigint, i); - for (; i < len_max; i += 1) - bigint_init_signed(&expand_mask->value->data.x_array.data.s_none.elements[i].data.x_bigint, -1); - - IrInstGen *undef = ir_const_undef(ira, source_instr, - get_vector_type(ira->codegen, len_min, scalar_type)); - - if (len_b < len_a) { - b = ir_analyze_shuffle_vector(ira, source_instr, scalar_type, b, undef, expand_mask); - } else { - a = ir_analyze_shuffle_vector(ira, source_instr, scalar_type, a, undef, expand_mask); - } - } - - return ir_build_shuffle_vector_gen(ira, source_instr->scope, source_instr->source_node, - result_type, a, b, mask); -} - -static IrInstGen *ir_analyze_instruction_shuffle_vector(IrAnalyze *ira, IrInstSrcShuffleVector *instruction) { - ZigType *scalar_type = ir_resolve_vector_elem_type(ira, instruction->scalar_type->child); - if (type_is_invalid(scalar_type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *a = instruction->a->child; - if (type_is_invalid(a->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *b = instruction->b->child; - if (type_is_invalid(b->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *mask = instruction->mask->child; - if (type_is_invalid(mask->value->type)) - return ira->codegen->invalid_inst_gen; - - return ir_analyze_shuffle_vector(ira, &instruction->base.base, scalar_type, a, b, mask); -} - -static IrInstGen *ir_analyze_instruction_splat(IrAnalyze *ira, IrInstSrcSplat *instruction) { - Error err; - - IrInstGen *len = instruction->len->child; - if (type_is_invalid(len->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *scalar = instruction->scalar->child; - if (type_is_invalid(scalar->value->type)) - return ira->codegen->invalid_inst_gen; - - uint64_t len_u64; - if (!ir_resolve_unsigned(ira, len, ira->codegen->builtin_types.entry_u32, &len_u64)) - return ira->codegen->invalid_inst_gen; - uint32_t len_int = len_u64; - - if ((err = ir_validate_vector_elem_type(ira, scalar->base.source_node, scalar->value->type))) - return ira->codegen->invalid_inst_gen; - - ZigType *return_type = get_vector_type(ira->codegen, len_int, scalar->value->type); - - if (instr_is_comptime(scalar)) { - ZigValue *scalar_val = ir_resolve_const(ira, scalar, UndefOk); - if (scalar_val == nullptr) - return ira->codegen->invalid_inst_gen; - if (scalar_val->special == ConstValSpecialUndef) - return ir_const_undef(ira, &instruction->base.base, return_type); - - IrInstGen *result = ir_const(ira, &instruction->base.base, return_type); - result->value->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(len_int); - for (uint32_t i = 0; i < len_int; i += 1) { - copy_const_val(ira->codegen, &result->value->data.x_array.data.s_none.elements[i], scalar_val); - } - return result; - } - - return ir_build_splat_gen(ira, &instruction->base.base, return_type, scalar); -} - -static IrInstGen *ir_analyze_instruction_bool_not(IrAnalyze *ira, IrInstSrcBoolNot *instruction) { - IrInstGen *value = instruction->value->child; - if (type_is_invalid(value->value->type)) - return ira->codegen->invalid_inst_gen; - - ZigType *bool_type = ira->codegen->builtin_types.entry_bool; - - IrInstGen *casted_value = ir_implicit_cast(ira, value, bool_type); - if (type_is_invalid(casted_value->value->type)) - return ira->codegen->invalid_inst_gen; - - if (instr_is_comptime(casted_value)) { - ZigValue *value = ir_resolve_const(ira, casted_value, UndefBad); - if (value == nullptr) - return ira->codegen->invalid_inst_gen; - - return ir_const_bool(ira, &instruction->base.base, !value->data.x_bool); - } - - return ir_build_bool_not_gen(ira, &instruction->base.base, casted_value); -} - -static IrInstGen *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstSrcMemset *instruction) { - Error err; - - IrInstGen *dest_ptr = instruction->dest_ptr->child; - if (type_is_invalid(dest_ptr->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *byte_value = instruction->byte->child; - if (type_is_invalid(byte_value->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *count_value = instruction->count->child; - if (type_is_invalid(count_value->value->type)) - return ira->codegen->invalid_inst_gen; - - ZigType *dest_uncasted_type = dest_ptr->value->type; - bool dest_is_volatile = (dest_uncasted_type->id == ZigTypeIdPointer) && - dest_uncasted_type->data.pointer.is_volatile; - - ZigType *usize = ira->codegen->builtin_types.entry_usize; - ZigType *u8 = ira->codegen->builtin_types.entry_u8; - uint32_t dest_align; - if (dest_uncasted_type->id == ZigTypeIdPointer) { - if ((err = resolve_ptr_align(ira, dest_uncasted_type, &dest_align))) - return ira->codegen->invalid_inst_gen; - } else { - dest_align = get_abi_alignment(ira->codegen, u8); - } - ZigType *u8_ptr = get_pointer_to_type_extra(ira->codegen, u8, false, dest_is_volatile, - PtrLenUnknown, dest_align, 0, 0, false); - - IrInstGen *casted_dest_ptr = ir_implicit_cast(ira, dest_ptr, u8_ptr); - if (type_is_invalid(casted_dest_ptr->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *casted_byte = ir_implicit_cast(ira, byte_value, u8); - if (type_is_invalid(casted_byte->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *casted_count = ir_implicit_cast(ira, count_value, usize); - if (type_is_invalid(casted_count->value->type)) - return ira->codegen->invalid_inst_gen; - - // TODO test this at comptime with u8 and non-u8 types - if (instr_is_comptime(casted_dest_ptr) && - instr_is_comptime(casted_byte) && - instr_is_comptime(casted_count)) - { - ZigValue *dest_ptr_val = ir_resolve_const(ira, casted_dest_ptr, UndefBad); - if (dest_ptr_val == nullptr) - return ira->codegen->invalid_inst_gen; - - ZigValue *byte_val = ir_resolve_const(ira, casted_byte, UndefOk); - if (byte_val == nullptr) - return ira->codegen->invalid_inst_gen; - - ZigValue *count_val = ir_resolve_const(ira, casted_count, UndefBad); - if (count_val == nullptr) - return ira->codegen->invalid_inst_gen; - - if (casted_dest_ptr->value->data.x_ptr.special != ConstPtrSpecialHardCodedAddr && - casted_dest_ptr->value->data.x_ptr.mut != ConstPtrMutRuntimeVar) - { - ZigValue *dest_elements; - size_t start; - size_t bound_end; - switch (dest_ptr_val->data.x_ptr.special) { - case ConstPtrSpecialInvalid: - case ConstPtrSpecialDiscard: - zig_unreachable(); - case ConstPtrSpecialRef: - dest_elements = dest_ptr_val->data.x_ptr.data.ref.pointee; - start = 0; - bound_end = 1; - break; - case ConstPtrSpecialSubArray: - case ConstPtrSpecialBaseArray: - { - ZigValue *array_val = dest_ptr_val->data.x_ptr.data.base_array.array_val; - expand_undef_array(ira->codegen, array_val); - dest_elements = array_val->data.x_array.data.s_none.elements; - start = dest_ptr_val->data.x_ptr.data.base_array.elem_index; - bound_end = array_val->type->data.array.len; - break; - } - case ConstPtrSpecialBaseStruct: - zig_panic("TODO memset on const inner struct"); - case ConstPtrSpecialBaseErrorUnionCode: - zig_panic("TODO memset on const inner error union code"); - case ConstPtrSpecialBaseErrorUnionPayload: - zig_panic("TODO memset on const inner error union payload"); - case ConstPtrSpecialBaseOptionalPayload: - zig_panic("TODO memset on const inner optional payload"); - case ConstPtrSpecialHardCodedAddr: - zig_unreachable(); - case ConstPtrSpecialFunction: - zig_panic("TODO memset on ptr cast from function"); - case ConstPtrSpecialNull: - zig_panic("TODO memset on null ptr"); - } - - size_t count = bigint_as_usize(&count_val->data.x_bigint); - size_t end = start + count; - if (end > bound_end) { - ir_add_error(ira, &count_value->base, buf_sprintf("out of bounds pointer access")); - return ira->codegen->invalid_inst_gen; - } - - for (size_t i = start; i < end; i += 1) { - copy_const_val(ira->codegen, &dest_elements[i], byte_val); - } - - return ir_const_void(ira, &instruction->base.base); - } - } - - return ir_build_memset_gen(ira, &instruction->base.base, casted_dest_ptr, casted_byte, casted_count); -} - -static IrInstGen *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstSrcMemcpy *instruction) { - Error err; - - IrInstGen *dest_ptr = instruction->dest_ptr->child; - if (type_is_invalid(dest_ptr->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *src_ptr = instruction->src_ptr->child; - if (type_is_invalid(src_ptr->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *count_value = instruction->count->child; - if (type_is_invalid(count_value->value->type)) - return ira->codegen->invalid_inst_gen; - - ZigType *u8 = ira->codegen->builtin_types.entry_u8; - ZigType *dest_uncasted_type = dest_ptr->value->type; - ZigType *src_uncasted_type = src_ptr->value->type; - bool dest_is_volatile = (dest_uncasted_type->id == ZigTypeIdPointer) && - dest_uncasted_type->data.pointer.is_volatile; - bool src_is_volatile = (src_uncasted_type->id == ZigTypeIdPointer) && - src_uncasted_type->data.pointer.is_volatile; - - uint32_t dest_align; - if (dest_uncasted_type->id == ZigTypeIdPointer) { - if ((err = resolve_ptr_align(ira, dest_uncasted_type, &dest_align))) - return ira->codegen->invalid_inst_gen; - } else { - dest_align = get_abi_alignment(ira->codegen, u8); - } - - uint32_t src_align; - if (src_uncasted_type->id == ZigTypeIdPointer) { - if ((err = resolve_ptr_align(ira, src_uncasted_type, &src_align))) - return ira->codegen->invalid_inst_gen; - } else { - src_align = get_abi_alignment(ira->codegen, u8); - } - - ZigType *usize = ira->codegen->builtin_types.entry_usize; - ZigType *u8_ptr_mut = get_pointer_to_type_extra(ira->codegen, u8, false, dest_is_volatile, - PtrLenUnknown, dest_align, 0, 0, false); - ZigType *u8_ptr_const = get_pointer_to_type_extra(ira->codegen, u8, true, src_is_volatile, - PtrLenUnknown, src_align, 0, 0, false); - - IrInstGen *casted_dest_ptr = ir_implicit_cast(ira, dest_ptr, u8_ptr_mut); - if (type_is_invalid(casted_dest_ptr->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *casted_src_ptr = ir_implicit_cast(ira, src_ptr, u8_ptr_const); - if (type_is_invalid(casted_src_ptr->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *casted_count = ir_implicit_cast(ira, count_value, usize); - if (type_is_invalid(casted_count->value->type)) - return ira->codegen->invalid_inst_gen; - - // TODO test this at comptime with u8 and non-u8 types - // TODO test with dest ptr being a global runtime variable - if (instr_is_comptime(casted_dest_ptr) && - instr_is_comptime(casted_src_ptr) && - instr_is_comptime(casted_count)) - { - ZigValue *dest_ptr_val = ir_resolve_const(ira, casted_dest_ptr, UndefBad); - if (dest_ptr_val == nullptr) - return ira->codegen->invalid_inst_gen; - - ZigValue *src_ptr_val = ir_resolve_const(ira, casted_src_ptr, UndefBad); - if (src_ptr_val == nullptr) - return ira->codegen->invalid_inst_gen; - - ZigValue *count_val = ir_resolve_const(ira, casted_count, UndefBad); - if (count_val == nullptr) - return ira->codegen->invalid_inst_gen; - - if (dest_ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { - size_t count = bigint_as_usize(&count_val->data.x_bigint); - - ZigValue *dest_elements; - size_t dest_start; - size_t dest_end; - switch (dest_ptr_val->data.x_ptr.special) { - case ConstPtrSpecialInvalid: - case ConstPtrSpecialDiscard: - zig_unreachable(); - case ConstPtrSpecialRef: - dest_elements = dest_ptr_val->data.x_ptr.data.ref.pointee; - dest_start = 0; - dest_end = 1; - break; - case ConstPtrSpecialSubArray: - case ConstPtrSpecialBaseArray: - { - ZigValue *array_val = dest_ptr_val->data.x_ptr.data.base_array.array_val; - expand_undef_array(ira->codegen, array_val); - dest_elements = array_val->data.x_array.data.s_none.elements; - dest_start = dest_ptr_val->data.x_ptr.data.base_array.elem_index; - dest_end = array_val->type->data.array.len; - break; - } - case ConstPtrSpecialBaseStruct: - zig_panic("TODO memcpy on const inner struct"); - case ConstPtrSpecialBaseErrorUnionCode: - zig_panic("TODO memcpy on const inner error union code"); - case ConstPtrSpecialBaseErrorUnionPayload: - zig_panic("TODO memcpy on const inner error union payload"); - case ConstPtrSpecialBaseOptionalPayload: - zig_panic("TODO memcpy on const inner optional payload"); - case ConstPtrSpecialHardCodedAddr: - zig_unreachable(); - case ConstPtrSpecialFunction: - zig_panic("TODO memcpy on ptr cast from function"); - case ConstPtrSpecialNull: - zig_panic("TODO memcpy on null ptr"); - } - - if (dest_start + count > dest_end) { - ir_add_error(ira, &instruction->base.base, buf_sprintf("out of bounds pointer access")); - return ira->codegen->invalid_inst_gen; - } - - ZigValue *src_elements; - size_t src_start; - size_t src_end; - - switch (src_ptr_val->data.x_ptr.special) { - case ConstPtrSpecialInvalid: - case ConstPtrSpecialDiscard: - zig_unreachable(); - case ConstPtrSpecialRef: - src_elements = src_ptr_val->data.x_ptr.data.ref.pointee; - src_start = 0; - src_end = 1; - break; - case ConstPtrSpecialSubArray: - case ConstPtrSpecialBaseArray: - { - ZigValue *array_val = src_ptr_val->data.x_ptr.data.base_array.array_val; - expand_undef_array(ira->codegen, array_val); - src_elements = array_val->data.x_array.data.s_none.elements; - src_start = src_ptr_val->data.x_ptr.data.base_array.elem_index; - src_end = array_val->type->data.array.len; - break; - } - case ConstPtrSpecialBaseStruct: - zig_panic("TODO memcpy on const inner struct"); - case ConstPtrSpecialBaseErrorUnionCode: - zig_panic("TODO memcpy on const inner error union code"); - case ConstPtrSpecialBaseErrorUnionPayload: - zig_panic("TODO memcpy on const inner error union payload"); - case ConstPtrSpecialBaseOptionalPayload: - zig_panic("TODO memcpy on const inner optional payload"); - case ConstPtrSpecialHardCodedAddr: - zig_unreachable(); - case ConstPtrSpecialFunction: - zig_panic("TODO memcpy on ptr cast from function"); - case ConstPtrSpecialNull: - zig_panic("TODO memcpy on null ptr"); - } - - if (src_start + count > src_end) { - ir_add_error(ira, &instruction->base.base, buf_sprintf("out of bounds pointer access")); - return ira->codegen->invalid_inst_gen; - } - - // TODO check for noalias violations - this should be generalized to work for any function - - for (size_t i = 0; i < count; i += 1) { - copy_const_val(ira->codegen, &dest_elements[dest_start + i], &src_elements[src_start + i]); - } - - return ir_const_void(ira, &instruction->base.base); - } - } - - return ir_build_memcpy_gen(ira, &instruction->base.base, casted_dest_ptr, casted_src_ptr, casted_count); -} - -static ZigType *get_result_loc_type(IrAnalyze *ira, ResultLoc *result_loc) { - if (result_loc == nullptr) return nullptr; - - if (result_loc->id == ResultLocIdCast) { - return ir_resolve_type(ira, result_loc->source_instruction->child); - } - - return nullptr; -} - -static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *instruction) { - Error err; - - IrInstGen *ptr_ptr = instruction->ptr->child; - if (type_is_invalid(ptr_ptr->value->type)) - return ira->codegen->invalid_inst_gen; - - ZigType *ptr_ptr_type = ptr_ptr->value->type; - assert(ptr_ptr_type->id == ZigTypeIdPointer); - ZigType *array_type = ptr_ptr_type->data.pointer.child_type; - - IrInstGen *start = instruction->start->child; - if (type_is_invalid(start->value->type)) - return ira->codegen->invalid_inst_gen; - - ZigType *usize = ira->codegen->builtin_types.entry_usize; - IrInstGen *casted_start = ir_implicit_cast(ira, start, usize); - if (type_is_invalid(casted_start->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *end; - if (instruction->end) { - end = instruction->end->child; - if (type_is_invalid(end->value->type)) - return ira->codegen->invalid_inst_gen; - end = ir_implicit_cast(ira, end, usize); - if (type_is_invalid(end->value->type)) - return ira->codegen->invalid_inst_gen; - } else { - end = nullptr; - } - - ZigValue *slice_sentinel_val = nullptr; - ZigType *non_sentinel_slice_ptr_type; - ZigType *elem_type; - - bool generate_non_null_assert = false; - - if (array_type->id == ZigTypeIdArray) { - elem_type = array_type->data.array.child_type; - non_sentinel_slice_ptr_type = get_pointer_to_type_extra(ira->codegen, elem_type, - ptr_ptr_type->data.pointer.is_const, - ptr_ptr_type->data.pointer.is_volatile, - PtrLenUnknown, - ptr_ptr_type->data.pointer.explicit_alignment, 0, 0, false); - } else if (array_type->id == ZigTypeIdPointer) { - if (array_type->data.pointer.ptr_len == PtrLenSingle) { - ZigType *main_type = array_type->data.pointer.child_type; - if (main_type->id == ZigTypeIdArray) { - elem_type = main_type->data.pointer.child_type; - non_sentinel_slice_ptr_type = get_pointer_to_type_extra(ira->codegen, - elem_type, - array_type->data.pointer.is_const, array_type->data.pointer.is_volatile, - PtrLenUnknown, - array_type->data.pointer.explicit_alignment, 0, 0, false); - } else { - ir_add_error(ira, &instruction->base.base, buf_sprintf("slice of single-item pointer")); - return ira->codegen->invalid_inst_gen; - } - } else { - elem_type = array_type->data.pointer.child_type; - if (array_type->data.pointer.ptr_len == PtrLenC) { - array_type = adjust_ptr_len(ira->codegen, array_type, PtrLenUnknown); - - // C pointers are allowzero by default. - // However, we want to be able to slice them without generating an allowzero slice (see issue #4401). - // To achieve this, we generate a runtime safety check and make the slice type non-allowzero. - if (array_type->data.pointer.allow_zero) { - array_type = adjust_ptr_allow_zero(ira->codegen, array_type, false); - generate_non_null_assert = true; - } - } - ZigType *maybe_sentineled_slice_ptr_type = array_type; - non_sentinel_slice_ptr_type = adjust_ptr_sentinel(ira->codegen, maybe_sentineled_slice_ptr_type, nullptr); - if (!end) { - ir_add_error(ira, &instruction->base.base, buf_sprintf("slice of pointer must include end value")); - return ira->codegen->invalid_inst_gen; - } - } - } else if (is_slice(array_type)) { - ZigType *maybe_sentineled_slice_ptr_type = array_type->data.structure.fields[slice_ptr_index]->type_entry; - slice_sentinel_val = maybe_sentineled_slice_ptr_type->data.pointer.sentinel; - non_sentinel_slice_ptr_type = adjust_ptr_sentinel(ira->codegen, maybe_sentineled_slice_ptr_type, nullptr); - elem_type = non_sentinel_slice_ptr_type->data.pointer.child_type; - } else { - ir_add_error(ira, &instruction->base.base, - buf_sprintf("slice of non-array type '%s'", buf_ptr(&array_type->name))); - return ira->codegen->invalid_inst_gen; - } - - ZigValue *sentinel_val = nullptr; - if (instruction->sentinel) { - IrInstGen *uncasted_sentinel = instruction->sentinel->child; - if (type_is_invalid(uncasted_sentinel->value->type)) - return ira->codegen->invalid_inst_gen; - IrInstGen *sentinel = ir_implicit_cast(ira, uncasted_sentinel, elem_type); - if (type_is_invalid(sentinel->value->type)) - return ira->codegen->invalid_inst_gen; - sentinel_val = ir_resolve_const(ira, sentinel, UndefBad); - if (sentinel_val == nullptr) - return ira->codegen->invalid_inst_gen; - } - - ZigType *child_array_type = (array_type->id == ZigTypeIdPointer && - array_type->data.pointer.ptr_len == PtrLenSingle) ? array_type->data.pointer.child_type : array_type; - - ZigType *return_type; - - // If start index and end index are both comptime known, then the result type is a pointer to array - // not a slice. However, if the start or end index is a lazy value, and the result location is a slice, - // then the pointer-to-array would be casted to a slice anyway. So, we preserve the laziness of these - // values by making the return type a slice. - ZigType *res_loc_type = get_result_loc_type(ira, instruction->result_loc); - bool result_loc_is_slice = (res_loc_type != nullptr && is_slice(res_loc_type)); - bool end_is_known = !result_loc_is_slice && - ((end != nullptr && value_is_comptime(end->value)) || - (end == nullptr && child_array_type->id == ZigTypeIdArray)); - - ZigValue *array_sentinel = sentinel_val; - if (end_is_known) { - uint64_t end_scalar; - if (end != nullptr) { - ZigValue *end_val = ir_resolve_const(ira, end, UndefBad); - if (!end_val) - return ira->codegen->invalid_inst_gen; - end_scalar = bigint_as_u64(&end_val->data.x_bigint); - } else { - end_scalar = child_array_type->data.array.len; - } - array_sentinel = (child_array_type->id == ZigTypeIdArray && end_scalar == child_array_type->data.array.len) - ? child_array_type->data.array.sentinel : sentinel_val; - - if (value_is_comptime(casted_start->value)) { - ZigValue *start_val = ir_resolve_const(ira, casted_start, UndefBad); - if (!start_val) - return ira->codegen->invalid_inst_gen; - - uint64_t start_scalar = bigint_as_u64(&start_val->data.x_bigint); - - if (start_scalar > end_scalar) { - ir_add_error(ira, &instruction->base.base, buf_sprintf("out of bounds slice")); - return ira->codegen->invalid_inst_gen; - } - - uint32_t base_ptr_align = non_sentinel_slice_ptr_type->data.pointer.explicit_alignment; - uint32_t ptr_byte_alignment = 0; - if (end_scalar > start_scalar) { - if ((err = compute_elem_align(ira, elem_type, base_ptr_align, start_scalar, &ptr_byte_alignment))) - return ira->codegen->invalid_inst_gen; - } - - ZigType *return_array_type = get_array_type(ira->codegen, elem_type, end_scalar - start_scalar, - array_sentinel); - return_type = get_pointer_to_type_extra(ira->codegen, return_array_type, - non_sentinel_slice_ptr_type->data.pointer.is_const, - non_sentinel_slice_ptr_type->data.pointer.is_volatile, - PtrLenSingle, ptr_byte_alignment, 0, 0, false); - goto done_with_return_type; - } - } else if (array_sentinel == nullptr && end == nullptr) { - array_sentinel = slice_sentinel_val; - } - if (array_sentinel != nullptr) { - // TODO deal with non-abi-alignment here - ZigType *slice_ptr_type = adjust_ptr_sentinel(ira->codegen, non_sentinel_slice_ptr_type, array_sentinel); - return_type = get_slice_type(ira->codegen, slice_ptr_type); - } else { - // TODO deal with non-abi-alignment here - return_type = get_slice_type(ira->codegen, non_sentinel_slice_ptr_type); - } -done_with_return_type: - - if (instr_is_comptime(ptr_ptr) && - value_is_comptime(casted_start->value) && - (!end || value_is_comptime(end->value))) - { - ZigValue *array_val; - ZigValue *parent_ptr; - size_t abs_offset; - size_t rel_end; - bool ptr_is_undef = false; - if (child_array_type->id == ZigTypeIdArray) { - if (array_type->id == ZigTypeIdPointer) { - parent_ptr = const_ptr_pointee(ira, ira->codegen, ptr_ptr->value, instruction->base.base.source_node); - if (parent_ptr == nullptr) - return ira->codegen->invalid_inst_gen; - - if (parent_ptr->special == ConstValSpecialUndef) { - array_val = nullptr; - abs_offset = 0; - rel_end = SIZE_MAX; - ptr_is_undef = true; - } else if (parent_ptr->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) { - array_val = nullptr; - abs_offset = 0; - rel_end = SIZE_MAX; - } else { - array_val = const_ptr_pointee(ira, ira->codegen, parent_ptr, instruction->base.base.source_node); - if (array_val == nullptr) - return ira->codegen->invalid_inst_gen; - - rel_end = child_array_type->data.array.len; - abs_offset = 0; - } - } else { - array_val = const_ptr_pointee(ira, ira->codegen, ptr_ptr->value, instruction->base.base.source_node); - if (array_val == nullptr) - return ira->codegen->invalid_inst_gen; - rel_end = array_type->data.array.len; - parent_ptr = nullptr; - abs_offset = 0; - } - } else if (array_type->id == ZigTypeIdPointer) { - assert(array_type->data.pointer.ptr_len == PtrLenUnknown); - parent_ptr = const_ptr_pointee(ira, ira->codegen, ptr_ptr->value, instruction->base.base.source_node); - if (parent_ptr == nullptr) - return ira->codegen->invalid_inst_gen; - - if (parent_ptr->special == ConstValSpecialUndef) { - array_val = nullptr; - abs_offset = 0; - rel_end = SIZE_MAX; - ptr_is_undef = true; - } else switch (parent_ptr->data.x_ptr.special) { - case ConstPtrSpecialInvalid: - case ConstPtrSpecialDiscard: - zig_unreachable(); - case ConstPtrSpecialRef: - if (parent_ptr->data.x_ptr.data.ref.pointee->type->id == ZigTypeIdArray) { - array_val = parent_ptr->data.x_ptr.data.ref.pointee; - abs_offset = 0; - rel_end = array_val->type->data.array.len; - } else { - array_val = nullptr; - abs_offset = SIZE_MAX; - rel_end = 1; - } - break; - case ConstPtrSpecialSubArray: - case ConstPtrSpecialBaseArray: - array_val = parent_ptr->data.x_ptr.data.base_array.array_val; - abs_offset = parent_ptr->data.x_ptr.data.base_array.elem_index; - rel_end = array_val->type->data.array.len - abs_offset; - break; - case ConstPtrSpecialBaseStruct: - zig_panic("TODO slice const inner struct"); - case ConstPtrSpecialBaseErrorUnionCode: - zig_panic("TODO slice const inner error union code"); - case ConstPtrSpecialBaseErrorUnionPayload: - zig_panic("TODO slice const inner error union payload"); - case ConstPtrSpecialBaseOptionalPayload: - zig_panic("TODO slice const inner optional payload"); - case ConstPtrSpecialHardCodedAddr: - array_val = nullptr; - abs_offset = 0; - rel_end = SIZE_MAX; - break; - case ConstPtrSpecialFunction: - zig_panic("TODO slice of ptr cast from function"); - case ConstPtrSpecialNull: - zig_panic("TODO slice of null ptr"); - } - } else if (is_slice(array_type)) { - ZigValue *slice_ptr = const_ptr_pointee(ira, ira->codegen, ptr_ptr->value, instruction->base.base.source_node); - if (slice_ptr == nullptr) - return ira->codegen->invalid_inst_gen; - - if (slice_ptr->special == ConstValSpecialUndef) { - ir_add_error(ira, &instruction->base.base, buf_sprintf("slice of undefined")); - return ira->codegen->invalid_inst_gen; - } - - parent_ptr = slice_ptr->data.x_struct.fields[slice_ptr_index]; - if (parent_ptr->special == ConstValSpecialUndef) { - ir_add_error(ira, &instruction->base.base, buf_sprintf("slice of undefined")); - return ira->codegen->invalid_inst_gen; - } - - ZigValue *len_val = slice_ptr->data.x_struct.fields[slice_len_index]; - - switch (parent_ptr->data.x_ptr.special) { - case ConstPtrSpecialInvalid: - case ConstPtrSpecialDiscard: - zig_unreachable(); - case ConstPtrSpecialRef: - array_val = nullptr; - abs_offset = SIZE_MAX; - rel_end = 1; - break; - case ConstPtrSpecialSubArray: - case ConstPtrSpecialBaseArray: - array_val = parent_ptr->data.x_ptr.data.base_array.array_val; - abs_offset = parent_ptr->data.x_ptr.data.base_array.elem_index; - rel_end = bigint_as_usize(&len_val->data.x_bigint); - break; - case ConstPtrSpecialBaseStruct: - zig_panic("TODO slice const inner struct"); - case ConstPtrSpecialBaseErrorUnionCode: - zig_panic("TODO slice const inner error union code"); - case ConstPtrSpecialBaseErrorUnionPayload: - zig_panic("TODO slice const inner error union payload"); - case ConstPtrSpecialBaseOptionalPayload: - zig_panic("TODO slice const inner optional payload"); - case ConstPtrSpecialHardCodedAddr: - array_val = nullptr; - abs_offset = 0; - rel_end = bigint_as_usize(&len_val->data.x_bigint); - break; - case ConstPtrSpecialFunction: - zig_panic("TODO slice of slice cast from function"); - case ConstPtrSpecialNull: - zig_panic("TODO slice of null"); - } - } else { - zig_unreachable(); - } - - ZigValue *start_val = ir_resolve_const(ira, casted_start, UndefBad); - if (!start_val) - return ira->codegen->invalid_inst_gen; - - uint64_t start_scalar = bigint_as_u64(&start_val->data.x_bigint); - if (!ptr_is_undef && start_scalar > rel_end) { - ir_add_error(ira, &instruction->base.base, buf_sprintf("out of bounds slice")); - return ira->codegen->invalid_inst_gen; - } - - uint64_t end_scalar = rel_end; - if (end) { - ZigValue *end_val = ir_resolve_const(ira, end, UndefBad); - if (!end_val) - return ira->codegen->invalid_inst_gen; - end_scalar = bigint_as_u64(&end_val->data.x_bigint); - } - if (!ptr_is_undef) { - if (end_scalar > rel_end) { - ir_add_error(ira, &instruction->base.base, buf_sprintf("out of bounds slice")); - return ira->codegen->invalid_inst_gen; - } - if (start_scalar > end_scalar) { - ir_add_error(ira, &instruction->base.base, buf_sprintf("slice start is greater than end")); - return ira->codegen->invalid_inst_gen; - } - } - if (ptr_is_undef && start_scalar != end_scalar) { - ir_add_error(ira, &instruction->base.base, buf_sprintf("non-zero length slice of undefined pointer")); - return ira->codegen->invalid_inst_gen; - } - - // check sentinel when target is comptime-known - { - if (!sentinel_val) - goto exit_check_sentinel; - - switch (ptr_ptr->value->data.x_ptr.mut) { - case ConstPtrMutComptimeConst: - case ConstPtrMutComptimeVar: - break; - case ConstPtrMutRuntimeVar: - case ConstPtrMutInfer: - goto exit_check_sentinel; - } - - // prepare check parameters - ZigValue *target = const_ptr_pointee(ira, ira->codegen, ptr_ptr->value, instruction->base.base.source_node); - if (target == nullptr) - return ira->codegen->invalid_inst_gen; - - uint64_t target_len = 0; - ZigValue *target_sentinel = nullptr; - ZigValue *target_elements = nullptr; - - for (;;) { - if (target->type->id == ZigTypeIdArray) { - // handle `[N]T` - target_len = target->type->data.array.len; - target_sentinel = target->type->data.array.sentinel; - target_elements = target->data.x_array.data.s_none.elements; - break; - } else if (target->type->id == ZigTypeIdPointer && target->type->data.pointer.child_type->id == ZigTypeIdArray) { - // handle `*[N]T` - target = const_ptr_pointee(ira, ira->codegen, target, instruction->base.base.source_node); - if (target == nullptr) - return ira->codegen->invalid_inst_gen; - assert(target->type->id == ZigTypeIdArray); - continue; - } else if (target->type->id == ZigTypeIdPointer) { - // handle `[*]T` - // handle `[*c]T` - switch (target->data.x_ptr.special) { - case ConstPtrSpecialInvalid: - case ConstPtrSpecialDiscard: - zig_unreachable(); - case ConstPtrSpecialRef: - target = target->data.x_ptr.data.ref.pointee; - assert(target->type->id == ZigTypeIdArray); - continue; - case ConstPtrSpecialBaseArray: - case ConstPtrSpecialSubArray: - target = target->data.x_ptr.data.base_array.array_val; - assert(target->type->id == ZigTypeIdArray); - continue; - case ConstPtrSpecialBaseStruct: - zig_panic("TODO slice const inner struct"); - case ConstPtrSpecialBaseErrorUnionCode: - zig_panic("TODO slice const inner error union code"); - case ConstPtrSpecialBaseErrorUnionPayload: - zig_panic("TODO slice const inner error union payload"); - case ConstPtrSpecialBaseOptionalPayload: - zig_panic("TODO slice const inner optional payload"); - case ConstPtrSpecialHardCodedAddr: - // skip check - goto exit_check_sentinel; - case ConstPtrSpecialFunction: - zig_panic("TODO slice of ptr cast from function"); - case ConstPtrSpecialNull: - zig_panic("TODO slice of null ptr"); - } - break; - } else if (is_slice(target->type)) { - // handle `[]T` - target = target->data.x_struct.fields[slice_ptr_index]; - assert(target->type->id == ZigTypeIdPointer); - continue; - } - - zig_unreachable(); - } - - // perform check - if (target_sentinel == nullptr) { - if (end_scalar >= target_len) { - ir_add_error(ira, &instruction->base.base, buf_sprintf("slice-sentinel is out of bounds")); - return ira->codegen->invalid_inst_gen; - } - if (!const_values_equal(ira->codegen, sentinel_val, &target_elements[end_scalar])) { - ir_add_error(ira, &instruction->base.base, buf_sprintf("slice-sentinel does not match memory at target index")); - return ira->codegen->invalid_inst_gen; - } - } else { - assert(end_scalar <= target_len); - if (end_scalar == target_len) { - if (!const_values_equal(ira->codegen, sentinel_val, target_sentinel)) { - ir_add_error(ira, &instruction->base.base, buf_sprintf("slice-sentinel does not match target-sentinel")); - return ira->codegen->invalid_inst_gen; - } - } else { - if (!const_values_equal(ira->codegen, sentinel_val, &target_elements[end_scalar])) { - ir_add_error(ira, &instruction->base.base, buf_sprintf("slice-sentinel does not match memory at target index")); - return ira->codegen->invalid_inst_gen; - } - } - } - } - exit_check_sentinel: - - IrInstGen *result = ir_const(ira, &instruction->base.base, return_type); - - ZigValue *ptr_val; - if (return_type->id == ZigTypeIdPointer) { - // pointer to array - ptr_val = result->value; - } else { - // slice - result->value->data.x_struct.fields = alloc_const_vals_ptrs(ira->codegen, 2); - - ptr_val = result->value->data.x_struct.fields[slice_ptr_index]; - - ZigValue *len_val = result->value->data.x_struct.fields[slice_len_index]; - init_const_usize(ira->codegen, len_val, end_scalar - start_scalar); - } - - bool return_type_is_const = non_sentinel_slice_ptr_type->data.pointer.is_const; - if (array_val) { - size_t index = abs_offset + start_scalar; - init_const_ptr_array(ira->codegen, ptr_val, array_val, index, return_type_is_const, PtrLenUnknown); - if (return_type->id == ZigTypeIdPointer) { - ptr_val->data.x_ptr.special = ConstPtrSpecialSubArray; - } - if (array_type->id == ZigTypeIdArray) { - ptr_val->data.x_ptr.mut = ptr_ptr->value->data.x_ptr.mut; - } else if (is_slice(array_type)) { - ptr_val->data.x_ptr.mut = parent_ptr->data.x_ptr.mut; - } else if (array_type->id == ZigTypeIdPointer) { - ptr_val->data.x_ptr.mut = parent_ptr->data.x_ptr.mut; - } - } else if (ptr_is_undef) { - ptr_val->type = get_pointer_to_type(ira->codegen, parent_ptr->type->data.pointer.child_type, - return_type_is_const); - ptr_val->special = ConstValSpecialUndef; - } else switch (parent_ptr->data.x_ptr.special) { - case ConstPtrSpecialInvalid: - case ConstPtrSpecialDiscard: - zig_unreachable(); - case ConstPtrSpecialRef: - init_const_ptr_ref(ira->codegen, ptr_val, parent_ptr->data.x_ptr.data.ref.pointee, - return_type_is_const); - break; - case ConstPtrSpecialSubArray: - case ConstPtrSpecialBaseArray: - zig_unreachable(); - case ConstPtrSpecialBaseStruct: - zig_panic("TODO"); - case ConstPtrSpecialBaseErrorUnionCode: - zig_panic("TODO"); - case ConstPtrSpecialBaseErrorUnionPayload: - zig_panic("TODO"); - case ConstPtrSpecialBaseOptionalPayload: - zig_panic("TODO"); - case ConstPtrSpecialHardCodedAddr: - init_const_ptr_hard_coded_addr(ira->codegen, ptr_val, - parent_ptr->type->data.pointer.child_type, - parent_ptr->data.x_ptr.data.hard_coded_addr.addr + start_scalar, - return_type_is_const); - break; - case ConstPtrSpecialFunction: - zig_panic("TODO"); - case ConstPtrSpecialNull: - zig_panic("TODO"); - } - - // In the case of pointer-to-array, we must restore this because above it overwrites ptr_val->type - result->value->type = return_type; - return result; - } - - if (generate_non_null_assert) { - IrInstGen *ptr_val = ir_get_deref(ira, &instruction->base.base, ptr_ptr, nullptr); - - if (type_is_invalid(ptr_val->value->type)) - return ira->codegen->invalid_inst_gen; - - ir_build_assert_non_null(ira, &instruction->base.base, ptr_val); - } - - IrInstGen *result_loc = nullptr; - - if (return_type->id != ZigTypeIdPointer) { - result_loc = ir_resolve_result(ira, &instruction->base.base, instruction->result_loc, - return_type, nullptr, true, true); - if (result_loc != nullptr) { - if (type_is_invalid(result_loc->value->type) || result_loc->value->type->id == ZigTypeIdUnreachable) { - return result_loc; - } - - ir_assert(result_loc->value->type->id == ZigTypeIdPointer, &instruction->base.base); - if (result_loc->value->type->data.pointer.is_const) { - ir_add_error(ira, &instruction->base.base, buf_sprintf("cannot assign to constant")); - return ira->codegen->invalid_inst_gen; - } - - IrInstGen *dummy_value = ir_const(ira, &instruction->base.base, return_type); - dummy_value->value->special = ConstValSpecialRuntime; - IrInstGen *dummy_result = ir_implicit_cast2(ira, &instruction->base.base, - dummy_value, result_loc->value->type->data.pointer.child_type); - if (type_is_invalid(dummy_result->value->type)) - return ira->codegen->invalid_inst_gen; - } - } - - return ir_build_slice_gen(ira, &instruction->base.base, return_type, ptr_ptr, - casted_start, end, instruction->safety_check_on, result_loc, sentinel_val); -} - -static IrInstGen *ir_analyze_instruction_has_field(IrAnalyze *ira, IrInstSrcHasField *instruction) { - Error err; - ZigType *container_type = ir_resolve_type(ira, instruction->container_type->child); - if (type_is_invalid(container_type)) - return ira->codegen->invalid_inst_gen; - - if ((err = type_resolve(ira->codegen, container_type, ResolveStatusZeroBitsKnown))) - return ira->codegen->invalid_inst_gen; - - Buf *field_name = ir_resolve_str(ira, instruction->field_name->child); - if (field_name == nullptr) - return ira->codegen->invalid_inst_gen; - - bool result; - if (container_type->id == ZigTypeIdStruct) { - result = find_struct_type_field(container_type, field_name) != nullptr; - } else if (container_type->id == ZigTypeIdEnum) { - result = find_enum_type_field(container_type, field_name) != nullptr; - } else if (container_type->id == ZigTypeIdUnion) { - result = find_union_type_field(container_type, field_name) != nullptr; - } else { - ir_add_error(ira, &instruction->container_type->base, - buf_sprintf("type '%s' does not support @hasField", buf_ptr(&container_type->name))); - return ira->codegen->invalid_inst_gen; - } - return ir_const_bool(ira, &instruction->base.base, result); -} - -static IrInstGen *ir_analyze_instruction_wasm_memory_size(IrAnalyze *ira, IrInstSrcWasmMemorySize *instruction) { - // TODO generate compile error for target_arch different than 32bit - if (!target_is_wasm(ira->codegen->zig_target)) { - ir_add_error_node(ira, instruction->base.base.source_node, - buf_sprintf("@wasmMemorySize is a wasm32 feature only")); - return ira->codegen->invalid_inst_gen; - } - - IrInstGen *index = instruction->index->child; - if (type_is_invalid(index->value->type)) - return ira->codegen->invalid_inst_gen; - - ZigType *u32 = ira->codegen->builtin_types.entry_u32; - - IrInstGen *casted_index = ir_implicit_cast(ira, index, u32); - if (type_is_invalid(casted_index->value->type)) - return ira->codegen->invalid_inst_gen; - - return ir_build_wasm_memory_size_gen(ira, &instruction->base.base, casted_index); -} - -static IrInstGen *ir_analyze_instruction_wasm_memory_grow(IrAnalyze *ira, IrInstSrcWasmMemoryGrow *instruction) { - // TODO generate compile error for target_arch different than 32bit - if (!target_is_wasm(ira->codegen->zig_target)) { - ir_add_error_node(ira, instruction->base.base.source_node, - buf_sprintf("@wasmMemoryGrow is a wasm32 feature only")); - return ira->codegen->invalid_inst_gen; - } - - IrInstGen *index = instruction->index->child; - if (type_is_invalid(index->value->type)) - return ira->codegen->invalid_inst_gen; - - ZigType *u32 = ira->codegen->builtin_types.entry_u32; - - IrInstGen *casted_index = ir_implicit_cast(ira, index, u32); - if (type_is_invalid(casted_index->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *delta = instruction->delta->child; - if (type_is_invalid(delta->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *casted_delta = ir_implicit_cast(ira, delta, u32); - if (type_is_invalid(casted_delta->value->type)) - return ira->codegen->invalid_inst_gen; - - return ir_build_wasm_memory_grow_gen(ira, &instruction->base.base, casted_index, casted_delta); -} - -static IrInstGen *ir_analyze_instruction_breakpoint(IrAnalyze *ira, IrInstSrcBreakpoint *instruction) { - return ir_build_breakpoint_gen(ira, &instruction->base.base); -} - -static IrInstGen *ir_analyze_instruction_return_address(IrAnalyze *ira, IrInstSrcReturnAddress *instruction) { - return ir_build_return_address_gen(ira, &instruction->base.base); -} - -static IrInstGen *ir_analyze_instruction_frame_address(IrAnalyze *ira, IrInstSrcFrameAddress *instruction) { - return ir_build_frame_address_gen(ira, &instruction->base.base); -} - -static IrInstGen *ir_analyze_instruction_frame_handle(IrAnalyze *ira, IrInstSrcFrameHandle *instruction) { - ZigFn *fn = ira->new_irb.exec->fn_entry; - ir_assert(fn != nullptr, &instruction->base.base); - - if (fn->inferred_async_node == nullptr) { - fn->inferred_async_node = instruction->base.base.source_node; - } - - ZigType *frame_type = get_fn_frame_type(ira->codegen, fn); - ZigType *ptr_frame_type = get_pointer_to_type(ira->codegen, frame_type, false); - - return ir_build_handle_gen(ira, &instruction->base.base, ptr_frame_type); -} - -static IrInstGen *ir_analyze_instruction_frame_type(IrAnalyze *ira, IrInstSrcFrameType *instruction) { - ZigFn *fn = ir_resolve_fn(ira, instruction->fn->child); - if (fn == nullptr) - return ira->codegen->invalid_inst_gen; - - if (fn->type_entry->data.fn.is_generic) { - ir_add_error(ira, &instruction->base.base, - buf_sprintf("@Frame() of generic function")); - return ira->codegen->invalid_inst_gen; - } - - ZigType *ty = get_fn_frame_type(ira->codegen, fn); - return ir_const_type(ira, &instruction->base.base, ty); -} - -static IrInstGen *ir_analyze_instruction_frame_size(IrAnalyze *ira, IrInstSrcFrameSize *instruction) { - IrInstGen *fn = instruction->fn->child; - if (type_is_invalid(fn->value->type)) - return ira->codegen->invalid_inst_gen; - - if (fn->value->type->id != ZigTypeIdFn) { - ir_add_error(ira, &fn->base, - buf_sprintf("expected function, found '%s'", buf_ptr(&fn->value->type->name))); - return ira->codegen->invalid_inst_gen; - } - - ira->codegen->need_frame_size_prefix_data = true; - - return ir_build_frame_size_gen(ira, &instruction->base.base, fn); -} - -static IrInstGen *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstSrcAlignOf *instruction) { - // Here we create a lazy value in order to avoid resolving the alignment of the type - // immediately. This avoids false positive dependency loops such as: - // const Node = struct { - // field: []align(@alignOf(Node)) Node, - // }; - IrInstGen *result = ir_const(ira, &instruction->base.base, ira->codegen->builtin_types.entry_num_lit_int); - result->value->special = ConstValSpecialLazy; - - LazyValueAlignOf *lazy_align_of = heap::c_allocator.create(); - lazy_align_of->ira = ira; ira_ref(ira); - result->value->data.x_lazy = &lazy_align_of->base; - lazy_align_of->base.id = LazyValueIdAlignOf; - - lazy_align_of->target_type = instruction->type_value->child; - if (ir_resolve_type_lazy(ira, lazy_align_of->target_type) == nullptr) - return ira->codegen->invalid_inst_gen; - - return result; -} - -static IrInstGen *ir_analyze_instruction_overflow_op(IrAnalyze *ira, IrInstSrcOverflowOp *instruction) { - Error err; - - IrInstGen *type_value = instruction->type_value->child; - if (type_is_invalid(type_value->value->type)) - return ira->codegen->invalid_inst_gen; - - ZigType *dest_type = ir_resolve_type(ira, type_value); - if (type_is_invalid(dest_type)) - return ira->codegen->invalid_inst_gen; - - if (dest_type->id != ZigTypeIdInt) { - ir_add_error(ira, &type_value->base, - buf_sprintf("expected integer type, found '%s'", buf_ptr(&dest_type->name))); - return ira->codegen->invalid_inst_gen; - } - - IrInstGen *op1 = instruction->op1->child; - if (type_is_invalid(op1->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *casted_op1 = ir_implicit_cast(ira, op1, dest_type); - if (type_is_invalid(casted_op1->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *op2 = instruction->op2->child; - if (type_is_invalid(op2->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *casted_op2; - if (instruction->op == IrOverflowOpShl) { - ZigType *shift_amt_type = get_smallest_unsigned_int_type(ira->codegen, - dest_type->data.integral.bit_count - 1); - casted_op2 = ir_implicit_cast(ira, op2, shift_amt_type); - } else { - casted_op2 = ir_implicit_cast(ira, op2, dest_type); - } - if (type_is_invalid(casted_op2->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *result_ptr = instruction->result_ptr->child; - if (type_is_invalid(result_ptr->value->type)) - return ira->codegen->invalid_inst_gen; - - ZigType *expected_ptr_type; - if (result_ptr->value->type->id == ZigTypeIdPointer) { - uint32_t alignment; - if ((err = resolve_ptr_align(ira, result_ptr->value->type, &alignment))) - return ira->codegen->invalid_inst_gen; - expected_ptr_type = get_pointer_to_type_extra(ira->codegen, dest_type, - false, result_ptr->value->type->data.pointer.is_volatile, - PtrLenSingle, - alignment, 0, 0, false); - } else { - expected_ptr_type = get_pointer_to_type(ira->codegen, dest_type, false); - } - - IrInstGen *casted_result_ptr = ir_implicit_cast(ira, result_ptr, expected_ptr_type); - if (type_is_invalid(casted_result_ptr->value->type)) - return ira->codegen->invalid_inst_gen; - - if (instr_is_comptime(casted_op1) && - instr_is_comptime(casted_op2) && - instr_is_comptime(casted_result_ptr)) - { - ZigValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad); - if (op1_val == nullptr) - return ira->codegen->invalid_inst_gen; - - ZigValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); - if (op2_val == nullptr) - return ira->codegen->invalid_inst_gen; - - ZigValue *result_val = ir_resolve_const(ira, casted_result_ptr, UndefBad); - if (result_val == nullptr) - return ira->codegen->invalid_inst_gen; - - BigInt *op1_bigint = &op1_val->data.x_bigint; - BigInt *op2_bigint = &op2_val->data.x_bigint; - ZigValue *pointee_val = const_ptr_pointee(ira, ira->codegen, result_val, - casted_result_ptr->base.source_node); - if (pointee_val == nullptr) - return ira->codegen->invalid_inst_gen; - BigInt *dest_bigint = &pointee_val->data.x_bigint; - switch (instruction->op) { - case IrOverflowOpAdd: - bigint_add(dest_bigint, op1_bigint, op2_bigint); - break; - case IrOverflowOpSub: - bigint_sub(dest_bigint, op1_bigint, op2_bigint); - break; - case IrOverflowOpMul: - bigint_mul(dest_bigint, op1_bigint, op2_bigint); - break; - case IrOverflowOpShl: - bigint_shl(dest_bigint, op1_bigint, op2_bigint); - break; - } - bool result_bool = false; - if (!bigint_fits_in_bits(dest_bigint, dest_type->data.integral.bit_count, - dest_type->data.integral.is_signed)) - { - result_bool = true; - BigInt tmp_bigint; - bigint_init_bigint(&tmp_bigint, dest_bigint); - bigint_truncate(dest_bigint, &tmp_bigint, dest_type->data.integral.bit_count, - dest_type->data.integral.is_signed); - } - pointee_val->special = ConstValSpecialStatic; - return ir_const_bool(ira, &instruction->base.base, result_bool); - } - - return ir_build_overflow_op_gen(ira, &instruction->base.base, instruction->op, - casted_op1, casted_op2, casted_result_ptr, dest_type); -} - -static void ir_eval_mul_add(IrAnalyze *ira, IrInstSrcMulAdd *source_instr, ZigType *float_type, - ZigValue *op1, ZigValue *op2, ZigValue *op3, ZigValue *out_val) { - if (float_type->id == ZigTypeIdComptimeFloat) { - f128M_mulAdd(&out_val->data.x_bigfloat.value, &op1->data.x_bigfloat.value, &op2->data.x_bigfloat.value, - &op3->data.x_bigfloat.value); - } else if (float_type->id == ZigTypeIdFloat) { - switch (float_type->data.floating.bit_count) { - case 16: - out_val->data.x_f16 = f16_mulAdd(op1->data.x_f16, op2->data.x_f16, op3->data.x_f16); - break; - case 32: - out_val->data.x_f32 = fmaf(op1->data.x_f32, op2->data.x_f32, op3->data.x_f32); - break; - case 64: - out_val->data.x_f64 = fma(op1->data.x_f64, op2->data.x_f64, op3->data.x_f64); - break; - case 128: - f128M_mulAdd(&op1->data.x_f128, &op2->data.x_f128, &op3->data.x_f128, &out_val->data.x_f128); - break; - default: - zig_unreachable(); - } - } else { - zig_unreachable(); - } -} - -static IrInstGen *ir_analyze_instruction_mul_add(IrAnalyze *ira, IrInstSrcMulAdd *instruction) { - IrInstGen *type_value = instruction->type_value->child; - if (type_is_invalid(type_value->value->type)) - return ira->codegen->invalid_inst_gen; - - ZigType *expr_type = ir_resolve_type(ira, type_value); - if (type_is_invalid(expr_type)) - return ira->codegen->invalid_inst_gen; - - // Only allow float types, and vectors of floats. - ZigType *float_type = (expr_type->id == ZigTypeIdVector) ? expr_type->data.vector.elem_type : expr_type; - if (float_type->id != ZigTypeIdFloat) { - ir_add_error(ira, &type_value->base, - buf_sprintf("expected float or vector of float type, found '%s'", buf_ptr(&float_type->name))); - return ira->codegen->invalid_inst_gen; - } - - IrInstGen *op1 = instruction->op1->child; - if (type_is_invalid(op1->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *casted_op1 = ir_implicit_cast(ira, op1, expr_type); - if (type_is_invalid(casted_op1->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *op2 = instruction->op2->child; - if (type_is_invalid(op2->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *casted_op2 = ir_implicit_cast(ira, op2, expr_type); - if (type_is_invalid(casted_op2->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *op3 = instruction->op3->child; - if (type_is_invalid(op3->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *casted_op3 = ir_implicit_cast(ira, op3, expr_type); - if (type_is_invalid(casted_op3->value->type)) - return ira->codegen->invalid_inst_gen; - - if (instr_is_comptime(casted_op1) && - instr_is_comptime(casted_op2) && - instr_is_comptime(casted_op3)) { - ZigValue *op1_const = ir_resolve_const(ira, casted_op1, UndefBad); - if (!op1_const) - return ira->codegen->invalid_inst_gen; - ZigValue *op2_const = ir_resolve_const(ira, casted_op2, UndefBad); - if (!op2_const) - return ira->codegen->invalid_inst_gen; - ZigValue *op3_const = ir_resolve_const(ira, casted_op3, UndefBad); - if (!op3_const) - return ira->codegen->invalid_inst_gen; - - IrInstGen *result = ir_const(ira, &instruction->base.base, expr_type); - ZigValue *out_val = result->value; - - if (expr_type->id == ZigTypeIdVector) { - expand_undef_array(ira->codegen, op1_const); - expand_undef_array(ira->codegen, op2_const); - expand_undef_array(ira->codegen, op3_const); - out_val->special = ConstValSpecialUndef; - expand_undef_array(ira->codegen, out_val); - size_t len = expr_type->data.vector.len; - for (size_t i = 0; i < len; i += 1) { - ZigValue *float_operand_op1 = &op1_const->data.x_array.data.s_none.elements[i]; - ZigValue *float_operand_op2 = &op2_const->data.x_array.data.s_none.elements[i]; - ZigValue *float_operand_op3 = &op3_const->data.x_array.data.s_none.elements[i]; - ZigValue *float_out_val = &out_val->data.x_array.data.s_none.elements[i]; - assert(float_operand_op1->type == float_type); - assert(float_operand_op2->type == float_type); - assert(float_operand_op3->type == float_type); - assert(float_out_val->type == float_type); - ir_eval_mul_add(ira, instruction, float_type, - op1_const, op2_const, op3_const, float_out_val); - float_out_val->type = float_type; - } - out_val->type = expr_type; - out_val->special = ConstValSpecialStatic; - } else { - ir_eval_mul_add(ira, instruction, float_type, op1_const, op2_const, op3_const, out_val); - } - return result; - } - - return ir_build_mul_add_gen(ira, &instruction->base.base, casted_op1, casted_op2, casted_op3, expr_type); -} - -static IrInstGen *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstSrcTestErr *instruction) { - IrInstGen *base_ptr = instruction->base_ptr->child; - if (type_is_invalid(base_ptr->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *value; - if (instruction->base_ptr_is_payload) { - value = base_ptr; - } else { - value = ir_get_deref(ira, &instruction->base.base, base_ptr, nullptr); - } - - ZigType *type_entry = value->value->type; - if (type_is_invalid(type_entry)) - return ira->codegen->invalid_inst_gen; - if (type_entry->id == ZigTypeIdErrorUnion) { - if (instr_is_comptime(value)) { - ZigValue *err_union_val = ir_resolve_const(ira, value, UndefBad); - if (!err_union_val) - return ira->codegen->invalid_inst_gen; - - if (err_union_val->special != ConstValSpecialRuntime) { - ErrorTableEntry *err = err_union_val->data.x_err_union.error_set->data.x_err_set; - return ir_const_bool(ira, &instruction->base.base, (err != nullptr)); - } - } - - if (instruction->resolve_err_set) { - ZigType *err_set_type = type_entry->data.error_union.err_set_type; - if (!resolve_inferred_error_set(ira->codegen, err_set_type, instruction->base.base.source_node)) { - return ira->codegen->invalid_inst_gen; - } - if (!type_is_global_error_set(err_set_type) && - err_set_type->data.error_set.err_count == 0) - { - assert(!err_set_type->data.error_set.incomplete); - return ir_const_bool(ira, &instruction->base.base, false); - } - } - - return ir_build_test_err_gen(ira, &instruction->base.base, value); - } else if (type_entry->id == ZigTypeIdErrorSet) { - return ir_const_bool(ira, &instruction->base.base, true); - } else { - return ir_const_bool(ira, &instruction->base.base, false); - } -} - -static IrInstGen *ir_analyze_unwrap_err_code(IrAnalyze *ira, IrInst* source_instr, - IrInstGen *base_ptr, bool initializing) -{ - ZigType *ptr_type = base_ptr->value->type; - - // This will be a pointer type because unwrap err payload IR instruction operates on a pointer to a thing. - assert(ptr_type->id == ZigTypeIdPointer); - - ZigType *type_entry = ptr_type->data.pointer.child_type; - if (type_is_invalid(type_entry)) - return ira->codegen->invalid_inst_gen; - - if (type_entry->id != ZigTypeIdErrorUnion) { - ir_add_error(ira, &base_ptr->base, - buf_sprintf("expected error union type, found '%s'", buf_ptr(&type_entry->name))); - return ira->codegen->invalid_inst_gen; - } - - ZigType *err_set_type = type_entry->data.error_union.err_set_type; - ZigType *result_type = get_pointer_to_type_extra(ira->codegen, err_set_type, - ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, PtrLenSingle, - ptr_type->data.pointer.explicit_alignment, 0, 0, false); - - if (instr_is_comptime(base_ptr)) { - ZigValue *ptr_val = ir_resolve_const(ira, base_ptr, UndefBad); - if (!ptr_val) - return ira->codegen->invalid_inst_gen; - if (ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar && - ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) - { - ZigValue *err_union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node); - if (err_union_val == nullptr) - return ira->codegen->invalid_inst_gen; - - if (initializing && err_union_val->special == ConstValSpecialUndef) { - ZigValue *vals = ira->codegen->pass1_arena->allocate(2); - ZigValue *err_set_val = &vals[0]; - ZigValue *payload_val = &vals[1]; - - err_set_val->special = ConstValSpecialUndef; - err_set_val->type = err_set_type; - err_set_val->parent.id = ConstParentIdErrUnionCode; - err_set_val->parent.data.p_err_union_code.err_union_val = err_union_val; - - payload_val->special = ConstValSpecialUndef; - payload_val->type = type_entry->data.error_union.payload_type; - payload_val->parent.id = ConstParentIdErrUnionPayload; - payload_val->parent.data.p_err_union_payload.err_union_val = err_union_val; - - err_union_val->special = ConstValSpecialStatic; - err_union_val->data.x_err_union.error_set = err_set_val; - err_union_val->data.x_err_union.payload = payload_val; - } - ir_assert(err_union_val->special != ConstValSpecialRuntime, source_instr); - - IrInstGen *result; - if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { - result = ir_build_unwrap_err_code_gen(ira, source_instr->scope, - source_instr->source_node, base_ptr, result_type); - result->value->special = ConstValSpecialStatic; - } else { - result = ir_const(ira, source_instr, result_type); - } - ZigValue *const_val = result->value; - const_val->data.x_ptr.special = ConstPtrSpecialBaseErrorUnionCode; - const_val->data.x_ptr.data.base_err_union_code.err_union_val = err_union_val; - const_val->data.x_ptr.mut = ptr_val->data.x_ptr.mut; - return result; - } - } - - return ir_build_unwrap_err_code_gen(ira, source_instr->scope, source_instr->source_node, base_ptr, result_type); -} - -static IrInstGen *ir_analyze_instruction_unwrap_err_code(IrAnalyze *ira, IrInstSrcUnwrapErrCode *instruction) { - IrInstGen *base_ptr = instruction->err_union_ptr->child; - if (type_is_invalid(base_ptr->value->type)) - return ira->codegen->invalid_inst_gen; - return ir_analyze_unwrap_err_code(ira, &instruction->base.base, base_ptr, false); -} - -static IrInstGen *ir_analyze_unwrap_error_payload(IrAnalyze *ira, IrInst* source_instr, - IrInstGen *base_ptr, bool safety_check_on, bool initializing) -{ - ZigType *ptr_type = base_ptr->value->type; - - // This will be a pointer type because unwrap err payload IR instruction operates on a pointer to a thing. - assert(ptr_type->id == ZigTypeIdPointer); - - ZigType *type_entry = ptr_type->data.pointer.child_type; - if (type_is_invalid(type_entry)) - return ira->codegen->invalid_inst_gen; - - if (type_entry->id != ZigTypeIdErrorUnion) { - ir_add_error(ira, &base_ptr->base, - buf_sprintf("expected error union type, found '%s'", buf_ptr(&type_entry->name))); - return ira->codegen->invalid_inst_gen; - } - - ZigType *payload_type = type_entry->data.error_union.payload_type; - if (type_is_invalid(payload_type)) - return ira->codegen->invalid_inst_gen; - - ZigType *result_type = get_pointer_to_type_extra(ira->codegen, payload_type, - ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, - PtrLenSingle, 0, 0, 0, false); - - if (instr_is_comptime(base_ptr)) { - ZigValue *ptr_val = ir_resolve_const(ira, base_ptr, UndefBad); - if (!ptr_val) - return ira->codegen->invalid_inst_gen; - if (ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar) { - ZigValue *err_union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node); - if (err_union_val == nullptr) - return ira->codegen->invalid_inst_gen; - if (initializing && err_union_val->special == ConstValSpecialUndef) { - ZigValue *vals = ira->codegen->pass1_arena->allocate(2); - ZigValue *err_set_val = &vals[0]; - ZigValue *payload_val = &vals[1]; - - err_set_val->special = ConstValSpecialStatic; - err_set_val->type = type_entry->data.error_union.err_set_type; - err_set_val->data.x_err_set = nullptr; - - payload_val->special = ConstValSpecialUndef; - payload_val->type = payload_type; - - err_union_val->special = ConstValSpecialStatic; - err_union_val->data.x_err_union.error_set = err_set_val; - err_union_val->data.x_err_union.payload = payload_val; - } - - if (err_union_val->special != ConstValSpecialRuntime) { - ErrorTableEntry *err = err_union_val->data.x_err_union.error_set->data.x_err_set; - if (err != nullptr) { - ir_add_error(ira, source_instr, - buf_sprintf("caught unexpected error '%s'", buf_ptr(&err->name))); - return ira->codegen->invalid_inst_gen; - } - - IrInstGen *result; - if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { - result = ir_build_unwrap_err_payload_gen(ira, source_instr->scope, - source_instr->source_node, base_ptr, safety_check_on, initializing, result_type); - result->value->special = ConstValSpecialStatic; - } else { - result = ir_const(ira, source_instr, result_type); - } - result->value->data.x_ptr.special = ConstPtrSpecialRef; - result->value->data.x_ptr.data.ref.pointee = err_union_val->data.x_err_union.payload; - result->value->data.x_ptr.mut = ptr_val->data.x_ptr.mut; - return result; - } - } - } - - return ir_build_unwrap_err_payload_gen(ira, source_instr->scope, source_instr->source_node, - base_ptr, safety_check_on, initializing, result_type); -} - -static IrInstGen *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira, - IrInstSrcUnwrapErrPayload *instruction) -{ - assert(instruction->value->child); - IrInstGen *value = instruction->value->child; - if (type_is_invalid(value->value->type)) - return ira->codegen->invalid_inst_gen; - - return ir_analyze_unwrap_error_payload(ira, &instruction->base.base, value, instruction->safety_check_on, false); -} - -static IrInstGen *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstSrcFnProto *instruction) { - AstNode *proto_node = instruction->base.base.source_node; - assert(proto_node->type == NodeTypeFnProto); - - IrInstGen *result = ir_const(ira, &instruction->base.base, ira->codegen->builtin_types.entry_type); - result->value->special = ConstValSpecialLazy; - - LazyValueFnType *lazy_fn_type = heap::c_allocator.create(); - lazy_fn_type->ira = ira; ira_ref(ira); - result->value->data.x_lazy = &lazy_fn_type->base; - lazy_fn_type->base.id = LazyValueIdFnType; - - if (proto_node->data.fn_proto.auto_err_set) { - ir_add_error(ira, &instruction->base.base, - buf_sprintf("inferring error set of return type valid only for function definitions")); - return ira->codegen->invalid_inst_gen; - } - - lazy_fn_type->cc = cc_from_fn_proto(&proto_node->data.fn_proto); - if (instruction->callconv_value != nullptr) { - ZigType *cc_enum_type = get_builtin_type(ira->codegen, "CallingConvention"); - - IrInstGen *casted_value = ir_implicit_cast(ira, instruction->callconv_value->child, cc_enum_type); - if (type_is_invalid(casted_value->value->type)) - return ira->codegen->invalid_inst_gen; - - ZigValue *const_value = ir_resolve_const(ira, casted_value, UndefBad); - if (const_value == nullptr) - return ira->codegen->invalid_inst_gen; - - lazy_fn_type->cc = (CallingConvention)bigint_as_u32(&const_value->data.x_enum_tag); - } - - size_t param_count = proto_node->data.fn_proto.params.length; - lazy_fn_type->proto_node = proto_node; - lazy_fn_type->param_types = heap::c_allocator.allocate(param_count); - - for (size_t param_index = 0; param_index < param_count; param_index += 1) { - AstNode *param_node = proto_node->data.fn_proto.params.at(param_index); - assert(param_node->type == NodeTypeParamDecl); - - bool param_is_var_args = param_node->data.param_decl.is_var_args; - if (param_is_var_args) { - const CallingConvention cc = lazy_fn_type->cc; - - if (cc == CallingConventionC) { - break; - } else { - ir_add_error(ira, &instruction->base.base, - buf_sprintf("var args only allowed in functions with C calling convention")); - return ira->codegen->invalid_inst_gen; - } - } - - if (instruction->param_types[param_index] == nullptr) { - lazy_fn_type->is_generic = true; - return result; - } - - IrInstGen *param_type_value = instruction->param_types[param_index]->child; - if (type_is_invalid(param_type_value->value->type)) - return ira->codegen->invalid_inst_gen; - if (ir_resolve_const(ira, param_type_value, LazyOk) == nullptr) - return ira->codegen->invalid_inst_gen; - lazy_fn_type->param_types[param_index] = param_type_value; - } - - if (instruction->align_value != nullptr) { - lazy_fn_type->align_inst = instruction->align_value->child; - if (ir_resolve_const(ira, lazy_fn_type->align_inst, LazyOk) == nullptr) - return ira->codegen->invalid_inst_gen; - } - - lazy_fn_type->return_type = instruction->return_type->child; - if (ir_resolve_const(ira, lazy_fn_type->return_type, LazyOk) == nullptr) - return ira->codegen->invalid_inst_gen; - - return result; -} - -static IrInstGen *ir_analyze_instruction_test_comptime(IrAnalyze *ira, IrInstSrcTestComptime *instruction) { - IrInstGen *value = instruction->value->child; - if (type_is_invalid(value->value->type)) - return ira->codegen->invalid_inst_gen; - - return ir_const_bool(ira, &instruction->base.base, instr_is_comptime(value)); -} - -static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira, - IrInstSrcCheckSwitchProngs *instruction) -{ - IrInstGen *target_value = instruction->target_value->child; - ZigType *switch_type = target_value->value->type; - if (type_is_invalid(switch_type)) - return ira->codegen->invalid_inst_gen; - - ZigValue *original_value = ((IrInstSrcSwitchTarget *)(instruction->target_value))->target_value_ptr->child->value; - bool target_is_originally_union = original_value->type->id == ZigTypeIdPointer && - original_value->type->data.pointer.child_type->id == ZigTypeIdUnion; - - if (switch_type->id == ZigTypeIdEnum) { - HashMap field_prev_uses = {}; - field_prev_uses.init(switch_type->data.enumeration.src_field_count); - - for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) { - IrInstSrcCheckSwitchProngsRange *range = &instruction->ranges[range_i]; - - IrInstGen *start_value_uncasted = range->start->child; - if (type_is_invalid(start_value_uncasted->value->type)) - return ira->codegen->invalid_inst_gen; - IrInstGen *start_value = ir_implicit_cast(ira, start_value_uncasted, switch_type); - if (type_is_invalid(start_value->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *end_value_uncasted = range->end->child; - if (type_is_invalid(end_value_uncasted->value->type)) - return ira->codegen->invalid_inst_gen; - IrInstGen *end_value = ir_implicit_cast(ira, end_value_uncasted, switch_type); - if (type_is_invalid(end_value->value->type)) - return ira->codegen->invalid_inst_gen; - - assert(start_value->value->type->id == ZigTypeIdEnum); - BigInt start_index; - bigint_init_bigint(&start_index, &start_value->value->data.x_enum_tag); - - assert(end_value->value->type->id == ZigTypeIdEnum); - BigInt end_index; - bigint_init_bigint(&end_index, &end_value->value->data.x_enum_tag); - - if (bigint_cmp(&start_index, &end_index) == CmpGT) { - ir_add_error(ira, &start_value->base, - buf_sprintf("range start value is greater than the end value")); - } - - BigInt field_index; - bigint_init_bigint(&field_index, &start_index); - for (;;) { - Cmp cmp = bigint_cmp(&field_index, &end_index); - if (cmp == CmpGT) { - break; - } - auto entry = field_prev_uses.put_unique(field_index, start_value->base.source_node); - if (entry) { - AstNode *prev_node = entry->value; - TypeEnumField *enum_field = find_enum_field_by_tag(switch_type, &field_index); - assert(enum_field != nullptr); - ErrorMsg *msg = ir_add_error(ira, &start_value->base, - buf_sprintf("duplicate switch value: '%s.%s'", buf_ptr(&switch_type->name), - buf_ptr(enum_field->name))); - add_error_note(ira->codegen, msg, prev_node, buf_sprintf("other value is here")); - } - bigint_incr(&field_index); - } - } - if (instruction->have_underscore_prong) { - if (!switch_type->data.enumeration.non_exhaustive) { - ir_add_error(ira, &instruction->base.base, - buf_sprintf("switch on exhaustive enum has `_` prong")); - } else if (target_is_originally_union) { - ir_add_error(ira, &instruction->base.base, - buf_sprintf("`_` prong not allowed when switching on tagged union")); - } - for (uint32_t i = 0; i < switch_type->data.enumeration.src_field_count; i += 1) { - TypeEnumField *enum_field = &switch_type->data.enumeration.fields[i]; - if (buf_eql_str(enum_field->name, "_")) - continue; - - auto entry = field_prev_uses.maybe_get(enum_field->value); - if (!entry) { - ir_add_error(ira, &instruction->base.base, - buf_sprintf("enumeration value '%s.%s' not handled in switch", buf_ptr(&switch_type->name), - buf_ptr(enum_field->name))); - } - } - } else if (instruction->else_prong == nullptr) { - if (switch_type->data.enumeration.non_exhaustive && !target_is_originally_union) { - ir_add_error(ira, &instruction->base.base, - buf_sprintf("switch on non-exhaustive enum must include `else` or `_` prong")); - } - for (uint32_t i = 0; i < switch_type->data.enumeration.src_field_count; i += 1) { - TypeEnumField *enum_field = &switch_type->data.enumeration.fields[i]; - - auto entry = field_prev_uses.maybe_get(enum_field->value); - if (!entry) { - ir_add_error(ira, &instruction->base.base, - buf_sprintf("enumeration value '%s.%s' not handled in switch", buf_ptr(&switch_type->name), - buf_ptr(enum_field->name))); - } - } - } else if(!switch_type->data.enumeration.non_exhaustive && switch_type->data.enumeration.src_field_count == instruction->range_count) { - ir_add_error_node(ira, instruction->else_prong, - buf_sprintf("unreachable else prong, all cases already handled")); - return ira->codegen->invalid_inst_gen; - } - } else if (switch_type->id == ZigTypeIdErrorSet) { - if (!resolve_inferred_error_set(ira->codegen, switch_type, target_value->base.source_node)) { - return ira->codegen->invalid_inst_gen; - } - - size_t field_prev_uses_count = ira->codegen->errors_by_index.length; - AstNode **field_prev_uses = heap::c_allocator.allocate(field_prev_uses_count); - - for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) { - IrInstSrcCheckSwitchProngsRange *range = &instruction->ranges[range_i]; - - IrInstGen *start_value_uncasted = range->start->child; - if (type_is_invalid(start_value_uncasted->value->type)) - return ira->codegen->invalid_inst_gen; - IrInstGen *start_value = ir_implicit_cast(ira, start_value_uncasted, switch_type); - if (type_is_invalid(start_value->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *end_value_uncasted = range->end->child; - if (type_is_invalid(end_value_uncasted->value->type)) - return ira->codegen->invalid_inst_gen; - IrInstGen *end_value = ir_implicit_cast(ira, end_value_uncasted, switch_type); - if (type_is_invalid(end_value->value->type)) - return ira->codegen->invalid_inst_gen; - - ir_assert(start_value->value->type->id == ZigTypeIdErrorSet, &instruction->base.base); - uint32_t start_index = start_value->value->data.x_err_set->value; - - ir_assert(end_value->value->type->id == ZigTypeIdErrorSet, &instruction->base.base); - uint32_t end_index = end_value->value->data.x_err_set->value; - - if (start_index != end_index) { - ir_add_error(ira, &end_value->base, buf_sprintf("ranges not allowed when switching on errors")); - return ira->codegen->invalid_inst_gen; - } - - AstNode *prev_node = field_prev_uses[start_index]; - if (prev_node != nullptr) { - Buf *err_name = &ira->codegen->errors_by_index.at(start_index)->name; - ErrorMsg *msg = ir_add_error(ira, &start_value->base, - buf_sprintf("duplicate switch value: '%s.%s'", buf_ptr(&switch_type->name), buf_ptr(err_name))); - add_error_note(ira->codegen, msg, prev_node, buf_sprintf("other value is here")); - } - field_prev_uses[start_index] = start_value->base.source_node; - } - if (instruction->else_prong == nullptr) { - if (type_is_global_error_set(switch_type)) { - ir_add_error(ira, &instruction->base.base, - buf_sprintf("else prong required when switching on type 'anyerror'")); - return ira->codegen->invalid_inst_gen; - } else { - for (uint32_t i = 0; i < switch_type->data.error_set.err_count; i += 1) { - ErrorTableEntry *err_entry = switch_type->data.error_set.errors[i]; - - AstNode *prev_node = field_prev_uses[err_entry->value]; - if (prev_node == nullptr) { - ir_add_error(ira, &instruction->base.base, - buf_sprintf("error.%s not handled in switch", buf_ptr(&err_entry->name))); - } - } - } - } - - heap::c_allocator.deallocate(field_prev_uses, field_prev_uses_count); - } else if (switch_type->id == ZigTypeIdInt) { - RangeSet rs = {0}; - for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) { - IrInstSrcCheckSwitchProngsRange *range = &instruction->ranges[range_i]; - - IrInstGen *start_value = range->start->child; - if (type_is_invalid(start_value->value->type)) - return ira->codegen->invalid_inst_gen; - IrInstGen *casted_start_value = ir_implicit_cast(ira, start_value, switch_type); - if (type_is_invalid(casted_start_value->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *end_value = range->end->child; - if (type_is_invalid(end_value->value->type)) - return ira->codegen->invalid_inst_gen; - IrInstGen *casted_end_value = ir_implicit_cast(ira, end_value, switch_type); - if (type_is_invalid(casted_end_value->value->type)) - return ira->codegen->invalid_inst_gen; - - ZigValue *start_val = ir_resolve_const(ira, casted_start_value, UndefBad); - if (!start_val) - return ira->codegen->invalid_inst_gen; - - ZigValue *end_val = ir_resolve_const(ira, casted_end_value, UndefBad); - if (!end_val) - return ira->codegen->invalid_inst_gen; - - assert(start_val->type->id == ZigTypeIdInt || start_val->type->id == ZigTypeIdComptimeInt); - assert(end_val->type->id == ZigTypeIdInt || end_val->type->id == ZigTypeIdComptimeInt); - - if (bigint_cmp(&start_val->data.x_bigint, &end_val->data.x_bigint) == CmpGT) { - ir_add_error(ira, &start_value->base, - buf_sprintf("range start value is greater than the end value")); - } - - AstNode *prev_node = rangeset_add_range(&rs, &start_val->data.x_bigint, &end_val->data.x_bigint, - start_value->base.source_node); - if (prev_node != nullptr) { - ErrorMsg *msg = ir_add_error(ira, &start_value->base, buf_sprintf("duplicate switch value")); - add_error_note(ira->codegen, msg, prev_node, buf_sprintf("previous value is here")); - return ira->codegen->invalid_inst_gen; - } - } - - BigInt min_val; - eval_min_max_value_int(ira->codegen, switch_type, &min_val, false); - BigInt max_val; - eval_min_max_value_int(ira->codegen, switch_type, &max_val, true); - bool handles_all_cases = rangeset_spans(&rs, &min_val, &max_val); - if (!handles_all_cases && instruction->else_prong == nullptr) { - ir_add_error(ira, &instruction->base.base, buf_sprintf("switch must handle all possibilities")); - return ira->codegen->invalid_inst_gen; - } else if(handles_all_cases && instruction->else_prong != nullptr) { - ir_add_error_node(ira, instruction->else_prong, - buf_sprintf("unreachable else prong, all cases already handled")); - return ira->codegen->invalid_inst_gen; - } - } else if (switch_type->id == ZigTypeIdBool) { - int seenTrue = 0; - int seenFalse = 0; - for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) { - IrInstSrcCheckSwitchProngsRange *range = &instruction->ranges[range_i]; - - IrInstGen *value = range->start->child; - - IrInstGen *casted_value = ir_implicit_cast(ira, value, switch_type); - if (type_is_invalid(casted_value->value->type)) - return ira->codegen->invalid_inst_gen; - - ZigValue *const_expr_val = ir_resolve_const(ira, casted_value, UndefBad); - if (!const_expr_val) - return ira->codegen->invalid_inst_gen; - - assert(const_expr_val->type->id == ZigTypeIdBool); - - if (const_expr_val->data.x_bool == true) { - seenTrue += 1; - } else { - seenFalse += 1; - } - - if ((seenTrue > 1) || (seenFalse > 1)) { - ir_add_error(ira, &value->base, buf_sprintf("duplicate switch value")); - return ira->codegen->invalid_inst_gen; - } - } - if (((seenTrue < 1) || (seenFalse < 1)) && instruction->else_prong == nullptr) { - ir_add_error(ira, &instruction->base.base, buf_sprintf("switch must handle all possibilities")); - return ira->codegen->invalid_inst_gen; - } - - if(seenTrue == 1 && seenFalse == 1 && instruction->else_prong != nullptr) { - ir_add_error_node(ira, instruction->else_prong, - buf_sprintf("unreachable else prong, all cases already handled")); - return ira->codegen->invalid_inst_gen; - } - } else if (instruction->else_prong == nullptr) { - ir_add_error(ira, &instruction->base.base, - buf_sprintf("else prong required when switching on type '%s'", buf_ptr(&switch_type->name))); - return ira->codegen->invalid_inst_gen; - } else if(switch_type->id == ZigTypeIdMetaType) { - HashMap prevs; - // HashMap doubles capacity when reaching 60% capacity, - // because we know the size at init we can avoid reallocation by doubling it here - prevs.init(instruction->range_count * 2); - for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) { - IrInstSrcCheckSwitchProngsRange *range = &instruction->ranges[range_i]; - - IrInstGen *value = range->start->child; - IrInstGen *casted_value = ir_implicit_cast(ira, value, switch_type); - if (type_is_invalid(casted_value->value->type)) { - prevs.deinit(); - return ira->codegen->invalid_inst_gen; - } - - ZigValue *const_expr_val = ir_resolve_const(ira, casted_value, UndefBad); - if (!const_expr_val) { - prevs.deinit(); - return ira->codegen->invalid_inst_gen; - } - - auto entry = prevs.put_unique(const_expr_val->data.x_type, value); - if(entry != nullptr) { - ErrorMsg *msg = ir_add_error(ira, &value->base, buf_sprintf("duplicate switch value")); - add_error_note(ira->codegen, msg, entry->value->base.source_node, buf_sprintf("previous value is here")); - prevs.deinit(); - return ira->codegen->invalid_inst_gen; - } - } - prevs.deinit(); - } - return ir_const_void(ira, &instruction->base.base); -} - -static IrInstGen *ir_analyze_instruction_check_statement_is_void(IrAnalyze *ira, - IrInstSrcCheckStatementIsVoid *instruction) -{ - IrInstGen *statement_value = instruction->statement_value->child; - ZigType *statement_type = statement_value->value->type; - if (type_is_invalid(statement_type)) - return ira->codegen->invalid_inst_gen; - - if (statement_type->id != ZigTypeIdVoid && statement_type->id != ZigTypeIdUnreachable) { - ir_add_error(ira, &instruction->base.base, buf_sprintf("expression value is ignored")); - } - - return ir_const_void(ira, &instruction->base.base); -} - -static IrInstGen *ir_analyze_instruction_panic(IrAnalyze *ira, IrInstSrcPanic *instruction) { - IrInstGen *msg = instruction->msg->child; - if (type_is_invalid(msg->value->type)) - return ir_unreach_error(ira); - - if (ir_should_inline(ira->old_irb.exec, instruction->base.base.scope)) { - ir_add_error(ira, &instruction->base.base, buf_sprintf("encountered @panic at compile-time")); - return ir_unreach_error(ira); - } - - ZigType *u8_ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, - true, false, PtrLenUnknown, 0, 0, 0, false); - ZigType *str_type = get_slice_type(ira->codegen, u8_ptr_type); - IrInstGen *casted_msg = ir_implicit_cast(ira, msg, str_type); - if (type_is_invalid(casted_msg->value->type)) - return ir_unreach_error(ira); - - IrInstGen *new_instruction = ir_build_panic_gen(ira, &instruction->base.base, casted_msg); - return ir_finish_anal(ira, new_instruction); -} - -static IrInstGen *ir_align_cast(IrAnalyze *ira, IrInstGen *target, uint32_t align_bytes, bool safety_check_on) { - Error err; - - ZigType *target_type = target->value->type; - assert(!type_is_invalid(target_type)); - - ZigType *result_type; - uint32_t old_align_bytes; - - ZigType *actual_ptr = target_type; - if (actual_ptr->id == ZigTypeIdOptional) { - actual_ptr = actual_ptr->data.maybe.child_type; - } else if (is_slice(actual_ptr)) { - actual_ptr = actual_ptr->data.structure.fields[slice_ptr_index]->type_entry; - } - - if (safety_check_on && !type_has_bits(ira->codegen, actual_ptr)) { - ir_add_error(ira, &target->base, - buf_sprintf("cannot adjust alignment of zero sized type '%s'", buf_ptr(&target_type->name))); - return ira->codegen->invalid_inst_gen; - } - - if (target_type->id == ZigTypeIdPointer) { - result_type = adjust_ptr_align(ira->codegen, target_type, align_bytes); - if ((err = resolve_ptr_align(ira, target_type, &old_align_bytes))) - return ira->codegen->invalid_inst_gen; - } else if (target_type->id == ZigTypeIdFn) { - FnTypeId fn_type_id = target_type->data.fn.fn_type_id; - old_align_bytes = fn_type_id.alignment; - fn_type_id.alignment = align_bytes; - result_type = get_fn_type(ira->codegen, &fn_type_id); - } else if (target_type->id == ZigTypeIdAnyFrame) { - if (align_bytes >= target_fn_align(ira->codegen->zig_target)) { - result_type = target_type; - } else { - ir_add_error(ira, &target->base, buf_sprintf("sub-aligned anyframe not allowed")); - return ira->codegen->invalid_inst_gen; - } - } else if (target_type->id == ZigTypeIdOptional && - target_type->data.maybe.child_type->id == ZigTypeIdPointer) - { - ZigType *ptr_type = target_type->data.maybe.child_type; - if ((err = resolve_ptr_align(ira, ptr_type, &old_align_bytes))) - return ira->codegen->invalid_inst_gen; - ZigType *better_ptr_type = adjust_ptr_align(ira->codegen, ptr_type, align_bytes); - - result_type = get_optional_type(ira->codegen, better_ptr_type); - } else if (target_type->id == ZigTypeIdOptional && - target_type->data.maybe.child_type->id == ZigTypeIdFn) - { - FnTypeId fn_type_id = target_type->data.maybe.child_type->data.fn.fn_type_id; - old_align_bytes = fn_type_id.alignment; - fn_type_id.alignment = align_bytes; - ZigType *fn_type = get_fn_type(ira->codegen, &fn_type_id); - result_type = get_optional_type(ira->codegen, fn_type); - } else if (is_slice(target_type)) { - ZigType *slice_ptr_type = target_type->data.structure.fields[slice_ptr_index]->type_entry; - if ((err = resolve_ptr_align(ira, slice_ptr_type, &old_align_bytes))) - return ira->codegen->invalid_inst_gen; - ZigType *result_ptr_type = adjust_ptr_align(ira->codegen, slice_ptr_type, align_bytes); - result_type = get_slice_type(ira->codegen, result_ptr_type); - } else { - ir_add_error(ira, &target->base, - buf_sprintf("expected pointer or slice, found '%s'", buf_ptr(&target_type->name))); - return ira->codegen->invalid_inst_gen; - } - - if (instr_is_comptime(target)) { - ZigValue *val = ir_resolve_const(ira, target, UndefBad); - if (!val) - return ira->codegen->invalid_inst_gen; - - if (val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr && - val->data.x_ptr.data.hard_coded_addr.addr % align_bytes != 0) - { - ir_add_error(ira, &target->base, - buf_sprintf("pointer address 0x%" ZIG_PRI_x64 " is not aligned to %" PRIu32 " bytes", - val->data.x_ptr.data.hard_coded_addr.addr, align_bytes)); - return ira->codegen->invalid_inst_gen; - } - - IrInstGen *result = ir_const(ira, &target->base, result_type); - copy_const_val(ira->codegen, result->value, val); - result->value->type = result_type; - return result; - } - - if (safety_check_on && align_bytes > old_align_bytes && align_bytes != 1) { - return ir_build_align_cast_gen(ira, target->base.scope, target->base.source_node, target, result_type); - } else { - return ir_build_cast(ira, &target->base, result_type, target, CastOpNoop); - } -} - -static IrInstGen *ir_analyze_ptr_cast(IrAnalyze *ira, IrInst* source_instr, IrInstGen *ptr, - IrInst *ptr_src, ZigType *dest_type, IrInst *dest_type_src, bool safety_check_on, - bool keep_bigger_alignment) -{ - Error err; - - ZigType *src_type = ptr->value->type; - assert(!type_is_invalid(src_type)); - - if (src_type == dest_type) { - return ptr; - } - - // We have a check for zero bits later so we use get_src_ptr_type to - // validate src_type and dest_type. - - ZigType *if_slice_ptr_type; - if (is_slice(src_type)) { - TypeStructField *ptr_field = src_type->data.structure.fields[slice_ptr_index]; - if_slice_ptr_type = resolve_struct_field_type(ira->codegen, ptr_field); - } else { - if_slice_ptr_type = src_type; - - ZigType *src_ptr_type = get_src_ptr_type(src_type); - if (src_ptr_type == nullptr) { - ir_add_error(ira, ptr_src, buf_sprintf("expected pointer, found '%s'", buf_ptr(&src_type->name))); - return ira->codegen->invalid_inst_gen; - } - } - - ZigType *dest_ptr_type = get_src_ptr_type(dest_type); - if (dest_ptr_type == nullptr) { - ir_add_error(ira, dest_type_src, - buf_sprintf("expected pointer, found '%s'", buf_ptr(&dest_type->name))); - return ira->codegen->invalid_inst_gen; - } - - if (get_ptr_const(ira->codegen, src_type) && !get_ptr_const(ira->codegen, dest_type)) { - ir_add_error(ira, source_instr, buf_sprintf("cast discards const qualifier")); - return ira->codegen->invalid_inst_gen; - } - uint32_t dest_align_bytes; - if ((err = resolve_ptr_align(ira, dest_type, &dest_align_bytes))) - return ira->codegen->invalid_inst_gen; - - uint32_t src_align_bytes = 0; - if (keep_bigger_alignment || dest_align_bytes != 1) { - if ((err = resolve_ptr_align(ira, src_type, &src_align_bytes))) - return ira->codegen->invalid_inst_gen; - } - - if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusZeroBitsKnown))) - return ira->codegen->invalid_inst_gen; - - if ((err = type_resolve(ira->codegen, src_type, ResolveStatusZeroBitsKnown))) - return ira->codegen->invalid_inst_gen; - - if (safety_check_on && - type_has_bits(ira->codegen, dest_type) && - !type_has_bits(ira->codegen, if_slice_ptr_type)) - { - ErrorMsg *msg = ir_add_error(ira, source_instr, - buf_sprintf("'%s' and '%s' do not have the same in-memory representation", - buf_ptr(&src_type->name), buf_ptr(&dest_type->name))); - add_error_note(ira->codegen, msg, ptr_src->source_node, - buf_sprintf("'%s' has no in-memory bits", buf_ptr(&src_type->name))); - add_error_note(ira->codegen, msg, dest_type_src->source_node, - buf_sprintf("'%s' has in-memory bits", buf_ptr(&dest_type->name))); - return ira->codegen->invalid_inst_gen; - } - - // For slices, follow the `ptr` field. - if (is_slice(src_type)) { - TypeStructField *ptr_field = src_type->data.structure.fields[slice_ptr_index]; - IrInstGen *ptr_ref = ir_get_ref(ira, source_instr, ptr, true, false); - IrInstGen *ptr_ptr = ir_analyze_struct_field_ptr(ira, source_instr, ptr_field, ptr_ref, src_type, false); - ptr = ir_get_deref(ira, source_instr, ptr_ptr, nullptr); - } - - if (instr_is_comptime(ptr)) { - bool dest_allows_addr_zero = ptr_allows_addr_zero(dest_type); - UndefAllowed is_undef_allowed = dest_allows_addr_zero ? UndefOk : UndefBad; - ZigValue *val = ir_resolve_const(ira, ptr, is_undef_allowed); - if (val == nullptr) - return ira->codegen->invalid_inst_gen; - - if (value_is_comptime(val) && val->special != ConstValSpecialUndef) { - bool is_addr_zero = val->data.x_ptr.special == ConstPtrSpecialNull || - (val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr && - val->data.x_ptr.data.hard_coded_addr.addr == 0); - if (is_addr_zero && !dest_allows_addr_zero) { - ir_add_error(ira, source_instr, - buf_sprintf("null pointer casted to type '%s'", buf_ptr(&dest_type->name))); - return ira->codegen->invalid_inst_gen; - } - } - - IrInstGen *result; - if (val->data.x_ptr.mut == ConstPtrMutInfer) { - result = ir_build_ptr_cast_gen(ira, source_instr, dest_type, ptr, safety_check_on); - } else { - result = ir_const(ira, source_instr, dest_type); - } - InferredStructField *isf = (val->type->id == ZigTypeIdPointer) ? - val->type->data.pointer.inferred_struct_field : nullptr; - if (isf == nullptr) { - copy_const_val(ira->codegen, result->value, val); - } else { - // The destination value should have x_ptr struct pointing to underlying struct value - result->value->data.x_ptr.mut = val->data.x_ptr.mut; - TypeStructField *field = find_struct_type_field(isf->inferred_struct_type, isf->field_name); - assert(field != nullptr); - if (field->is_comptime) { - result->value->data.x_ptr.special = ConstPtrSpecialRef; - result->value->data.x_ptr.data.ref.pointee = field->init_val; - } else { - assert(val->data.x_ptr.special == ConstPtrSpecialRef); - result->value->data.x_ptr.special = ConstPtrSpecialBaseStruct; - result->value->data.x_ptr.data.base_struct.struct_val = val->data.x_ptr.data.ref.pointee; - result->value->data.x_ptr.data.base_struct.field_index = field->src_index; - } - result->value->special = ConstValSpecialStatic; - } - result->value->type = dest_type; - - // Keep the bigger alignment, it can only help- unless the target is zero bits. - if (keep_bigger_alignment && src_align_bytes > dest_align_bytes && type_has_bits(ira->codegen, dest_type)) { - result = ir_align_cast(ira, result, src_align_bytes, false); - } - - return result; - } - - if (src_align_bytes != 0 && dest_align_bytes > src_align_bytes) { - ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("cast increases pointer alignment")); - add_error_note(ira->codegen, msg, ptr_src->source_node, - buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&src_type->name), src_align_bytes)); - add_error_note(ira->codegen, msg, dest_type_src->source_node, - buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&dest_type->name), dest_align_bytes)); - return ira->codegen->invalid_inst_gen; - } - - IrInstGen *casted_ptr = ir_build_ptr_cast_gen(ira, source_instr, dest_type, ptr, safety_check_on); - - // Keep the bigger alignment, it can only help- unless the target is zero bits. - IrInstGen *result; - if (keep_bigger_alignment && src_align_bytes > dest_align_bytes && type_has_bits(ira->codegen, dest_type)) { - result = ir_align_cast(ira, casted_ptr, src_align_bytes, false); - if (type_is_invalid(result->value->type)) - return ira->codegen->invalid_inst_gen; - } else { - result = casted_ptr; - } - return result; -} - -static IrInstGen *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstSrcPtrCast *instruction) { - IrInstGen *dest_type_value = instruction->dest_type->child; - ZigType *dest_type = ir_resolve_type(ira, dest_type_value); - if (type_is_invalid(dest_type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *ptr = instruction->ptr->child; - ZigType *src_type = ptr->value->type; - if (type_is_invalid(src_type)) - return ira->codegen->invalid_inst_gen; - - bool keep_bigger_alignment = true; - return ir_analyze_ptr_cast(ira, &instruction->base.base, ptr, &instruction->ptr->base, - dest_type, &dest_type_value->base, instruction->safety_check_on, keep_bigger_alignment); -} - -static void buf_write_value_bytes_array(CodeGen *codegen, uint8_t *buf, ZigValue *val, size_t len) { - size_t buf_i = 0; - // TODO optimize the buf case - expand_undef_array(codegen, val); - for (size_t elem_i = 0; elem_i < val->type->data.array.len; elem_i += 1) { - ZigValue *elem = &val->data.x_array.data.s_none.elements[elem_i]; - buf_write_value_bytes(codegen, &buf[buf_i], elem); - buf_i += type_size(codegen, elem->type); - } - if (val->type->id == ZigTypeIdArray && val->type->data.array.sentinel != nullptr) { - buf_write_value_bytes(codegen, &buf[buf_i], val->type->data.array.sentinel); - } -} - -static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ZigValue *val) { - if (val->special == ConstValSpecialUndef) { - expand_undef_struct(codegen, val); - val->special = ConstValSpecialStatic; - } - assert(val->special == ConstValSpecialStatic); - switch (val->type->id) { - case ZigTypeIdInvalid: - case ZigTypeIdMetaType: - case ZigTypeIdOpaque: - case ZigTypeIdBoundFn: - case ZigTypeIdUnreachable: - case ZigTypeIdComptimeFloat: - case ZigTypeIdComptimeInt: - case ZigTypeIdEnumLiteral: - case ZigTypeIdUndefined: - case ZigTypeIdNull: - case ZigTypeIdErrorUnion: - case ZigTypeIdErrorSet: - zig_unreachable(); - case ZigTypeIdVoid: - return; - case ZigTypeIdBool: - buf[0] = val->data.x_bool ? 1 : 0; - return; - case ZigTypeIdInt: - bigint_write_twos_complement(&val->data.x_bigint, buf, val->type->data.integral.bit_count, - codegen->is_big_endian); - return; - case ZigTypeIdEnum: - bigint_write_twos_complement(&val->data.x_enum_tag, buf, - val->type->data.enumeration.tag_int_type->data.integral.bit_count, - codegen->is_big_endian); - return; - case ZigTypeIdFloat: - float_write_ieee597(val, buf, codegen->is_big_endian); - return; - case ZigTypeIdPointer: - if (val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) { - BigInt bn; - bigint_init_unsigned(&bn, val->data.x_ptr.data.hard_coded_addr.addr); - bigint_write_twos_complement(&bn, buf, codegen->builtin_types.entry_usize->data.integral.bit_count, codegen->is_big_endian); - return; - } else { - zig_unreachable(); - } - case ZigTypeIdArray: - return buf_write_value_bytes_array(codegen, buf, val, val->type->data.array.len); - case ZigTypeIdVector: - return buf_write_value_bytes_array(codegen, buf, val, val->type->data.vector.len); - case ZigTypeIdStruct: - switch (val->type->data.structure.layout) { - case ContainerLayoutAuto: - zig_unreachable(); - case ContainerLayoutExtern: { - size_t src_field_count = val->type->data.structure.src_field_count; - for (size_t field_i = 0; field_i < src_field_count; field_i += 1) { - TypeStructField *struct_field = val->type->data.structure.fields[field_i]; - if (struct_field->gen_index == SIZE_MAX) - continue; - ZigValue *field_val = val->data.x_struct.fields[field_i]; - size_t offset = struct_field->offset; - buf_write_value_bytes(codegen, buf + offset, field_val); - } - return; - } - case ContainerLayoutPacked: { - size_t src_field_count = val->type->data.structure.src_field_count; - size_t gen_field_count = val->type->data.structure.gen_field_count; - size_t gen_i = 0; - size_t src_i = 0; - size_t offset = 0; - bool is_big_endian = codegen->is_big_endian; - uint8_t child_buf_prealloc[16]; - size_t child_buf_len = 16; - uint8_t *child_buf = child_buf_prealloc; - while (gen_i < gen_field_count) { - size_t big_int_byte_count = val->type->data.structure.host_int_bytes[gen_i]; - if (big_int_byte_count > child_buf_len) { - child_buf = heap::c_allocator.allocate_nonzero(big_int_byte_count); - child_buf_len = big_int_byte_count; - } - BigInt big_int; - bigint_init_unsigned(&big_int, 0); - size_t used_bits = 0; - while (src_i < src_field_count) { - TypeStructField *field = val->type->data.structure.fields[src_i]; - assert(field->gen_index != SIZE_MAX); - if (field->gen_index != gen_i) - break; - uint32_t packed_bits_size = type_size_bits(codegen, field->type_entry); - buf_write_value_bytes(codegen, child_buf, val->data.x_struct.fields[src_i]); - BigInt child_val; - bigint_read_twos_complement(&child_val, child_buf, packed_bits_size, is_big_endian, - false); - if (is_big_endian) { - BigInt shift_amt; - bigint_init_unsigned(&shift_amt, packed_bits_size); - BigInt shifted; - bigint_shl(&shifted, &big_int, &shift_amt); - bigint_or(&big_int, &shifted, &child_val); - } else { - BigInt shift_amt; - bigint_init_unsigned(&shift_amt, used_bits); - BigInt child_val_shifted; - bigint_shl(&child_val_shifted, &child_val, &shift_amt); - BigInt tmp; - bigint_or(&tmp, &big_int, &child_val_shifted); - big_int = tmp; - used_bits += packed_bits_size; - } - src_i += 1; - } - bigint_write_twos_complement(&big_int, buf + offset, big_int_byte_count * 8, is_big_endian); - offset += big_int_byte_count; - gen_i += 1; - } - return; - } - } - zig_unreachable(); - case ZigTypeIdOptional: - zig_panic("TODO buf_write_value_bytes maybe type"); - case ZigTypeIdFn: - zig_panic("TODO buf_write_value_bytes fn type"); - case ZigTypeIdUnion: - zig_panic("TODO buf_write_value_bytes union type"); - case ZigTypeIdFnFrame: - zig_panic("TODO buf_write_value_bytes async fn frame type"); - case ZigTypeIdAnyFrame: - zig_panic("TODO buf_write_value_bytes anyframe type"); - } - zig_unreachable(); -} - -static Error buf_read_value_bytes_array(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, uint8_t *buf, - ZigValue *val, ZigType *elem_type, size_t len) -{ - Error err; - uint64_t elem_size = type_size(codegen, elem_type); - - switch (val->data.x_array.special) { - case ConstArraySpecialNone: - val->data.x_array.data.s_none.elements = codegen->pass1_arena->allocate(len); - for (size_t i = 0; i < len; i++) { - ZigValue *elem = &val->data.x_array.data.s_none.elements[i]; - elem->special = ConstValSpecialStatic; - elem->type = elem_type; - if ((err = buf_read_value_bytes(ira, codegen, source_node, buf + (elem_size * i), elem))) - return err; - } - return ErrorNone; - case ConstArraySpecialUndef: - zig_panic("TODO buf_read_value_bytes ConstArraySpecialUndef array type"); - case ConstArraySpecialBuf: - zig_panic("TODO buf_read_value_bytes ConstArraySpecialBuf array type"); - } - zig_unreachable(); -} - -static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, uint8_t *buf, ZigValue *val) { - Error err; - src_assert(val->special == ConstValSpecialStatic, source_node); - switch (val->type->id) { - case ZigTypeIdInvalid: - case ZigTypeIdMetaType: - case ZigTypeIdOpaque: - case ZigTypeIdBoundFn: - case ZigTypeIdUnreachable: - case ZigTypeIdComptimeFloat: - case ZigTypeIdComptimeInt: - case ZigTypeIdEnumLiteral: - case ZigTypeIdUndefined: - case ZigTypeIdNull: - zig_unreachable(); - case ZigTypeIdVoid: - return ErrorNone; - case ZigTypeIdBool: - val->data.x_bool = (buf[0] != 0); - return ErrorNone; - case ZigTypeIdInt: - bigint_read_twos_complement(&val->data.x_bigint, buf, val->type->data.integral.bit_count, - codegen->is_big_endian, val->type->data.integral.is_signed); - return ErrorNone; - case ZigTypeIdFloat: - float_read_ieee597(val, buf, codegen->is_big_endian); - return ErrorNone; - case ZigTypeIdPointer: - { - val->data.x_ptr.special = ConstPtrSpecialHardCodedAddr; - BigInt bn; - bigint_read_twos_complement(&bn, buf, codegen->builtin_types.entry_usize->data.integral.bit_count, - codegen->is_big_endian, false); - val->data.x_ptr.data.hard_coded_addr.addr = bigint_as_usize(&bn); - return ErrorNone; - } - case ZigTypeIdArray: - return buf_read_value_bytes_array(ira, codegen, source_node, buf, val, val->type->data.array.child_type, - val->type->data.array.len); - case ZigTypeIdVector: - return buf_read_value_bytes_array(ira, codegen, source_node, buf, val, val->type->data.vector.elem_type, - val->type->data.vector.len); - case ZigTypeIdEnum: - switch (val->type->data.enumeration.layout) { - case ContainerLayoutAuto: - zig_panic("TODO buf_read_value_bytes enum auto"); - case ContainerLayoutPacked: - zig_panic("TODO buf_read_value_bytes enum packed"); - case ContainerLayoutExtern: { - ZigType *tag_int_type = val->type->data.enumeration.tag_int_type; - src_assert(tag_int_type->id == ZigTypeIdInt, source_node); - bigint_read_twos_complement(&val->data.x_enum_tag, buf, tag_int_type->data.integral.bit_count, - codegen->is_big_endian, tag_int_type->data.integral.is_signed); - return ErrorNone; - } - } - zig_unreachable(); - case ZigTypeIdStruct: - switch (val->type->data.structure.layout) { - case ContainerLayoutAuto: { - switch(val->type->data.structure.special){ - case StructSpecialNone: - case StructSpecialInferredTuple: - case StructSpecialInferredStruct: { - ErrorMsg *msg = opt_ir_add_error_node(ira, codegen, source_node, - buf_sprintf("non-extern, non-packed struct '%s' cannot have its bytes reinterpreted", - buf_ptr(&val->type->name))); - add_error_note(codegen, msg, val->type->data.structure.decl_node, - buf_sprintf("declared here")); - break; - } - case StructSpecialSlice: { - opt_ir_add_error_node(ira, codegen, source_node, - buf_sprintf("slice '%s' cannot have its bytes reinterpreted", - buf_ptr(&val->type->name))); - break; - } - } - return ErrorSemanticAnalyzeFail; - } - case ContainerLayoutExtern: { - size_t src_field_count = val->type->data.structure.src_field_count; - val->data.x_struct.fields = alloc_const_vals_ptrs(codegen, src_field_count); - for (size_t field_i = 0; field_i < src_field_count; field_i += 1) { - ZigValue *field_val = val->data.x_struct.fields[field_i]; - field_val->special = ConstValSpecialStatic; - TypeStructField *struct_field = val->type->data.structure.fields[field_i]; - field_val->type = struct_field->type_entry; - if (struct_field->gen_index == SIZE_MAX) - continue; - size_t offset = struct_field->offset; - uint8_t *new_buf = buf + offset; - if ((err = buf_read_value_bytes(ira, codegen, source_node, new_buf, field_val))) - return err; - } - return ErrorNone; - } - case ContainerLayoutPacked: { - size_t src_field_count = val->type->data.structure.src_field_count; - val->data.x_struct.fields = alloc_const_vals_ptrs(codegen, src_field_count); - size_t gen_field_count = val->type->data.structure.gen_field_count; - size_t gen_i = 0; - size_t src_i = 0; - size_t offset = 0; - bool is_big_endian = codegen->is_big_endian; - uint8_t child_buf_prealloc[16]; - size_t child_buf_len = 16; - uint8_t *child_buf = child_buf_prealloc; - while (gen_i < gen_field_count) { - size_t big_int_byte_count = val->type->data.structure.host_int_bytes[gen_i]; - if (big_int_byte_count > child_buf_len) { - child_buf = heap::c_allocator.allocate_nonzero(big_int_byte_count); - child_buf_len = big_int_byte_count; - } - BigInt big_int; - bigint_read_twos_complement(&big_int, buf + offset, big_int_byte_count * 8, is_big_endian, false); - uint64_t bit_offset = 0; - while (src_i < src_field_count) { - TypeStructField *field = val->type->data.structure.fields[src_i]; - src_assert(field->gen_index != SIZE_MAX, source_node); - if (field->gen_index != gen_i) - break; - ZigValue *field_val = val->data.x_struct.fields[src_i]; - field_val->special = ConstValSpecialStatic; - field_val->type = field->type_entry; - uint32_t packed_bits_size = type_size_bits(codegen, field->type_entry); - - BigInt child_val; - if (is_big_endian) { - BigInt packed_bits_size_bi; - bigint_init_unsigned(&packed_bits_size_bi, big_int_byte_count * 8 - packed_bits_size - bit_offset); - BigInt tmp; - bigint_shr(&tmp, &big_int, &packed_bits_size_bi); - bigint_truncate(&child_val, &tmp, packed_bits_size, false); - } else { - BigInt packed_bits_size_bi; - bigint_init_unsigned(&packed_bits_size_bi, packed_bits_size); - bigint_truncate(&child_val, &big_int, packed_bits_size, false); - BigInt tmp; - bigint_shr(&tmp, &big_int, &packed_bits_size_bi); - big_int = tmp; - } - - bigint_write_twos_complement(&child_val, child_buf, packed_bits_size, is_big_endian); - if ((err = buf_read_value_bytes(ira, codegen, source_node, child_buf, field_val))) { - return err; - } - - bit_offset += packed_bits_size; - src_i += 1; - } - offset += big_int_byte_count; - gen_i += 1; - } - return ErrorNone; - } - } - zig_unreachable(); - case ZigTypeIdOptional: - zig_panic("TODO buf_read_value_bytes maybe type"); - case ZigTypeIdErrorUnion: - zig_panic("TODO buf_read_value_bytes error union"); - case ZigTypeIdErrorSet: - zig_panic("TODO buf_read_value_bytes pure error type"); - case ZigTypeIdFn: - zig_panic("TODO buf_read_value_bytes fn type"); - case ZigTypeIdUnion: - zig_panic("TODO buf_read_value_bytes union type"); - case ZigTypeIdFnFrame: - zig_panic("TODO buf_read_value_bytes async fn frame type"); - case ZigTypeIdAnyFrame: - zig_panic("TODO buf_read_value_bytes anyframe type"); - } - zig_unreachable(); -} - -static IrInstGen *ir_analyze_bit_cast(IrAnalyze *ira, IrInst* source_instr, IrInstGen *value, - ZigType *dest_type) -{ - Error err; - - ZigType *src_type = value->value->type; - ir_assert(type_can_bit_cast(src_type), source_instr); - ir_assert(type_can_bit_cast(dest_type), source_instr); - - if (dest_type->id == ZigTypeIdEnum) { - ErrorMsg *msg = ir_add_error_node(ira, source_instr->source_node, - buf_sprintf("cannot cast a value of type '%s'", buf_ptr(&dest_type->name))); - add_error_note(ira->codegen, msg, source_instr->source_node, - buf_sprintf("use @intToEnum for type coercion")); - return ira->codegen->invalid_inst_gen; - } - - if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusSizeKnown))) - return ira->codegen->invalid_inst_gen; - - if ((err = type_resolve(ira->codegen, src_type, ResolveStatusSizeKnown))) - return ira->codegen->invalid_inst_gen; - - const bool src_is_ptr = handle_is_ptr(ira->codegen, src_type); - const bool dest_is_ptr = handle_is_ptr(ira->codegen, dest_type); - - const uint64_t dest_size_bytes = type_size(ira->codegen, dest_type); - const uint64_t src_size_bytes = type_size(ira->codegen, src_type); - if (dest_size_bytes != src_size_bytes) { - ir_add_error(ira, source_instr, - buf_sprintf("destination type '%s' has size %" ZIG_PRI_u64 " but source type '%s' has size %" ZIG_PRI_u64, - buf_ptr(&dest_type->name), dest_size_bytes, - buf_ptr(&src_type->name), src_size_bytes)); - return ira->codegen->invalid_inst_gen; - } - - const uint64_t dest_size_bits = type_size_bits(ira->codegen, dest_type); - const uint64_t src_size_bits = type_size_bits(ira->codegen, src_type); - if (dest_size_bits != src_size_bits) { - ir_add_error(ira, source_instr, - buf_sprintf("destination type '%s' has %" ZIG_PRI_u64 " bits but source type '%s' has %" ZIG_PRI_u64 " bits", - buf_ptr(&dest_type->name), dest_size_bits, - buf_ptr(&src_type->name), src_size_bits)); - return ira->codegen->invalid_inst_gen; - } - - if (instr_is_comptime(value)) { - ZigValue *val = ir_resolve_const(ira, value, UndefBad); - if (!val) - return ira->codegen->invalid_inst_gen; - - IrInstGen *result = ir_const(ira, source_instr, dest_type); - uint8_t *buf = heap::c_allocator.allocate_nonzero(src_size_bytes); - buf_write_value_bytes(ira->codegen, buf, val); - if ((err = buf_read_value_bytes(ira, ira->codegen, source_instr->source_node, buf, result->value))) - return ira->codegen->invalid_inst_gen; - return result; - } - - if (dest_is_ptr && !src_is_ptr) { - // Spill the scalar into a local memory location and take its address - value = ir_get_ref(ira, source_instr, value, false, false); - } - - return ir_build_bit_cast_gen(ira, source_instr, value, dest_type); -} - -static IrInstGen *ir_analyze_int_to_ptr(IrAnalyze *ira, IrInst* source_instr, IrInstGen *target, - ZigType *ptr_type) -{ - Error err; - - ir_assert(get_src_ptr_type(ptr_type) != nullptr, source_instr); - ir_assert(type_has_bits(ira->codegen, ptr_type), source_instr); - - IrInstGen *casted_int = ir_implicit_cast(ira, target, ira->codegen->builtin_types.entry_usize); - if (type_is_invalid(casted_int->value->type)) - return ira->codegen->invalid_inst_gen; - - if (instr_is_comptime(casted_int)) { - ZigValue *val = ir_resolve_const(ira, casted_int, UndefBad); - if (!val) - return ira->codegen->invalid_inst_gen; - - uint64_t addr = bigint_as_u64(&val->data.x_bigint); - if (!ptr_allows_addr_zero(ptr_type) && addr == 0) { - ir_add_error(ira, source_instr, - buf_sprintf("pointer type '%s' does not allow address zero", buf_ptr(&ptr_type->name))); - return ira->codegen->invalid_inst_gen; - } - - uint32_t align_bytes; - if ((err = resolve_ptr_align(ira, ptr_type, &align_bytes))) - return ira->codegen->invalid_inst_gen; - - if (addr != 0 && addr % align_bytes != 0) { - ir_add_error(ira, source_instr, - buf_sprintf("pointer type '%s' requires aligned address", - buf_ptr(&ptr_type->name))); - return ira->codegen->invalid_inst_gen; - } - - IrInstGen *result = ir_const(ira, source_instr, ptr_type); - if (ptr_type->id == ZigTypeIdOptional && addr == 0) { - result->value->data.x_ptr.special = ConstPtrSpecialNull; - result->value->data.x_ptr.mut = ConstPtrMutComptimeConst; - } else { - result->value->data.x_ptr.special = ConstPtrSpecialHardCodedAddr; - result->value->data.x_ptr.mut = ConstPtrMutRuntimeVar; - result->value->data.x_ptr.data.hard_coded_addr.addr = addr; - } - - return result; - } - - return ir_build_int_to_ptr_gen(ira, source_instr->scope, source_instr->source_node, casted_int, ptr_type); -} - -static IrInstGen *ir_analyze_instruction_int_to_ptr(IrAnalyze *ira, IrInstSrcIntToPtr *instruction) { - Error err; - IrInstGen *dest_type_value = instruction->dest_type->child; - ZigType *dest_type = ir_resolve_type(ira, dest_type_value); - if (type_is_invalid(dest_type)) - return ira->codegen->invalid_inst_gen; - - // We explicitly check for the size, so we can use get_src_ptr_type - if (get_src_ptr_type(dest_type) == nullptr) { - ir_add_error(ira, &dest_type_value->base, buf_sprintf("expected pointer, found '%s'", buf_ptr(&dest_type->name))); - return ira->codegen->invalid_inst_gen; - } - - bool has_bits; - if ((err = type_has_bits2(ira->codegen, dest_type, &has_bits))) - return ira->codegen->invalid_inst_gen; - - if (!has_bits) { - ir_add_error(ira, &dest_type_value->base, - buf_sprintf("type '%s' has 0 bits and cannot store information", buf_ptr(&dest_type->name))); - return ira->codegen->invalid_inst_gen; - } - - IrInstGen *target = instruction->target->child; - if (type_is_invalid(target->value->type)) - return ira->codegen->invalid_inst_gen; - - return ir_analyze_int_to_ptr(ira, &instruction->base.base, target, dest_type); -} - -static IrInstGen *ir_analyze_instruction_decl_ref(IrAnalyze *ira, IrInstSrcDeclRef *instruction) { - IrInstGen *ref_instruction = ir_analyze_decl_ref(ira, &instruction->base.base, instruction->tld); - if (type_is_invalid(ref_instruction->value->type)) { - return ira->codegen->invalid_inst_gen; - } - - if (instruction->lval == LValPtr || instruction->lval == LValAssign) { - return ref_instruction; - } else { - return ir_get_deref(ira, &instruction->base.base, ref_instruction, nullptr); - } -} - -static IrInstGen *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstSrcPtrToInt *instruction) { - Error err; - IrInstGen *target = instruction->target->child; - if (type_is_invalid(target->value->type)) - return ira->codegen->invalid_inst_gen; - - ZigType *usize = ira->codegen->builtin_types.entry_usize; - - ZigType *src_ptr_type = get_src_ptr_type(target->value->type); - if (src_ptr_type == nullptr) { - ir_add_error(ira, &target->base, - buf_sprintf("expected pointer, found '%s'", buf_ptr(&target->value->type->name))); - return ira->codegen->invalid_inst_gen; - } - - bool has_bits; - if ((err = type_has_bits2(ira->codegen, src_ptr_type, &has_bits))) - return ira->codegen->invalid_inst_gen; - - if (!has_bits) { - ir_add_error(ira, &target->base, - buf_sprintf("pointer to size 0 type has no address")); - return ira->codegen->invalid_inst_gen; - } - - if (instr_is_comptime(target)) { - ZigValue *val = ir_resolve_const(ira, target, UndefBad); - if (!val) - return ira->codegen->invalid_inst_gen; - - // Since we've already run this type trough get_src_ptr_type it is - // safe to access the x_ptr fields - if (val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) { - IrInstGen *result = ir_const(ira, &instruction->base.base, usize); - bigint_init_unsigned(&result->value->data.x_bigint, val->data.x_ptr.data.hard_coded_addr.addr); - result->value->type = usize; - return result; - } else if (val->data.x_ptr.special == ConstPtrSpecialNull) { - IrInstGen *result = ir_const(ira, &instruction->base.base, usize); - bigint_init_unsigned(&result->value->data.x_bigint, 0); - result->value->type = usize; - return result; - } - } - - return ir_build_ptr_to_int_gen(ira, &instruction->base.base, target); -} - -static IrInstGen *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstSrcPtrType *instruction) { - IrInstGen *result = ir_const(ira, &instruction->base.base, ira->codegen->builtin_types.entry_type); - result->value->special = ConstValSpecialLazy; - - LazyValuePtrType *lazy_ptr_type = heap::c_allocator.create(); - lazy_ptr_type->ira = ira; ira_ref(ira); - result->value->data.x_lazy = &lazy_ptr_type->base; - lazy_ptr_type->base.id = LazyValueIdPtrType; - - if (instruction->sentinel != nullptr) { - if (instruction->ptr_len != PtrLenUnknown) { - ir_add_error(ira, &instruction->base.base, - buf_sprintf("sentinels are only allowed on unknown-length pointers")); - return ira->codegen->invalid_inst_gen; - } - - lazy_ptr_type->sentinel = instruction->sentinel->child; - if (ir_resolve_const(ira, lazy_ptr_type->sentinel, LazyOk) == nullptr) - return ira->codegen->invalid_inst_gen; - } - - lazy_ptr_type->elem_type = instruction->child_type->child; - if (ir_resolve_type_lazy(ira, lazy_ptr_type->elem_type) == nullptr) - return ira->codegen->invalid_inst_gen; - - if (instruction->align_value != nullptr) { - lazy_ptr_type->align_inst = instruction->align_value->child; - if (ir_resolve_const(ira, lazy_ptr_type->align_inst, LazyOk) == nullptr) - return ira->codegen->invalid_inst_gen; - } - - lazy_ptr_type->ptr_len = instruction->ptr_len; - lazy_ptr_type->is_const = instruction->is_const; - lazy_ptr_type->is_volatile = instruction->is_volatile; - lazy_ptr_type->is_allowzero = instruction->is_allow_zero; - lazy_ptr_type->bit_offset_in_host = instruction->bit_offset_start; - lazy_ptr_type->host_int_bytes = instruction->host_int_bytes; - - return result; -} - -static IrInstGen *ir_analyze_instruction_align_cast(IrAnalyze *ira, IrInstSrcAlignCast *instruction) { - IrInstGen *target = instruction->target->child; - if (type_is_invalid(target->value->type)) - return ira->codegen->invalid_inst_gen; - - ZigType *elem_type = nullptr; - if (is_slice(target->value->type)) { - ZigType *slice_ptr_type = target->value->type->data.structure.fields[slice_ptr_index]->type_entry; - elem_type = slice_ptr_type->data.pointer.child_type; - } else if (target->value->type->id == ZigTypeIdPointer) { - elem_type = target->value->type->data.pointer.child_type; - } - - uint32_t align_bytes; - IrInstGen *align_bytes_inst = instruction->align_bytes->child; - if (!ir_resolve_align(ira, align_bytes_inst, elem_type, &align_bytes)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *result = ir_align_cast(ira, target, align_bytes, true); - if (type_is_invalid(result->value->type)) - return ira->codegen->invalid_inst_gen; - - return result; -} - -static IrInstGen *ir_analyze_instruction_set_align_stack(IrAnalyze *ira, IrInstSrcSetAlignStack *instruction) { - uint32_t align_bytes; - IrInstGen *align_bytes_inst = instruction->align_bytes->child; - if (!ir_resolve_align(ira, align_bytes_inst, nullptr, &align_bytes)) - return ira->codegen->invalid_inst_gen; - - if (align_bytes > 256) { - ir_add_error(ira, &instruction->base.base, buf_sprintf("attempt to @setAlignStack(%" PRIu32 "); maximum is 256", align_bytes)); - return ira->codegen->invalid_inst_gen; - } - - ZigFn *fn_entry = ira->new_irb.exec->fn_entry; - if (fn_entry == nullptr) { - ir_add_error(ira, &instruction->base.base, buf_sprintf("@setAlignStack outside function")); - return ira->codegen->invalid_inst_gen; - } - if (fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionNaked) { - ir_add_error(ira, &instruction->base.base, buf_sprintf("@setAlignStack in naked function")); - return ira->codegen->invalid_inst_gen; - } - - if (fn_entry->fn_inline == FnInlineAlways) { - ir_add_error(ira, &instruction->base.base, buf_sprintf("@setAlignStack in inline function")); - return ira->codegen->invalid_inst_gen; - } - - if (fn_entry->set_alignstack_node != nullptr) { - ErrorMsg *msg = ir_add_error(ira, &instruction->base.base, - buf_sprintf("alignstack set twice")); - add_error_note(ira->codegen, msg, fn_entry->set_alignstack_node, buf_sprintf("first set here")); - return ira->codegen->invalid_inst_gen; - } - - fn_entry->set_alignstack_node = instruction->base.base.source_node; - fn_entry->alignstack_value = align_bytes; - - return ir_const_void(ira, &instruction->base.base); -} - -static IrInstGen *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstSrcArgType *instruction) { - IrInstGen *fn_type_inst = instruction->fn_type->child; - ZigType *fn_type = ir_resolve_type(ira, fn_type_inst); - if (type_is_invalid(fn_type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *arg_index_inst = instruction->arg_index->child; - uint64_t arg_index; - if (!ir_resolve_usize(ira, arg_index_inst, &arg_index)) - return ira->codegen->invalid_inst_gen; - - if (fn_type->id == ZigTypeIdBoundFn) { - fn_type = fn_type->data.bound_fn.fn_type; - arg_index += 1; - } - if (fn_type->id != ZigTypeIdFn) { - ir_add_error(ira, &fn_type_inst->base, buf_sprintf("expected function, found '%s'", buf_ptr(&fn_type->name))); - return ira->codegen->invalid_inst_gen; - } - - FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; - if (arg_index >= fn_type_id->param_count) { - if (instruction->allow_var) { - // TODO remove this with var args - return ir_const_type(ira, &instruction->base.base, ira->codegen->builtin_types.entry_anytype); - } - ir_add_error(ira, &arg_index_inst->base, - buf_sprintf("arg index %" ZIG_PRI_u64 " out of bounds; '%s' has %" ZIG_PRI_usize " argument(s)", - arg_index, buf_ptr(&fn_type->name), fn_type_id->param_count)); - return ira->codegen->invalid_inst_gen; - } - - ZigType *result_type = fn_type_id->param_info[arg_index].type; - if (result_type == nullptr) { - // Args are only unresolved if our function is generic. - ir_assert(fn_type->data.fn.is_generic, &instruction->base.base); - - if (instruction->allow_var) { - return ir_const_type(ira, &instruction->base.base, ira->codegen->builtin_types.entry_anytype); - } else { - ir_add_error(ira, &arg_index_inst->base, - buf_sprintf("@ArgType could not resolve the type of arg %" ZIG_PRI_u64 " because '%s' is generic", - arg_index, buf_ptr(&fn_type->name))); - return ira->codegen->invalid_inst_gen; - } - } - return ir_const_type(ira, &instruction->base.base, result_type); -} - -static IrInstGen *ir_analyze_instruction_tag_type(IrAnalyze *ira, IrInstSrcTagType *instruction) { - Error err; - IrInstGen *target_inst = instruction->target->child; - ZigType *enum_type = ir_resolve_type(ira, target_inst); - if (type_is_invalid(enum_type)) - return ira->codegen->invalid_inst_gen; - - if (enum_type->id == ZigTypeIdEnum) { - if ((err = type_resolve(ira->codegen, enum_type, ResolveStatusSizeKnown))) - return ira->codegen->invalid_inst_gen; - - return ir_const_type(ira, &instruction->base.base, enum_type->data.enumeration.tag_int_type); - } else if (enum_type->id == ZigTypeIdUnion) { - ZigType *tag_type = ir_resolve_union_tag_type(ira, instruction->target->base.source_node, enum_type); - if (type_is_invalid(tag_type)) - return ira->codegen->invalid_inst_gen; - return ir_const_type(ira, &instruction->base.base, tag_type); - } else { - ir_add_error(ira, &target_inst->base, buf_sprintf("expected enum or union, found '%s'", - buf_ptr(&enum_type->name))); - return ira->codegen->invalid_inst_gen; - } -} - -static ZigType *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstGen *op) { - ZigType *operand_type = ir_resolve_type(ira, op); - if (type_is_invalid(operand_type)) - return ira->codegen->builtin_types.entry_invalid; - - if (operand_type->id == ZigTypeIdInt || operand_type->id == ZigTypeIdEnum) { - ZigType *int_type; - if (operand_type->id == ZigTypeIdEnum) { - int_type = operand_type->data.enumeration.tag_int_type; - } else { - int_type = operand_type; - } - auto bit_count = int_type->data.integral.bit_count; - uint32_t max_atomic_bits = target_arch_largest_atomic_bits(ira->codegen->zig_target->arch); - - if (bit_count > max_atomic_bits) { - ir_add_error(ira, &op->base, - buf_sprintf("expected %" PRIu32 "-bit integer type or smaller, found %" PRIu32 "-bit integer type", - max_atomic_bits, bit_count)); - return ira->codegen->builtin_types.entry_invalid; - } - } else if (operand_type->id == ZigTypeIdFloat) { - uint32_t max_atomic_bits = target_arch_largest_atomic_bits(ira->codegen->zig_target->arch); - if (operand_type->data.floating.bit_count > max_atomic_bits) { - ir_add_error(ira, &op->base, - buf_sprintf("expected %" PRIu32 "-bit float or smaller, found %" PRIu32 "-bit float", - max_atomic_bits, (uint32_t) operand_type->data.floating.bit_count)); - return ira->codegen->builtin_types.entry_invalid; - } - } else if (operand_type->id == ZigTypeIdBool) { - // will be treated as u8 - } else { - Error err; - ZigType *operand_ptr_type; - if ((err = get_codegen_ptr_type(ira->codegen, operand_type, &operand_ptr_type))) - return ira->codegen->builtin_types.entry_invalid; - if (operand_ptr_type == nullptr) { - ir_add_error(ira, &op->base, - buf_sprintf("expected bool, integer, float, enum or pointer type, found '%s'", - buf_ptr(&operand_type->name))); - return ira->codegen->builtin_types.entry_invalid; - } - } - - return operand_type; -} - -static IrInstGen *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstSrcAtomicRmw *instruction) { - ZigType *operand_type = ir_resolve_atomic_operand_type(ira, instruction->operand_type->child); - if (type_is_invalid(operand_type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *ptr_inst = instruction->ptr->child; - if (type_is_invalid(ptr_inst->value->type)) - return ira->codegen->invalid_inst_gen; - - // TODO let this be volatile - ZigType *ptr_type = get_pointer_to_type(ira->codegen, operand_type, false); - IrInstGen *casted_ptr = ir_implicit_cast(ira, ptr_inst, ptr_type); - if (type_is_invalid(casted_ptr->value->type)) - return ira->codegen->invalid_inst_gen; - - AtomicRmwOp op; - if (!ir_resolve_atomic_rmw_op(ira, instruction->op->child, &op)) { - return ira->codegen->invalid_inst_gen; - } - - if (operand_type->id == ZigTypeIdEnum && op != AtomicRmwOp_xchg) { - ir_add_error(ira, &instruction->op->base, - buf_sprintf("@atomicRmw with enum only allowed with .Xchg")); - return ira->codegen->invalid_inst_gen; - } else if (operand_type->id == ZigTypeIdBool && op != AtomicRmwOp_xchg) { - ir_add_error(ira, &instruction->op->base, - buf_sprintf("@atomicRmw with bool only allowed with .Xchg")); - return ira->codegen->invalid_inst_gen; - } else if (operand_type->id == ZigTypeIdFloat && op > AtomicRmwOp_sub) { - ir_add_error(ira, &instruction->op->base, - buf_sprintf("@atomicRmw with float only allowed with .Xchg, .Add and .Sub")); - return ira->codegen->invalid_inst_gen; - } - - IrInstGen *operand = instruction->operand->child; - if (type_is_invalid(operand->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *casted_operand = ir_implicit_cast(ira, operand, operand_type); - if (type_is_invalid(casted_operand->value->type)) - return ira->codegen->invalid_inst_gen; - - AtomicOrder ordering; - if (!ir_resolve_atomic_order(ira, instruction->ordering->child, &ordering)) - return ira->codegen->invalid_inst_gen; - if (ordering == AtomicOrderUnordered) { - ir_add_error(ira, &instruction->ordering->base, - buf_sprintf("@atomicRmw atomic ordering must not be Unordered")); - return ira->codegen->invalid_inst_gen; - } - - // special case zero bit types - switch (type_has_one_possible_value(ira->codegen, operand_type)) { - case OnePossibleValueInvalid: - return ira->codegen->invalid_inst_gen; - case OnePossibleValueYes: - return ir_const_move(ira, &instruction->base.base, get_the_one_possible_value(ira->codegen, operand_type)); - case OnePossibleValueNo: - break; - } - - IrInst *source_inst = &instruction->base.base; - if (instr_is_comptime(casted_operand) && instr_is_comptime(casted_ptr) && casted_ptr->value->data.x_ptr.mut == ConstPtrMutComptimeVar) { - ZigValue *ptr_val = ir_resolve_const(ira, casted_ptr, UndefBad); - if (ptr_val == nullptr) - return ira->codegen->invalid_inst_gen; - - ZigValue *op1_val = const_ptr_pointee(ira, ira->codegen, ptr_val, instruction->base.base.source_node); - if (op1_val == nullptr) - return ira->codegen->invalid_inst_gen; - - ZigValue *op2_val = ir_resolve_const(ira, casted_operand, UndefBad); - if (op2_val == nullptr) - return ira->codegen->invalid_inst_gen; - - IrInstGen *result = ir_const(ira, source_inst, operand_type); - copy_const_val(ira->codegen, result->value, op1_val); - if (op == AtomicRmwOp_xchg) { - copy_const_val(ira->codegen, op1_val, op2_val); - return result; - } - - if (operand_type->id == ZigTypeIdPointer || operand_type->id == ZigTypeIdOptional) { - ir_add_error(ira, &instruction->ordering->base, - buf_sprintf("TODO comptime @atomicRmw with pointers other than .Xchg")); - return ira->codegen->invalid_inst_gen; - } - - ErrorMsg *msg; - if (op == AtomicRmwOp_min || op == AtomicRmwOp_max) { - IrBinOp bin_op; - if (op == AtomicRmwOp_min) - // store op2 if op2 < op1 - bin_op = IrBinOpCmpGreaterThan; - else - // store op2 if op2 > op1 - bin_op = IrBinOpCmpLessThan; - - IrInstGen *dummy_value = ir_const(ira, source_inst, operand_type); - msg = ir_eval_bin_op_cmp_scalar(ira, source_inst, op1_val, bin_op, op2_val, dummy_value->value); - if (msg != nullptr) { - return ira->codegen->invalid_inst_gen; - } - if (dummy_value->value->data.x_bool) - copy_const_val(ira->codegen, op1_val, op2_val); - } else { - IrBinOp bin_op; - switch (op) { - case AtomicRmwOp_xchg: - case AtomicRmwOp_max: - case AtomicRmwOp_min: - zig_unreachable(); - case AtomicRmwOp_add: - if (operand_type->id == ZigTypeIdFloat) - bin_op = IrBinOpAdd; - else - bin_op = IrBinOpAddWrap; - break; - case AtomicRmwOp_sub: - if (operand_type->id == ZigTypeIdFloat) - bin_op = IrBinOpSub; - else - bin_op = IrBinOpSubWrap; - break; - case AtomicRmwOp_and: - case AtomicRmwOp_nand: - bin_op = IrBinOpBinAnd; - break; - case AtomicRmwOp_or: - bin_op = IrBinOpBinOr; - break; - case AtomicRmwOp_xor: - bin_op = IrBinOpBinXor; - break; - } - msg = ir_eval_math_op_scalar(ira, source_inst, operand_type, op1_val, bin_op, op2_val, op1_val); - if (msg != nullptr) { - return ira->codegen->invalid_inst_gen; - } - if (op == AtomicRmwOp_nand) { - bigint_not(&op1_val->data.x_bigint, &op1_val->data.x_bigint, - operand_type->data.integral.bit_count, operand_type->data.integral.is_signed); - } - } - return result; - } - - return ir_build_atomic_rmw_gen(ira, source_inst, casted_ptr, casted_operand, op, - ordering, operand_type); -} - -static IrInstGen *ir_analyze_instruction_atomic_load(IrAnalyze *ira, IrInstSrcAtomicLoad *instruction) { - ZigType *operand_type = ir_resolve_atomic_operand_type(ira, instruction->operand_type->child); - if (type_is_invalid(operand_type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *ptr_inst = instruction->ptr->child; - if (type_is_invalid(ptr_inst->value->type)) - return ira->codegen->invalid_inst_gen; - - ZigType *ptr_type = get_pointer_to_type(ira->codegen, operand_type, true); - IrInstGen *casted_ptr = ir_implicit_cast(ira, ptr_inst, ptr_type); - if (type_is_invalid(casted_ptr->value->type)) - return ira->codegen->invalid_inst_gen; - - AtomicOrder ordering; - if (!ir_resolve_atomic_order(ira, instruction->ordering->child, &ordering)) - return ira->codegen->invalid_inst_gen; - - if (ordering == AtomicOrderRelease || ordering == AtomicOrderAcqRel) { - ir_assert(instruction->ordering != nullptr, &instruction->base.base); - ir_add_error(ira, &instruction->ordering->base, - buf_sprintf("@atomicLoad atomic ordering must not be Release or AcqRel")); - return ira->codegen->invalid_inst_gen; - } - - if (instr_is_comptime(casted_ptr)) { - IrInstGen *result = ir_get_deref(ira, &instruction->base.base, casted_ptr, nullptr); - ir_assert(result->value->type != nullptr, &instruction->base.base); - return result; - } - - return ir_build_atomic_load_gen(ira, &instruction->base.base, casted_ptr, ordering, operand_type); -} - -static IrInstGen *ir_analyze_instruction_atomic_store(IrAnalyze *ira, IrInstSrcAtomicStore *instruction) { - ZigType *operand_type = ir_resolve_atomic_operand_type(ira, instruction->operand_type->child); - if (type_is_invalid(operand_type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *ptr_inst = instruction->ptr->child; - if (type_is_invalid(ptr_inst->value->type)) - return ira->codegen->invalid_inst_gen; - - ZigType *ptr_type = get_pointer_to_type(ira->codegen, operand_type, false); - IrInstGen *casted_ptr = ir_implicit_cast(ira, ptr_inst, ptr_type); - if (type_is_invalid(casted_ptr->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *value = instruction->value->child; - if (type_is_invalid(value->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *casted_value = ir_implicit_cast(ira, value, operand_type); - if (type_is_invalid(casted_value->value->type)) - return ira->codegen->invalid_inst_gen; - - - AtomicOrder ordering; - if (!ir_resolve_atomic_order(ira, instruction->ordering->child, &ordering)) - return ira->codegen->invalid_inst_gen; - - if (ordering == AtomicOrderAcquire || ordering == AtomicOrderAcqRel) { - ir_assert(instruction->ordering != nullptr, &instruction->base.base); - ir_add_error(ira, &instruction->ordering->base, - buf_sprintf("@atomicStore atomic ordering must not be Acquire or AcqRel")); - return ira->codegen->invalid_inst_gen; - } - - // special case zero bit types - switch (type_has_one_possible_value(ira->codegen, operand_type)) { - case OnePossibleValueInvalid: - return ira->codegen->invalid_inst_gen; - case OnePossibleValueYes: - return ir_const_void(ira, &instruction->base.base); - case OnePossibleValueNo: - break; - } - - if (instr_is_comptime(casted_value) && instr_is_comptime(casted_ptr)) { - IrInstGen *result = ir_analyze_store_ptr(ira, &instruction->base.base, casted_ptr, value, false); - result->value->type = ira->codegen->builtin_types.entry_void; - return result; - } - - return ir_build_atomic_store_gen(ira, &instruction->base.base, casted_ptr, casted_value, ordering); -} - -static IrInstGen *ir_analyze_instruction_save_err_ret_addr(IrAnalyze *ira, IrInstSrcSaveErrRetAddr *instruction) { - return ir_build_save_err_ret_addr_gen(ira, &instruction->base.base); -} - -static ErrorMsg *ir_eval_float_op(IrAnalyze *ira, IrInst* source_instr, BuiltinFnId fop, ZigType *float_type, - ZigValue *op, ZigValue *out_val) -{ - assert(ira && source_instr && float_type && out_val && op); - assert(float_type->id == ZigTypeIdFloat || - float_type->id == ZigTypeIdComptimeFloat); - - unsigned bits; - - switch (float_type->id) { - case ZigTypeIdComptimeFloat: - bits = 128; - break; - case ZigTypeIdFloat: - bits = float_type->data.floating.bit_count; - break; - default: - zig_unreachable(); - } - - switch (bits) { - case 16: { - switch (fop) { - case BuiltinFnIdSqrt: - out_val->data.x_f16 = f16_sqrt(op->data.x_f16); - break; - case BuiltinFnIdSin: - out_val->data.x_f16 = zig_double_to_f16(sin(zig_f16_to_double(op->data.x_f16))); - break; - case BuiltinFnIdCos: - out_val->data.x_f16 = zig_double_to_f16(cos(zig_f16_to_double(op->data.x_f16))); - break; - case BuiltinFnIdExp: - out_val->data.x_f16 = zig_double_to_f16(exp(zig_f16_to_double(op->data.x_f16))); - break; - case BuiltinFnIdExp2: - out_val->data.x_f16 = zig_double_to_f16(exp2(zig_f16_to_double(op->data.x_f16))); - break; - case BuiltinFnIdLog: - out_val->data.x_f16 = zig_double_to_f16(log(zig_f16_to_double(op->data.x_f16))); - break; - case BuiltinFnIdLog10: - out_val->data.x_f16 = zig_double_to_f16(log10(zig_f16_to_double(op->data.x_f16))); - break; - case BuiltinFnIdLog2: - out_val->data.x_f16 = zig_double_to_f16(log2(zig_f16_to_double(op->data.x_f16))); - break; - case BuiltinFnIdFabs: - out_val->data.x_f16 = zig_double_to_f16(fabs(zig_f16_to_double(op->data.x_f16))); - break; - case BuiltinFnIdFloor: - out_val->data.x_f16 = zig_double_to_f16(floor(zig_f16_to_double(op->data.x_f16))); - break; - case BuiltinFnIdCeil: - out_val->data.x_f16 = zig_double_to_f16(ceil(zig_f16_to_double(op->data.x_f16))); - break; - case BuiltinFnIdTrunc: - out_val->data.x_f16 = zig_double_to_f16(trunc(zig_f16_to_double(op->data.x_f16))); - break; - case BuiltinFnIdNearbyInt: - out_val->data.x_f16 = zig_double_to_f16(nearbyint(zig_f16_to_double(op->data.x_f16))); - break; - case BuiltinFnIdRound: - out_val->data.x_f16 = zig_double_to_f16(round(zig_f16_to_double(op->data.x_f16))); - break; - default: - zig_unreachable(); - }; - break; - } - case 32: { - switch (fop) { - case BuiltinFnIdSqrt: - out_val->data.x_f32 = sqrtf(op->data.x_f32); - break; - case BuiltinFnIdSin: - out_val->data.x_f32 = sinf(op->data.x_f32); - break; - case BuiltinFnIdCos: - out_val->data.x_f32 = cosf(op->data.x_f32); - break; - case BuiltinFnIdExp: - out_val->data.x_f32 = expf(op->data.x_f32); - break; - case BuiltinFnIdExp2: - out_val->data.x_f32 = exp2f(op->data.x_f32); - break; - case BuiltinFnIdLog: - out_val->data.x_f32 = logf(op->data.x_f32); - break; - case BuiltinFnIdLog10: - out_val->data.x_f32 = log10f(op->data.x_f32); - break; - case BuiltinFnIdLog2: - out_val->data.x_f32 = log2f(op->data.x_f32); - break; - case BuiltinFnIdFabs: - out_val->data.x_f32 = fabsf(op->data.x_f32); - break; - case BuiltinFnIdFloor: - out_val->data.x_f32 = floorf(op->data.x_f32); - break; - case BuiltinFnIdCeil: - out_val->data.x_f32 = ceilf(op->data.x_f32); - break; - case BuiltinFnIdTrunc: - out_val->data.x_f32 = truncf(op->data.x_f32); - break; - case BuiltinFnIdNearbyInt: - out_val->data.x_f32 = nearbyintf(op->data.x_f32); - break; - case BuiltinFnIdRound: - out_val->data.x_f32 = roundf(op->data.x_f32); - break; - default: - zig_unreachable(); - }; - break; - } - case 64: { - switch (fop) { - case BuiltinFnIdSqrt: - out_val->data.x_f64 = sqrt(op->data.x_f64); - break; - case BuiltinFnIdSin: - out_val->data.x_f64 = sin(op->data.x_f64); - break; - case BuiltinFnIdCos: - out_val->data.x_f64 = cos(op->data.x_f64); - break; - case BuiltinFnIdExp: - out_val->data.x_f64 = exp(op->data.x_f64); - break; - case BuiltinFnIdExp2: - out_val->data.x_f64 = exp2(op->data.x_f64); - break; - case BuiltinFnIdLog: - out_val->data.x_f64 = log(op->data.x_f64); - break; - case BuiltinFnIdLog10: - out_val->data.x_f64 = log10(op->data.x_f64); - break; - case BuiltinFnIdLog2: - out_val->data.x_f64 = log2(op->data.x_f64); - break; - case BuiltinFnIdFabs: - out_val->data.x_f64 = fabs(op->data.x_f64); - break; - case BuiltinFnIdFloor: - out_val->data.x_f64 = floor(op->data.x_f64); - break; - case BuiltinFnIdCeil: - out_val->data.x_f64 = ceil(op->data.x_f64); - break; - case BuiltinFnIdTrunc: - out_val->data.x_f64 = trunc(op->data.x_f64); - break; - case BuiltinFnIdNearbyInt: - out_val->data.x_f64 = nearbyint(op->data.x_f64); - break; - case BuiltinFnIdRound: - out_val->data.x_f64 = round(op->data.x_f64); - break; - default: - zig_unreachable(); - } - break; - } - case 80: - return ir_add_error(ira, source_instr, - buf_sprintf("compiler bug: TODO: implement '%s' for type '%s'. See https://github.com/ziglang/zig/issues/4026", - float_op_to_name(fop), buf_ptr(&float_type->name))); - case 128: { - float128_t *out, *in; - if (float_type->id == ZigTypeIdComptimeFloat) { - out = &out_val->data.x_bigfloat.value; - in = &op->data.x_bigfloat.value; - } else { - out = &out_val->data.x_f128; - in = &op->data.x_f128; - } - switch (fop) { - case BuiltinFnIdSqrt: - f128M_sqrt(in, out); - break; - case BuiltinFnIdFabs: - f128M_abs(in, out); - break; - case BuiltinFnIdFloor: - f128M_roundToInt(in, softfloat_round_min, false, out); - break; - case BuiltinFnIdCeil: - f128M_roundToInt(in, softfloat_round_max, false, out); - break; - case BuiltinFnIdTrunc: - f128M_trunc(in, out); - break; - case BuiltinFnIdRound: - f128M_roundToInt(in, softfloat_round_near_maxMag, false, out); - break; - case BuiltinFnIdNearbyInt: - case BuiltinFnIdSin: - case BuiltinFnIdCos: - case BuiltinFnIdExp: - case BuiltinFnIdExp2: - case BuiltinFnIdLog: - case BuiltinFnIdLog10: - case BuiltinFnIdLog2: - return ir_add_error(ira, source_instr, - buf_sprintf("compiler bug: TODO: implement '%s' for type '%s'. See https://github.com/ziglang/zig/issues/4026", - float_op_to_name(fop), buf_ptr(&float_type->name))); - default: - zig_unreachable(); - } - break; - } - default: - zig_unreachable(); - } - out_val->special = ConstValSpecialStatic; - return nullptr; -} - -static IrInstGen *ir_analyze_instruction_float_op(IrAnalyze *ira, IrInstSrcFloatOp *instruction) { - IrInstGen *operand = instruction->operand->child; - ZigType *operand_type = operand->value->type; - if (type_is_invalid(operand_type)) - return ira->codegen->invalid_inst_gen; - - // This instruction accepts floats and vectors of floats. - ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? - operand_type->data.vector.elem_type : operand_type; - - if (scalar_type->id != ZigTypeIdFloat && scalar_type->id != ZigTypeIdComptimeFloat) { - ir_add_error(ira, &operand->base, - buf_sprintf("expected float type, found '%s'", buf_ptr(&scalar_type->name))); - return ira->codegen->invalid_inst_gen; - } - - if (instr_is_comptime(operand)) { - ZigValue *operand_val = ir_resolve_const(ira, operand, UndefOk); - if (operand_val == nullptr) - return ira->codegen->invalid_inst_gen; - if (operand_val->special == ConstValSpecialUndef) - return ir_const_undef(ira, &instruction->base.base, operand_type); - - IrInstGen *result = ir_const(ira, &instruction->base.base, operand_type); - ZigValue *out_val = result->value; - - if (operand_type->id == ZigTypeIdVector) { - expand_undef_array(ira->codegen, operand_val); - out_val->special = ConstValSpecialUndef; - expand_undef_array(ira->codegen, out_val); - size_t len = operand_type->data.vector.len; - for (size_t i = 0; i < len; i += 1) { - ZigValue *elem_operand = &operand_val->data.x_array.data.s_none.elements[i]; - ZigValue *float_out_val = &out_val->data.x_array.data.s_none.elements[i]; - ir_assert(elem_operand->type == scalar_type, &instruction->base.base); - ir_assert(float_out_val->type == scalar_type, &instruction->base.base); - ErrorMsg *msg = ir_eval_float_op(ira, &instruction->base.base, instruction->fn_id, scalar_type, - elem_operand, float_out_val); - if (msg != nullptr) { - add_error_note(ira->codegen, msg, instruction->base.base.source_node, - buf_sprintf("when computing vector element at index %" ZIG_PRI_usize, i)); - return ira->codegen->invalid_inst_gen; - } - float_out_val->type = scalar_type; - } - out_val->type = operand_type; - out_val->special = ConstValSpecialStatic; - } else { - if (ir_eval_float_op(ira, &instruction->base.base, instruction->fn_id, scalar_type, - operand_val, out_val) != nullptr) - { - return ira->codegen->invalid_inst_gen; - } - } - return result; - } - - ir_assert(scalar_type->id == ZigTypeIdFloat, &instruction->base.base); - - return ir_build_float_op_gen(ira, &instruction->base.base, operand, instruction->fn_id, operand_type); -} - -static IrInstGen *ir_analyze_instruction_bswap(IrAnalyze *ira, IrInstSrcBswap *instruction) { - Error err; - - ZigType *int_type = ir_resolve_int_type(ira, instruction->type->child); - if (type_is_invalid(int_type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *uncasted_op = instruction->op->child; - if (type_is_invalid(uncasted_op->value->type)) - return ira->codegen->invalid_inst_gen; - - uint32_t vector_len = UINT32_MAX; // means not a vector - if (uncasted_op->value->type->id == ZigTypeIdArray) { - bool can_be_vec_elem; - if ((err = is_valid_vector_elem_type(ira->codegen, uncasted_op->value->type->data.array.child_type, - &can_be_vec_elem))) - { - return ira->codegen->invalid_inst_gen; - } - if (can_be_vec_elem) { - vector_len = uncasted_op->value->type->data.array.len; - } - } else if (uncasted_op->value->type->id == ZigTypeIdVector) { - vector_len = uncasted_op->value->type->data.vector.len; - } - - bool is_vector = (vector_len != UINT32_MAX); - ZigType *op_type = is_vector ? get_vector_type(ira->codegen, vector_len, int_type) : int_type; - - IrInstGen *op = ir_implicit_cast(ira, uncasted_op, op_type); - if (type_is_invalid(op->value->type)) - return ira->codegen->invalid_inst_gen; - - if (int_type->data.integral.bit_count == 8 || int_type->data.integral.bit_count == 0) - return op; - - if (int_type->data.integral.bit_count % 8 != 0) { - ir_add_error(ira, &instruction->op->base, - buf_sprintf("@byteSwap integer type '%s' has %" PRIu32 " bits which is not evenly divisible by 8", - buf_ptr(&int_type->name), int_type->data.integral.bit_count)); - return ira->codegen->invalid_inst_gen; - } - - if (instr_is_comptime(op)) { - ZigValue *val = ir_resolve_const(ira, op, UndefOk); - if (val == nullptr) - return ira->codegen->invalid_inst_gen; - if (val->special == ConstValSpecialUndef) - return ir_const_undef(ira, &instruction->base.base, op_type); - - IrInstGen *result = ir_const(ira, &instruction->base.base, op_type); - const size_t buf_size = int_type->data.integral.bit_count / 8; - uint8_t *buf = heap::c_allocator.allocate_nonzero(buf_size); - if (is_vector) { - expand_undef_array(ira->codegen, val); - result->value->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(op_type->data.vector.len); - for (unsigned i = 0; i < op_type->data.vector.len; i += 1) { - ZigValue *op_elem_val = &val->data.x_array.data.s_none.elements[i]; - if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, instruction->base.base.source_node, - op_elem_val, UndefOk))) - { - return ira->codegen->invalid_inst_gen; - } - ZigValue *result_elem_val = &result->value->data.x_array.data.s_none.elements[i]; - result_elem_val->type = int_type; - result_elem_val->special = op_elem_val->special; - if (op_elem_val->special == ConstValSpecialUndef) - continue; - - bigint_write_twos_complement(&op_elem_val->data.x_bigint, buf, int_type->data.integral.bit_count, true); - bigint_read_twos_complement(&result->value->data.x_array.data.s_none.elements[i].data.x_bigint, - buf, int_type->data.integral.bit_count, false, - int_type->data.integral.is_signed); - } - } else { - bigint_write_twos_complement(&val->data.x_bigint, buf, int_type->data.integral.bit_count, true); - bigint_read_twos_complement(&result->value->data.x_bigint, buf, int_type->data.integral.bit_count, false, - int_type->data.integral.is_signed); - } - heap::c_allocator.deallocate(buf, buf_size); - return result; - } - - return ir_build_bswap_gen(ira, &instruction->base.base, op_type, op); -} - -static IrInstGen *ir_analyze_instruction_bit_reverse(IrAnalyze *ira, IrInstSrcBitReverse *instruction) { - ZigType *int_type = ir_resolve_int_type(ira, instruction->type->child); - if (type_is_invalid(int_type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *op = ir_implicit_cast(ira, instruction->op->child, int_type); - if (type_is_invalid(op->value->type)) - return ira->codegen->invalid_inst_gen; - - if (int_type->data.integral.bit_count == 0) { - IrInstGen *result = ir_const(ira, &instruction->base.base, int_type); - bigint_init_unsigned(&result->value->data.x_bigint, 0); - return result; - } - - if (instr_is_comptime(op)) { - ZigValue *val = ir_resolve_const(ira, op, UndefOk); - if (val == nullptr) - return ira->codegen->invalid_inst_gen; - if (val->special == ConstValSpecialUndef) - return ir_const_undef(ira, &instruction->base.base, int_type); - - IrInstGen *result = ir_const(ira, &instruction->base.base, int_type); - size_t num_bits = int_type->data.integral.bit_count; - size_t buf_size = (num_bits + 7) / 8; - uint8_t *comptime_buf = heap::c_allocator.allocate_nonzero(buf_size); - uint8_t *result_buf = heap::c_allocator.allocate_nonzero(buf_size); - memset(comptime_buf,0,buf_size); - memset(result_buf,0,buf_size); - - bigint_write_twos_complement(&val->data.x_bigint,comptime_buf,num_bits,ira->codegen->is_big_endian); - - size_t bit_i = 0; - size_t bit_rev_i = num_bits - 1; - for (; bit_i < num_bits; bit_i++, bit_rev_i--) { - if (comptime_buf[bit_i / 8] & (1 << (bit_i % 8))) { - result_buf[bit_rev_i / 8] |= (1 << (bit_rev_i % 8)); - } - } - - bigint_read_twos_complement(&result->value->data.x_bigint, - result_buf, - int_type->data.integral.bit_count, - ira->codegen->is_big_endian, - int_type->data.integral.is_signed); - - return result; - } - - return ir_build_bit_reverse_gen(ira, &instruction->base.base, int_type, op); -} - - -static IrInstGen *ir_analyze_instruction_enum_to_int(IrAnalyze *ira, IrInstSrcEnumToInt *instruction) { - IrInstGen *target = instruction->target->child; - if (type_is_invalid(target->value->type)) - return ira->codegen->invalid_inst_gen; - - return ir_analyze_enum_to_int(ira, &instruction->base.base, target); -} - -static IrInstGen *ir_analyze_instruction_int_to_enum(IrAnalyze *ira, IrInstSrcIntToEnum *instruction) { - Error err; - IrInstGen *dest_type_value = instruction->dest_type->child; - ZigType *dest_type = ir_resolve_type(ira, dest_type_value); - if (type_is_invalid(dest_type)) - return ira->codegen->invalid_inst_gen; - - if (dest_type->id != ZigTypeIdEnum) { - ir_add_error(ira, &instruction->dest_type->base, - buf_sprintf("expected enum, found type '%s'", buf_ptr(&dest_type->name))); - return ira->codegen->invalid_inst_gen; - } - - if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusZeroBitsKnown))) - return ira->codegen->invalid_inst_gen; - - ZigType *tag_type = dest_type->data.enumeration.tag_int_type; - - IrInstGen *target = instruction->target->child; - if (type_is_invalid(target->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *casted_target = ir_implicit_cast(ira, target, tag_type); - if (type_is_invalid(casted_target->value->type)) - return ira->codegen->invalid_inst_gen; - - return ir_analyze_int_to_enum(ira, &instruction->base.base, casted_target, dest_type); -} - -static IrInstGen *ir_analyze_instruction_check_runtime_scope(IrAnalyze *ira, IrInstSrcCheckRuntimeScope *instruction) { - IrInstGen *block_comptime_inst = instruction->scope_is_comptime->child; - bool scope_is_comptime; - if (!ir_resolve_bool(ira, block_comptime_inst, &scope_is_comptime)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *is_comptime_inst = instruction->is_comptime->child; - bool is_comptime; - if (!ir_resolve_bool(ira, is_comptime_inst, &is_comptime)) - return ira->codegen->invalid_inst_gen; - - if (!scope_is_comptime && is_comptime) { - ErrorMsg *msg = ir_add_error(ira, &instruction->base.base, - buf_sprintf("comptime control flow inside runtime block")); - add_error_note(ira->codegen, msg, block_comptime_inst->base.source_node, - buf_sprintf("runtime block created here")); - return ira->codegen->invalid_inst_gen; - } - - return ir_const_void(ira, &instruction->base.base); -} - -static IrInstGen *ir_analyze_instruction_has_decl(IrAnalyze *ira, IrInstSrcHasDecl *instruction) { - ZigType *container_type = ir_resolve_type(ira, instruction->container->child); - if (type_is_invalid(container_type)) - return ira->codegen->invalid_inst_gen; - - Buf *name = ir_resolve_str(ira, instruction->name->child); - if (name == nullptr) - return ira->codegen->invalid_inst_gen; - - if (!is_container(container_type)) { - ir_add_error(ira, &instruction->container->base, - buf_sprintf("expected struct, enum, or union; found '%s'", buf_ptr(&container_type->name))); - return ira->codegen->invalid_inst_gen; - } - - ScopeDecls *container_scope = get_container_scope(container_type); - Tld *tld = find_container_decl(ira->codegen, container_scope, name); - if (tld == nullptr) - return ir_const_bool(ira, &instruction->base.base, false); - - if (tld->visib_mod == VisibModPrivate && tld->import != get_scope_import(instruction->base.base.scope)) { - return ir_const_bool(ira, &instruction->base.base, false); - } - - return ir_const_bool(ira, &instruction->base.base, true); -} - -static IrInstGen *ir_analyze_instruction_undeclared_ident(IrAnalyze *ira, IrInstSrcUndeclaredIdent *instruction) { - // put a variable of same name with invalid type in global scope - // so that future references to this same name will find a variable with an invalid type - populate_invalid_variable_in_scope(ira->codegen, instruction->base.base.scope, - instruction->base.base.source_node, instruction->name); - ir_add_error(ira, &instruction->base.base, - buf_sprintf("use of undeclared identifier '%s'", buf_ptr(instruction->name))); - return ira->codegen->invalid_inst_gen; -} - -static IrInstGen *ir_analyze_instruction_end_expr(IrAnalyze *ira, IrInstSrcEndExpr *instruction) { - IrInstGen *value = instruction->value->child; - if (type_is_invalid(value->value->type)) - return ira->codegen->invalid_inst_gen; - - bool was_written = instruction->result_loc->written; - IrInstGen *result_loc = ir_resolve_result(ira, &instruction->base.base, instruction->result_loc, - value->value->type, value, false, true); - if (result_loc != nullptr) { - if (type_is_invalid(result_loc->value->type)) - return ira->codegen->invalid_inst_gen; - if (result_loc->value->type->id == ZigTypeIdUnreachable) - return result_loc; - - if (!was_written || instruction->result_loc->id == ResultLocIdPeer) { - IrInstGen *store_ptr = ir_analyze_store_ptr(ira, &instruction->base.base, result_loc, value, - instruction->result_loc->allow_write_through_const); - if (type_is_invalid(store_ptr->value->type)) { - if (instruction->result_loc->id == ResultLocIdReturn && - (value->value->type->id == ZigTypeIdErrorUnion || value->value->type->id == ZigTypeIdErrorSet) && - ira->explicit_return_type->id != ZigTypeIdErrorUnion && ira->explicit_return_type->id != ZigTypeIdErrorSet) - { - add_error_note(ira->codegen, ira->new_irb.exec->first_err_trace_msg, - ira->explicit_return_type_source_node, buf_create_from_str("function cannot return an error")); - } - return ira->codegen->invalid_inst_gen; - } - } - - if (result_loc->value->data.x_ptr.mut == ConstPtrMutInfer && - instruction->result_loc->id != ResultLocIdPeer) - { - if (instr_is_comptime(value)) { - result_loc->value->data.x_ptr.mut = ConstPtrMutComptimeConst; - } else { - result_loc->value->special = ConstValSpecialRuntime; - } - } - } - - return ir_const_void(ira, &instruction->base.base); -} - -static IrInstGen *ir_analyze_instruction_implicit_cast(IrAnalyze *ira, IrInstSrcImplicitCast *instruction) { - IrInstGen *operand = instruction->operand->child; - if (type_is_invalid(operand->value->type)) - return operand; - - ZigType *dest_type = ir_resolve_type(ira, instruction->result_loc_cast->base.source_instruction->child); - if (type_is_invalid(dest_type)) - return ira->codegen->invalid_inst_gen; - return ir_implicit_cast2(ira, &instruction->base.base, operand, dest_type); -} - -static IrInstGen *ir_analyze_instruction_bit_cast_src(IrAnalyze *ira, IrInstSrcBitCast *instruction) { - IrInstGen *operand = instruction->operand->child; - if (type_is_invalid(operand->value->type)) - return operand; - - IrInstGen *result_loc = ir_resolve_result(ira, &instruction->base.base, - &instruction->result_loc_bit_cast->base, operand->value->type, operand, false, true); - if (result_loc != nullptr && - (type_is_invalid(result_loc->value->type) || result_loc->value->type->id == ZigTypeIdUnreachable)) - { - return result_loc; - } - - ZigType *dest_type = ir_resolve_type(ira, - instruction->result_loc_bit_cast->base.source_instruction->child); - if (type_is_invalid(dest_type)) - return ira->codegen->invalid_inst_gen; - return ir_analyze_bit_cast(ira, &instruction->base.base, operand, dest_type); -} - -static IrInstGen *ir_analyze_instruction_union_init_named_field(IrAnalyze *ira, - IrInstSrcUnionInitNamedField *instruction) -{ - ZigType *union_type = ir_resolve_type(ira, instruction->union_type->child); - if (type_is_invalid(union_type)) - return ira->codegen->invalid_inst_gen; - - if (union_type->id != ZigTypeIdUnion) { - ir_add_error(ira, &instruction->union_type->base, - buf_sprintf("non-union type '%s' passed to @unionInit", buf_ptr(&union_type->name))); - return ira->codegen->invalid_inst_gen; - } - - Buf *field_name = ir_resolve_str(ira, instruction->field_name->child); - if (field_name == nullptr) - return ira->codegen->invalid_inst_gen; - - IrInstGen *field_result_loc = instruction->field_result_loc->child; - if (type_is_invalid(field_result_loc->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *result_loc = instruction->result_loc->child; - if (type_is_invalid(result_loc->value->type)) - return ira->codegen->invalid_inst_gen; - - return ir_analyze_union_init(ira, &instruction->base.base, instruction->base.base.source_node, - union_type, field_name, field_result_loc, result_loc); -} - -static IrInstGen *ir_analyze_instruction_suspend_begin(IrAnalyze *ira, IrInstSrcSuspendBegin *instruction) { - return ir_build_suspend_begin_gen(ira, &instruction->base.base); -} - -static IrInstGen *ir_analyze_instruction_suspend_finish(IrAnalyze *ira, IrInstSrcSuspendFinish *instruction) { - IrInstGen *begin_base = instruction->begin->base.child; - if (type_is_invalid(begin_base->value->type)) - return ira->codegen->invalid_inst_gen; - ir_assert(begin_base->id == IrInstGenIdSuspendBegin, &instruction->base.base); - IrInstGenSuspendBegin *begin = reinterpret_cast(begin_base); - - ZigFn *fn_entry = ira->new_irb.exec->fn_entry; - ir_assert(fn_entry != nullptr, &instruction->base.base); - - if (fn_entry->inferred_async_node == nullptr) { - fn_entry->inferred_async_node = instruction->base.base.source_node; - } - - return ir_build_suspend_finish_gen(ira, &instruction->base.base, begin); -} - -static IrInstGen *analyze_frame_ptr_to_anyframe_T(IrAnalyze *ira, IrInst* source_instr, - IrInstGen *frame_ptr, ZigFn **target_fn) -{ - if (type_is_invalid(frame_ptr->value->type)) - return ira->codegen->invalid_inst_gen; - - *target_fn = nullptr; - - ZigType *result_type; - IrInstGen *frame; - if (frame_ptr->value->type->id == ZigTypeIdPointer && - frame_ptr->value->type->data.pointer.ptr_len == PtrLenSingle && - frame_ptr->value->type->data.pointer.child_type->id == ZigTypeIdFnFrame) - { - ZigFn *func = frame_ptr->value->type->data.pointer.child_type->data.frame.fn; - result_type = func->type_entry->data.fn.fn_type_id.return_type; - *target_fn = func; - frame = frame_ptr; - } else { - frame = ir_get_deref(ira, source_instr, frame_ptr, nullptr); - if (frame->value->type->id == ZigTypeIdPointer && - frame->value->type->data.pointer.ptr_len == PtrLenSingle && - frame->value->type->data.pointer.child_type->id == ZigTypeIdFnFrame) - { - ZigFn *func = frame->value->type->data.pointer.child_type->data.frame.fn; - result_type = func->type_entry->data.fn.fn_type_id.return_type; - *target_fn = func; - } else if (frame->value->type->id != ZigTypeIdAnyFrame || - frame->value->type->data.any_frame.result_type == nullptr) - { - ir_add_error(ira, source_instr, - buf_sprintf("expected anyframe->T, found '%s'", buf_ptr(&frame->value->type->name))); - return ira->codegen->invalid_inst_gen; - } else { - result_type = frame->value->type->data.any_frame.result_type; - } - } - - ZigType *any_frame_type = get_any_frame_type(ira->codegen, result_type); - IrInstGen *casted_frame = ir_implicit_cast(ira, frame, any_frame_type); - if (type_is_invalid(casted_frame->value->type)) - return ira->codegen->invalid_inst_gen; - - return casted_frame; -} - -static IrInstGen *ir_analyze_instruction_await(IrAnalyze *ira, IrInstSrcAwait *instruction) { - IrInstGen *operand = instruction->frame->child; - if (type_is_invalid(operand->value->type)) - return ira->codegen->invalid_inst_gen; - ZigFn *target_fn; - IrInstGen *frame = analyze_frame_ptr_to_anyframe_T(ira, &instruction->base.base, operand, &target_fn); - if (type_is_invalid(frame->value->type)) - return ira->codegen->invalid_inst_gen; - - ZigType *result_type = frame->value->type->data.any_frame.result_type; - - ZigFn *fn_entry = ira->new_irb.exec->fn_entry; - ir_assert(fn_entry != nullptr, &instruction->base.base); - - // If it's not @Frame(func) then it's definitely a suspend point - if (target_fn == nullptr && !instruction->is_nosuspend) { - if (fn_entry->inferred_async_node == nullptr) { - fn_entry->inferred_async_node = instruction->base.base.source_node; - } - } - - if (type_can_fail(result_type)) { - fn_entry->calls_or_awaits_errorable_fn = true; - } - - IrInstGen *result_loc; - if (type_has_bits(ira->codegen, result_type)) { - result_loc = ir_resolve_result(ira, &instruction->base.base, instruction->result_loc, - result_type, nullptr, true, true); - if (result_loc != nullptr && - (type_is_invalid(result_loc->value->type) || result_loc->value->type->id == ZigTypeIdUnreachable)) - { - return result_loc; - } - } else { - result_loc = nullptr; - } - - IrInstGenAwait *result = ir_build_await_gen(ira, &instruction->base.base, frame, result_type, result_loc, - instruction->is_nosuspend); - result->target_fn = target_fn; - fn_entry->await_list.append(result); - return ir_finish_anal(ira, &result->base); -} - -static IrInstGen *ir_analyze_instruction_resume(IrAnalyze *ira, IrInstSrcResume *instruction) { - IrInstGen *frame_ptr = instruction->frame->child; - if (type_is_invalid(frame_ptr->value->type)) - return ira->codegen->invalid_inst_gen; - - IrInstGen *frame; - if (frame_ptr->value->type->id == ZigTypeIdPointer && - frame_ptr->value->type->data.pointer.ptr_len == PtrLenSingle && - frame_ptr->value->type->data.pointer.child_type->id == ZigTypeIdFnFrame) - { - frame = frame_ptr; - } else { - frame = ir_get_deref(ira, &instruction->base.base, frame_ptr, nullptr); - } - - ZigType *any_frame_type = get_any_frame_type(ira->codegen, nullptr); - IrInstGen *casted_frame = ir_implicit_cast2(ira, &instruction->frame->base, frame, any_frame_type); - if (type_is_invalid(casted_frame->value->type)) - return ira->codegen->invalid_inst_gen; - - return ir_build_resume_gen(ira, &instruction->base.base, casted_frame); -} - -static IrInstGen *ir_analyze_instruction_spill_begin(IrAnalyze *ira, IrInstSrcSpillBegin *instruction) { - if (ir_should_inline(ira->old_irb.exec, instruction->base.base.scope)) - return ir_const_void(ira, &instruction->base.base); - - IrInstGen *operand = instruction->operand->child; - if (type_is_invalid(operand->value->type)) - return ira->codegen->invalid_inst_gen; - - if (!type_has_bits(ira->codegen, operand->value->type)) - return ir_const_void(ira, &instruction->base.base); - - switch (instruction->spill_id) { - case SpillIdInvalid: - zig_unreachable(); - case SpillIdRetErrCode: - ira->new_irb.exec->need_err_code_spill = true; - break; - } - - return ir_build_spill_begin_gen(ira, &instruction->base.base, operand, instruction->spill_id); -} - -static IrInstGen *ir_analyze_instruction_spill_end(IrAnalyze *ira, IrInstSrcSpillEnd *instruction) { - IrInstGen *operand = instruction->begin->operand->child; - if (type_is_invalid(operand->value->type)) - return ira->codegen->invalid_inst_gen; - - if (ir_should_inline(ira->old_irb.exec, instruction->base.base.scope) || - !type_has_bits(ira->codegen, operand->value->type) || - instr_is_comptime(operand)) - { - return operand; - } - - ir_assert(instruction->begin->base.child->id == IrInstGenIdSpillBegin, &instruction->base.base); - IrInstGenSpillBegin *begin = reinterpret_cast(instruction->begin->base.child); - - return ir_build_spill_end_gen(ira, &instruction->base.base, begin, operand->value->type); -} - -static IrInstGen *ir_analyze_instruction_src(IrAnalyze *ira, IrInstSrcSrc *instruction) { - ZigFn *fn_entry = scope_fn_entry(instruction->base.base.scope); - if (fn_entry == nullptr) { - ir_add_error(ira, &instruction->base.base, buf_sprintf("@src outside function")); - return ira->codegen->invalid_inst_gen; - } - - ZigType *u8_ptr = get_pointer_to_type_extra2( - ira->codegen, ira->codegen->builtin_types.entry_u8, - true, false, PtrLenUnknown, - 0, 0, 0, false, VECTOR_INDEX_NONE, nullptr, ira->codegen->intern.for_zero_byte()); - ZigType *u8_slice = get_slice_type(ira->codegen, u8_ptr); - - ZigType *source_location_type = get_builtin_type(ira->codegen, "SourceLocation"); - if (type_resolve(ira->codegen, source_location_type, ResolveStatusSizeKnown)) { - zig_unreachable(); - } - - ZigValue *result = ira->codegen->pass1_arena->create(); - result->special = ConstValSpecialStatic; - result->type = source_location_type; - - ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 4); - result->data.x_struct.fields = fields; - - // file: [:0]const u8 - ensure_field_index(source_location_type, "file", 0); - fields[0]->special = ConstValSpecialStatic; - - ZigType *import = instruction->base.base.source_node->owner; - Buf *path = import->data.structure.root_struct->path; - ZigValue *file_name = create_const_str_lit(ira->codegen, path)->data.x_ptr.data.ref.pointee; - init_const_slice(ira->codegen, fields[0], file_name, 0, buf_len(path), true); - fields[0]->type = u8_slice; - - // fn_name: [:0]const u8 - ensure_field_index(source_location_type, "fn_name", 1); - fields[1]->special = ConstValSpecialStatic; - - ZigValue *fn_name = create_const_str_lit(ira->codegen, &fn_entry->symbol_name)->data.x_ptr.data.ref.pointee; - init_const_slice(ira->codegen, fields[1], fn_name, 0, buf_len(&fn_entry->symbol_name), true); - fields[1]->type = u8_slice; - - // line: u32 - ensure_field_index(source_location_type, "line", 2); - fields[2]->special = ConstValSpecialStatic; - fields[2]->type = ira->codegen->builtin_types.entry_u32; - bigint_init_unsigned(&fields[2]->data.x_bigint, instruction->base.base.source_node->line + 1); - - // column: u32 - ensure_field_index(source_location_type, "column", 3); - fields[3]->special = ConstValSpecialStatic; - fields[3]->type = ira->codegen->builtin_types.entry_u32; - bigint_init_unsigned(&fields[3]->data.x_bigint, instruction->base.base.source_node->column + 1); - - return ir_const_move(ira, &instruction->base.base, result); -} - -static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruction) { - switch (instruction->id) { - case IrInstSrcIdInvalid: - zig_unreachable(); - - case IrInstSrcIdReturn: - return ir_analyze_instruction_return(ira, (IrInstSrcReturn *)instruction); - case IrInstSrcIdConst: - return ir_analyze_instruction_const(ira, (IrInstSrcConst *)instruction); - case IrInstSrcIdUnOp: - return ir_analyze_instruction_un_op(ira, (IrInstSrcUnOp *)instruction); - case IrInstSrcIdBinOp: - return ir_analyze_instruction_bin_op(ira, (IrInstSrcBinOp *)instruction); - case IrInstSrcIdMergeErrSets: - return ir_analyze_instruction_merge_err_sets(ira, (IrInstSrcMergeErrSets *)instruction); - case IrInstSrcIdDeclVar: - return ir_analyze_instruction_decl_var(ira, (IrInstSrcDeclVar *)instruction); - case IrInstSrcIdLoadPtr: - return ir_analyze_instruction_load_ptr(ira, (IrInstSrcLoadPtr *)instruction); - case IrInstSrcIdStorePtr: - return ir_analyze_instruction_store_ptr(ira, (IrInstSrcStorePtr *)instruction); - case IrInstSrcIdElemPtr: - return ir_analyze_instruction_elem_ptr(ira, (IrInstSrcElemPtr *)instruction); - case IrInstSrcIdVarPtr: - return ir_analyze_instruction_var_ptr(ira, (IrInstSrcVarPtr *)instruction); - case IrInstSrcIdFieldPtr: - return ir_analyze_instruction_field_ptr(ira, (IrInstSrcFieldPtr *)instruction); - case IrInstSrcIdCall: - return ir_analyze_instruction_call(ira, (IrInstSrcCall *)instruction); - case IrInstSrcIdCallArgs: - return ir_analyze_instruction_call_args(ira, (IrInstSrcCallArgs *)instruction); - case IrInstSrcIdCallExtra: - return ir_analyze_instruction_call_extra(ira, (IrInstSrcCallExtra *)instruction); - case IrInstSrcIdAsyncCallExtra: - return ir_analyze_instruction_async_call_extra(ira, (IrInstSrcAsyncCallExtra *)instruction); - case IrInstSrcIdBr: - return ir_analyze_instruction_br(ira, (IrInstSrcBr *)instruction); - case IrInstSrcIdCondBr: - return ir_analyze_instruction_cond_br(ira, (IrInstSrcCondBr *)instruction); - case IrInstSrcIdUnreachable: - return ir_analyze_instruction_unreachable(ira, (IrInstSrcUnreachable *)instruction); - case IrInstSrcIdPhi: - return ir_analyze_instruction_phi(ira, (IrInstSrcPhi *)instruction); - case IrInstSrcIdTypeOf: - return ir_analyze_instruction_typeof(ira, (IrInstSrcTypeOf *)instruction); - case IrInstSrcIdSetCold: - return ir_analyze_instruction_set_cold(ira, (IrInstSrcSetCold *)instruction); - case IrInstSrcIdSetRuntimeSafety: - return ir_analyze_instruction_set_runtime_safety(ira, (IrInstSrcSetRuntimeSafety *)instruction); - case IrInstSrcIdSetFloatMode: - return ir_analyze_instruction_set_float_mode(ira, (IrInstSrcSetFloatMode *)instruction); - case IrInstSrcIdAnyFrameType: - return ir_analyze_instruction_any_frame_type(ira, (IrInstSrcAnyFrameType *)instruction); - case IrInstSrcIdSliceType: - return ir_analyze_instruction_slice_type(ira, (IrInstSrcSliceType *)instruction); - case IrInstSrcIdAsm: - return ir_analyze_instruction_asm(ira, (IrInstSrcAsm *)instruction); - case IrInstSrcIdArrayType: - return ir_analyze_instruction_array_type(ira, (IrInstSrcArrayType *)instruction); - case IrInstSrcIdSizeOf: - return ir_analyze_instruction_size_of(ira, (IrInstSrcSizeOf *)instruction); - case IrInstSrcIdTestNonNull: - return ir_analyze_instruction_test_non_null(ira, (IrInstSrcTestNonNull *)instruction); - case IrInstSrcIdOptionalUnwrapPtr: - return ir_analyze_instruction_optional_unwrap_ptr(ira, (IrInstSrcOptionalUnwrapPtr *)instruction); - case IrInstSrcIdClz: - return ir_analyze_instruction_clz(ira, (IrInstSrcClz *)instruction); - case IrInstSrcIdCtz: - return ir_analyze_instruction_ctz(ira, (IrInstSrcCtz *)instruction); - case IrInstSrcIdPopCount: - return ir_analyze_instruction_pop_count(ira, (IrInstSrcPopCount *)instruction); - case IrInstSrcIdBswap: - return ir_analyze_instruction_bswap(ira, (IrInstSrcBswap *)instruction); - case IrInstSrcIdBitReverse: - return ir_analyze_instruction_bit_reverse(ira, (IrInstSrcBitReverse *)instruction); - case IrInstSrcIdSwitchBr: - return ir_analyze_instruction_switch_br(ira, (IrInstSrcSwitchBr *)instruction); - case IrInstSrcIdSwitchTarget: - return ir_analyze_instruction_switch_target(ira, (IrInstSrcSwitchTarget *)instruction); - case IrInstSrcIdSwitchVar: - return ir_analyze_instruction_switch_var(ira, (IrInstSrcSwitchVar *)instruction); - case IrInstSrcIdSwitchElseVar: - return ir_analyze_instruction_switch_else_var(ira, (IrInstSrcSwitchElseVar *)instruction); - case IrInstSrcIdImport: - return ir_analyze_instruction_import(ira, (IrInstSrcImport *)instruction); - case IrInstSrcIdRef: - return ir_analyze_instruction_ref(ira, (IrInstSrcRef *)instruction); - case IrInstSrcIdContainerInitList: - return ir_analyze_instruction_container_init_list(ira, (IrInstSrcContainerInitList *)instruction); - case IrInstSrcIdContainerInitFields: - return ir_analyze_instruction_container_init_fields(ira, (IrInstSrcContainerInitFields *)instruction); - case IrInstSrcIdCompileErr: - return ir_analyze_instruction_compile_err(ira, (IrInstSrcCompileErr *)instruction); - case IrInstSrcIdCompileLog: - return ir_analyze_instruction_compile_log(ira, (IrInstSrcCompileLog *)instruction); - case IrInstSrcIdErrName: - return ir_analyze_instruction_err_name(ira, (IrInstSrcErrName *)instruction); - case IrInstSrcIdTypeName: - return ir_analyze_instruction_type_name(ira, (IrInstSrcTypeName *)instruction); - case IrInstSrcIdCImport: - return ir_analyze_instruction_c_import(ira, (IrInstSrcCImport *)instruction); - case IrInstSrcIdCInclude: - return ir_analyze_instruction_c_include(ira, (IrInstSrcCInclude *)instruction); - case IrInstSrcIdCDefine: - return ir_analyze_instruction_c_define(ira, (IrInstSrcCDefine *)instruction); - case IrInstSrcIdCUndef: - return ir_analyze_instruction_c_undef(ira, (IrInstSrcCUndef *)instruction); - case IrInstSrcIdEmbedFile: - return ir_analyze_instruction_embed_file(ira, (IrInstSrcEmbedFile *)instruction); - case IrInstSrcIdCmpxchg: - return ir_analyze_instruction_cmpxchg(ira, (IrInstSrcCmpxchg *)instruction); - case IrInstSrcIdFence: - return ir_analyze_instruction_fence(ira, (IrInstSrcFence *)instruction); - case IrInstSrcIdTruncate: - return ir_analyze_instruction_truncate(ira, (IrInstSrcTruncate *)instruction); - case IrInstSrcIdIntCast: - return ir_analyze_instruction_int_cast(ira, (IrInstSrcIntCast *)instruction); - case IrInstSrcIdFloatCast: - return ir_analyze_instruction_float_cast(ira, (IrInstSrcFloatCast *)instruction); - case IrInstSrcIdErrSetCast: - return ir_analyze_instruction_err_set_cast(ira, (IrInstSrcErrSetCast *)instruction); - case IrInstSrcIdIntToFloat: - return ir_analyze_instruction_int_to_float(ira, (IrInstSrcIntToFloat *)instruction); - case IrInstSrcIdFloatToInt: - return ir_analyze_instruction_float_to_int(ira, (IrInstSrcFloatToInt *)instruction); - case IrInstSrcIdBoolToInt: - return ir_analyze_instruction_bool_to_int(ira, (IrInstSrcBoolToInt *)instruction); - case IrInstSrcIdVectorType: - return ir_analyze_instruction_vector_type(ira, (IrInstSrcVectorType *)instruction); - case IrInstSrcIdShuffleVector: - return ir_analyze_instruction_shuffle_vector(ira, (IrInstSrcShuffleVector *)instruction); - case IrInstSrcIdSplat: - return ir_analyze_instruction_splat(ira, (IrInstSrcSplat *)instruction); - case IrInstSrcIdBoolNot: - return ir_analyze_instruction_bool_not(ira, (IrInstSrcBoolNot *)instruction); - case IrInstSrcIdMemset: - return ir_analyze_instruction_memset(ira, (IrInstSrcMemset *)instruction); - case IrInstSrcIdMemcpy: - return ir_analyze_instruction_memcpy(ira, (IrInstSrcMemcpy *)instruction); - case IrInstSrcIdSlice: - return ir_analyze_instruction_slice(ira, (IrInstSrcSlice *)instruction); - case IrInstSrcIdBreakpoint: - return ir_analyze_instruction_breakpoint(ira, (IrInstSrcBreakpoint *)instruction); - case IrInstSrcIdReturnAddress: - return ir_analyze_instruction_return_address(ira, (IrInstSrcReturnAddress *)instruction); - case IrInstSrcIdFrameAddress: - return ir_analyze_instruction_frame_address(ira, (IrInstSrcFrameAddress *)instruction); - case IrInstSrcIdFrameHandle: - return ir_analyze_instruction_frame_handle(ira, (IrInstSrcFrameHandle *)instruction); - case IrInstSrcIdFrameType: - return ir_analyze_instruction_frame_type(ira, (IrInstSrcFrameType *)instruction); - case IrInstSrcIdFrameSize: - return ir_analyze_instruction_frame_size(ira, (IrInstSrcFrameSize *)instruction); - case IrInstSrcIdAlignOf: - return ir_analyze_instruction_align_of(ira, (IrInstSrcAlignOf *)instruction); - case IrInstSrcIdOverflowOp: - return ir_analyze_instruction_overflow_op(ira, (IrInstSrcOverflowOp *)instruction); - case IrInstSrcIdTestErr: - return ir_analyze_instruction_test_err(ira, (IrInstSrcTestErr *)instruction); - case IrInstSrcIdUnwrapErrCode: - return ir_analyze_instruction_unwrap_err_code(ira, (IrInstSrcUnwrapErrCode *)instruction); - case IrInstSrcIdUnwrapErrPayload: - return ir_analyze_instruction_unwrap_err_payload(ira, (IrInstSrcUnwrapErrPayload *)instruction); - case IrInstSrcIdFnProto: - return ir_analyze_instruction_fn_proto(ira, (IrInstSrcFnProto *)instruction); - case IrInstSrcIdTestComptime: - return ir_analyze_instruction_test_comptime(ira, (IrInstSrcTestComptime *)instruction); - case IrInstSrcIdCheckSwitchProngs: - return ir_analyze_instruction_check_switch_prongs(ira, (IrInstSrcCheckSwitchProngs *)instruction); - case IrInstSrcIdCheckStatementIsVoid: - return ir_analyze_instruction_check_statement_is_void(ira, (IrInstSrcCheckStatementIsVoid *)instruction); - case IrInstSrcIdDeclRef: - return ir_analyze_instruction_decl_ref(ira, (IrInstSrcDeclRef *)instruction); - case IrInstSrcIdPanic: - return ir_analyze_instruction_panic(ira, (IrInstSrcPanic *)instruction); - case IrInstSrcIdPtrCast: - return ir_analyze_instruction_ptr_cast(ira, (IrInstSrcPtrCast *)instruction); - case IrInstSrcIdIntToPtr: - return ir_analyze_instruction_int_to_ptr(ira, (IrInstSrcIntToPtr *)instruction); - case IrInstSrcIdPtrToInt: - return ir_analyze_instruction_ptr_to_int(ira, (IrInstSrcPtrToInt *)instruction); - case IrInstSrcIdTagName: - return ir_analyze_instruction_enum_tag_name(ira, (IrInstSrcTagName *)instruction); - case IrInstSrcIdFieldParentPtr: - return ir_analyze_instruction_field_parent_ptr(ira, (IrInstSrcFieldParentPtr *)instruction); - case IrInstSrcIdByteOffsetOf: - return ir_analyze_instruction_byte_offset_of(ira, (IrInstSrcByteOffsetOf *)instruction); - case IrInstSrcIdBitOffsetOf: - return ir_analyze_instruction_bit_offset_of(ira, (IrInstSrcBitOffsetOf *)instruction); - case IrInstSrcIdTypeInfo: - return ir_analyze_instruction_type_info(ira, (IrInstSrcTypeInfo *) instruction); - case IrInstSrcIdType: - return ir_analyze_instruction_type(ira, (IrInstSrcType *)instruction); - case IrInstSrcIdHasField: - return ir_analyze_instruction_has_field(ira, (IrInstSrcHasField *) instruction); - case IrInstSrcIdSetEvalBranchQuota: - return ir_analyze_instruction_set_eval_branch_quota(ira, (IrInstSrcSetEvalBranchQuota *)instruction); - case IrInstSrcIdPtrType: - return ir_analyze_instruction_ptr_type(ira, (IrInstSrcPtrType *)instruction); - case IrInstSrcIdAlignCast: - return ir_analyze_instruction_align_cast(ira, (IrInstSrcAlignCast *)instruction); - case IrInstSrcIdImplicitCast: - return ir_analyze_instruction_implicit_cast(ira, (IrInstSrcImplicitCast *)instruction); - case IrInstSrcIdResolveResult: - return ir_analyze_instruction_resolve_result(ira, (IrInstSrcResolveResult *)instruction); - case IrInstSrcIdResetResult: - return ir_analyze_instruction_reset_result(ira, (IrInstSrcResetResult *)instruction); - case IrInstSrcIdSetAlignStack: - return ir_analyze_instruction_set_align_stack(ira, (IrInstSrcSetAlignStack *)instruction); - case IrInstSrcIdArgType: - return ir_analyze_instruction_arg_type(ira, (IrInstSrcArgType *)instruction); - case IrInstSrcIdTagType: - return ir_analyze_instruction_tag_type(ira, (IrInstSrcTagType *)instruction); - case IrInstSrcIdExport: - return ir_analyze_instruction_export(ira, (IrInstSrcExport *)instruction); - case IrInstSrcIdErrorReturnTrace: - return ir_analyze_instruction_error_return_trace(ira, (IrInstSrcErrorReturnTrace *)instruction); - case IrInstSrcIdErrorUnion: - return ir_analyze_instruction_error_union(ira, (IrInstSrcErrorUnion *)instruction); - case IrInstSrcIdAtomicRmw: - return ir_analyze_instruction_atomic_rmw(ira, (IrInstSrcAtomicRmw *)instruction); - case IrInstSrcIdAtomicLoad: - return ir_analyze_instruction_atomic_load(ira, (IrInstSrcAtomicLoad *)instruction); - case IrInstSrcIdAtomicStore: - return ir_analyze_instruction_atomic_store(ira, (IrInstSrcAtomicStore *)instruction); - case IrInstSrcIdSaveErrRetAddr: - return ir_analyze_instruction_save_err_ret_addr(ira, (IrInstSrcSaveErrRetAddr *)instruction); - case IrInstSrcIdAddImplicitReturnType: - return ir_analyze_instruction_add_implicit_return_type(ira, (IrInstSrcAddImplicitReturnType *)instruction); - case IrInstSrcIdFloatOp: - return ir_analyze_instruction_float_op(ira, (IrInstSrcFloatOp *)instruction); - case IrInstSrcIdMulAdd: - return ir_analyze_instruction_mul_add(ira, (IrInstSrcMulAdd *)instruction); - case IrInstSrcIdIntToErr: - return ir_analyze_instruction_int_to_err(ira, (IrInstSrcIntToErr *)instruction); - case IrInstSrcIdErrToInt: - return ir_analyze_instruction_err_to_int(ira, (IrInstSrcErrToInt *)instruction); - case IrInstSrcIdIntToEnum: - return ir_analyze_instruction_int_to_enum(ira, (IrInstSrcIntToEnum *)instruction); - case IrInstSrcIdEnumToInt: - return ir_analyze_instruction_enum_to_int(ira, (IrInstSrcEnumToInt *)instruction); - case IrInstSrcIdCheckRuntimeScope: - return ir_analyze_instruction_check_runtime_scope(ira, (IrInstSrcCheckRuntimeScope *)instruction); - case IrInstSrcIdHasDecl: - return ir_analyze_instruction_has_decl(ira, (IrInstSrcHasDecl *)instruction); - case IrInstSrcIdUndeclaredIdent: - return ir_analyze_instruction_undeclared_ident(ira, (IrInstSrcUndeclaredIdent *)instruction); - case IrInstSrcIdAlloca: - return nullptr; - case IrInstSrcIdEndExpr: - return ir_analyze_instruction_end_expr(ira, (IrInstSrcEndExpr *)instruction); - case IrInstSrcIdBitCast: - return ir_analyze_instruction_bit_cast_src(ira, (IrInstSrcBitCast *)instruction); - case IrInstSrcIdUnionInitNamedField: - return ir_analyze_instruction_union_init_named_field(ira, (IrInstSrcUnionInitNamedField *)instruction); - case IrInstSrcIdSuspendBegin: - return ir_analyze_instruction_suspend_begin(ira, (IrInstSrcSuspendBegin *)instruction); - case IrInstSrcIdSuspendFinish: - return ir_analyze_instruction_suspend_finish(ira, (IrInstSrcSuspendFinish *)instruction); - case IrInstSrcIdResume: - return ir_analyze_instruction_resume(ira, (IrInstSrcResume *)instruction); - case IrInstSrcIdAwait: - return ir_analyze_instruction_await(ira, (IrInstSrcAwait *)instruction); - case IrInstSrcIdSpillBegin: - return ir_analyze_instruction_spill_begin(ira, (IrInstSrcSpillBegin *)instruction); - case IrInstSrcIdSpillEnd: - return ir_analyze_instruction_spill_end(ira, (IrInstSrcSpillEnd *)instruction); - case IrInstSrcIdWasmMemorySize: - return ir_analyze_instruction_wasm_memory_size(ira, (IrInstSrcWasmMemorySize *)instruction); - case IrInstSrcIdWasmMemoryGrow: - return ir_analyze_instruction_wasm_memory_grow(ira, (IrInstSrcWasmMemoryGrow *)instruction); - case IrInstSrcIdSrc: - return ir_analyze_instruction_src(ira, (IrInstSrcSrc *)instruction); - } - zig_unreachable(); -} - -// This function attempts to evaluate IR code while doing type checking and other analysis. -// It emits to a new IrExecutableGen which is partially evaluated IR code. -ZigType *ir_analyze(CodeGen *codegen, IrExecutableSrc *old_exec, IrExecutableGen *new_exec, - ZigType *expected_type, AstNode *expected_type_source_node, ZigValue *result_ptr) -{ - assert(old_exec->first_err_trace_msg == nullptr); - assert(expected_type == nullptr || !type_is_invalid(expected_type)); - - IrAnalyze *ira = heap::c_allocator.create(); - ira->ref_count = 1; - old_exec->analysis = ira; - ira->codegen = codegen; - - ira->explicit_return_type = expected_type; - ira->explicit_return_type_source_node = expected_type_source_node; - - ira->old_irb.codegen = codegen; - ira->old_irb.exec = old_exec; - - ira->new_irb.codegen = codegen; - ira->new_irb.exec = new_exec; - - IrBasicBlockSrc *old_entry_bb = ira->old_irb.exec->basic_block_list.at(0); - IrBasicBlockGen *new_entry_bb = ir_get_new_bb(ira, old_entry_bb, nullptr); - ira->new_irb.current_basic_block = new_entry_bb; - ira->old_bb_index = 0; - - ir_start_bb(ira, old_entry_bb, nullptr); - - if (result_ptr != nullptr) { - assert(result_ptr->type->id == ZigTypeIdPointer); - IrInstGenConst *const_inst = ir_create_inst_noval( - &ira->new_irb, new_exec->begin_scope, new_exec->source_node); - const_inst->base.value = result_ptr; - ira->return_ptr = &const_inst->base; - } else { - assert(new_exec->begin_scope != nullptr); - assert(new_exec->source_node != nullptr); - ira->return_ptr = ir_build_return_ptr(ira, new_exec->begin_scope, new_exec->source_node, - get_pointer_to_type(codegen, expected_type, false)); - } - - while (ira->old_bb_index < ira->old_irb.exec->basic_block_list.length) { - IrInstSrc *old_instruction = ira->old_irb.current_basic_block->instruction_list.at(ira->instruction_index); - - if (old_instruction->base.ref_count == 0 && !ir_inst_src_has_side_effects(old_instruction)) { - ira->instruction_index += 1; - continue; - } - - if (ira->codegen->verbose_ir) { - fprintf(stderr, "~ "); - old_instruction->src(); - fprintf(stderr, "~ "); - ir_print_inst_src(codegen, stderr, old_instruction, 0); - bool want_break = false; - if (ira->break_debug_id == old_instruction->base.debug_id) { - want_break = true; - } else if (old_instruction->base.source_node != nullptr) { - for (size_t i = 0; i < dbg_ir_breakpoints_count; i += 1) { - if (dbg_ir_breakpoints_buf[i].line == old_instruction->base.source_node->line + 1 && - buf_ends_with_str(old_instruction->base.source_node->owner->data.structure.root_struct->path, - dbg_ir_breakpoints_buf[i].src_file)) - { - want_break = true; - } - } - } - if (want_break) BREAKPOINT; - } - IrInstGen *new_instruction = ir_analyze_instruction_base(ira, old_instruction); - if (new_instruction != nullptr) { - ir_assert(new_instruction->value->type != nullptr || new_instruction->value->type != nullptr, &old_instruction->base); - old_instruction->child = new_instruction; - - if (type_is_invalid(new_instruction->value->type)) { - if (ira->codegen->verbose_ir) { - fprintf(stderr, "-> (invalid)"); - } - - if (new_exec->first_err_trace_msg != nullptr) { - ira->codegen->trace_err = new_exec->first_err_trace_msg; - } else { - new_exec->first_err_trace_msg = ira->codegen->trace_err; - } - if (new_exec->first_err_trace_msg != nullptr && - !old_instruction->base.source_node->already_traced_this_node) - { - old_instruction->base.source_node->already_traced_this_node = true; - new_exec->first_err_trace_msg = add_error_note(ira->codegen, new_exec->first_err_trace_msg, - old_instruction->base.source_node, buf_create_from_str("referenced here")); - } - return ira->codegen->builtin_types.entry_invalid; - } else if (ira->codegen->verbose_ir) { - fprintf(stderr, "-> "); - if (new_instruction->value->type->id == ZigTypeIdUnreachable) { - fprintf(stderr, "(noreturn)\n"); - } else { - ir_print_inst_gen(codegen, stderr, new_instruction, 0); - } - } - - // unreachable instructions do their own control flow. - if (new_instruction->value->type->id == ZigTypeIdUnreachable) - continue; - } else { - if (ira->codegen->verbose_ir) { - fprintf(stderr, "-> (null"); - } - } - - ira->instruction_index += 1; - } - - ZigType *res_type; - if (new_exec->first_err_trace_msg != nullptr) { - codegen->trace_err = new_exec->first_err_trace_msg; - if (codegen->trace_err != nullptr && new_exec->source_node != nullptr && - !new_exec->source_node->already_traced_this_node) - { - new_exec->source_node->already_traced_this_node = true; - codegen->trace_err = add_error_note(codegen, codegen->trace_err, - new_exec->source_node, buf_create_from_str("referenced here")); - } - res_type = ira->codegen->builtin_types.entry_invalid; - } else if (ira->src_implicit_return_type_list.length == 0) { - res_type = codegen->builtin_types.entry_unreachable; - } else { - res_type = ir_resolve_peer_types(ira, expected_type_source_node, expected_type, ira->src_implicit_return_type_list.items, - ira->src_implicit_return_type_list.length); - } - - // It is now safe to free Pass 1 IR instructions. - ira_deref(ira); - - return res_type; -} - -bool ir_inst_gen_has_side_effects(IrInstGen *instruction) { - switch (instruction->id) { - case IrInstGenIdInvalid: - zig_unreachable(); - case IrInstGenIdBr: - case IrInstGenIdCondBr: - case IrInstGenIdSwitchBr: - case IrInstGenIdDeclVar: - case IrInstGenIdStorePtr: - case IrInstGenIdVectorStoreElem: - case IrInstGenIdCall: - case IrInstGenIdReturn: - case IrInstGenIdUnreachable: - case IrInstGenIdFence: - case IrInstGenIdMemset: - case IrInstGenIdMemcpy: - case IrInstGenIdBreakpoint: - case IrInstGenIdOverflowOp: // TODO when we support multiple returns this can be side effect free - case IrInstGenIdPanic: - case IrInstGenIdSaveErrRetAddr: - case IrInstGenIdAtomicRmw: - case IrInstGenIdAtomicStore: - case IrInstGenIdCmpxchg: - case IrInstGenIdAssertZero: - case IrInstGenIdAssertNonNull: - case IrInstGenIdPtrOfArrayToSlice: - case IrInstGenIdSlice: - case IrInstGenIdOptionalWrap: - case IrInstGenIdVectorToArray: - case IrInstGenIdSuspendBegin: - case IrInstGenIdSuspendFinish: - case IrInstGenIdResume: - case IrInstGenIdAwait: - case IrInstGenIdSpillBegin: - case IrInstGenIdWasmMemoryGrow: - return true; - - case IrInstGenIdPhi: - case IrInstGenIdBinOp: - case IrInstGenIdConst: - case IrInstGenIdCast: - case IrInstGenIdElemPtr: - case IrInstGenIdVarPtr: - case IrInstGenIdReturnPtr: - case IrInstGenIdStructFieldPtr: - case IrInstGenIdTestNonNull: - case IrInstGenIdClz: - case IrInstGenIdCtz: - case IrInstGenIdPopCount: - case IrInstGenIdBswap: - case IrInstGenIdBitReverse: - case IrInstGenIdUnionTag: - case IrInstGenIdTruncate: - case IrInstGenIdShuffleVector: - case IrInstGenIdSplat: - case IrInstGenIdBoolNot: - case IrInstGenIdReturnAddress: - case IrInstGenIdFrameAddress: - case IrInstGenIdFrameHandle: - case IrInstGenIdFrameSize: - case IrInstGenIdTestErr: - case IrInstGenIdPtrCast: - case IrInstGenIdBitCast: - case IrInstGenIdWidenOrShorten: - case IrInstGenIdPtrToInt: - case IrInstGenIdIntToPtr: - case IrInstGenIdIntToEnum: - case IrInstGenIdIntToErr: - case IrInstGenIdErrToInt: - case IrInstGenIdErrName: - case IrInstGenIdTagName: - case IrInstGenIdFieldParentPtr: - case IrInstGenIdAlignCast: - case IrInstGenIdErrorReturnTrace: - case IrInstGenIdFloatOp: - case IrInstGenIdMulAdd: - case IrInstGenIdAtomicLoad: - case IrInstGenIdArrayToVector: - case IrInstGenIdAlloca: - case IrInstGenIdSpillEnd: - case IrInstGenIdVectorExtractElem: - case IrInstGenIdBinaryNot: - case IrInstGenIdNegation: - case IrInstGenIdNegationWrapping: - case IrInstGenIdWasmMemorySize: - return false; - - case IrInstGenIdAsm: - { - IrInstGenAsm *asm_instruction = (IrInstGenAsm *)instruction; - return asm_instruction->has_side_effects; - } - case IrInstGenIdUnwrapErrPayload: - { - IrInstGenUnwrapErrPayload *unwrap_err_payload_instruction = - (IrInstGenUnwrapErrPayload *)instruction; - return unwrap_err_payload_instruction->safety_check_on || - unwrap_err_payload_instruction->initializing; - } - case IrInstGenIdUnwrapErrCode: - return reinterpret_cast(instruction)->initializing; - case IrInstGenIdUnionFieldPtr: - return reinterpret_cast(instruction)->initializing; - case IrInstGenIdOptionalUnwrapPtr: - return reinterpret_cast(instruction)->initializing; - case IrInstGenIdErrWrapPayload: - return reinterpret_cast(instruction)->result_loc != nullptr; - case IrInstGenIdErrWrapCode: - return reinterpret_cast(instruction)->result_loc != nullptr; - case IrInstGenIdLoadPtr: - return reinterpret_cast(instruction)->result_loc != nullptr; - case IrInstGenIdRef: - return reinterpret_cast(instruction)->result_loc != nullptr; - } - zig_unreachable(); -} - -bool ir_inst_src_has_side_effects(IrInstSrc *instruction) { - switch (instruction->id) { - case IrInstSrcIdInvalid: - zig_unreachable(); - case IrInstSrcIdBr: - case IrInstSrcIdCondBr: - case IrInstSrcIdSwitchBr: - case IrInstSrcIdDeclVar: - case IrInstSrcIdStorePtr: - case IrInstSrcIdCallExtra: - case IrInstSrcIdAsyncCallExtra: - case IrInstSrcIdCall: - case IrInstSrcIdCallArgs: - case IrInstSrcIdReturn: - case IrInstSrcIdUnreachable: - case IrInstSrcIdSetCold: - case IrInstSrcIdSetRuntimeSafety: - case IrInstSrcIdSetFloatMode: - case IrInstSrcIdImport: - case IrInstSrcIdCompileErr: - case IrInstSrcIdCompileLog: - case IrInstSrcIdCImport: - case IrInstSrcIdCInclude: - case IrInstSrcIdCDefine: - case IrInstSrcIdCUndef: - case IrInstSrcIdFence: - case IrInstSrcIdMemset: - case IrInstSrcIdMemcpy: - case IrInstSrcIdBreakpoint: - case IrInstSrcIdOverflowOp: // TODO when we support multiple returns this can be side effect free - case IrInstSrcIdCheckSwitchProngs: - case IrInstSrcIdCheckStatementIsVoid: - case IrInstSrcIdCheckRuntimeScope: - case IrInstSrcIdPanic: - case IrInstSrcIdSetEvalBranchQuota: - case IrInstSrcIdPtrType: - case IrInstSrcIdSetAlignStack: - case IrInstSrcIdExport: - case IrInstSrcIdSaveErrRetAddr: - case IrInstSrcIdAddImplicitReturnType: - case IrInstSrcIdAtomicRmw: - case IrInstSrcIdAtomicStore: - case IrInstSrcIdCmpxchg: - case IrInstSrcIdUndeclaredIdent: - case IrInstSrcIdEndExpr: - case IrInstSrcIdResetResult: - case IrInstSrcIdSuspendBegin: - case IrInstSrcIdSuspendFinish: - case IrInstSrcIdResume: - case IrInstSrcIdAwait: - case IrInstSrcIdSpillBegin: - case IrInstSrcIdWasmMemoryGrow: - return true; - - case IrInstSrcIdPhi: - case IrInstSrcIdUnOp: - case IrInstSrcIdBinOp: - case IrInstSrcIdMergeErrSets: - case IrInstSrcIdLoadPtr: - case IrInstSrcIdConst: - case IrInstSrcIdContainerInitList: - case IrInstSrcIdContainerInitFields: - case IrInstSrcIdUnionInitNamedField: - case IrInstSrcIdFieldPtr: - case IrInstSrcIdElemPtr: - case IrInstSrcIdVarPtr: - case IrInstSrcIdTypeOf: - case IrInstSrcIdArrayType: - case IrInstSrcIdSliceType: - case IrInstSrcIdAnyFrameType: - case IrInstSrcIdSizeOf: - case IrInstSrcIdTestNonNull: - case IrInstSrcIdOptionalUnwrapPtr: - case IrInstSrcIdClz: - case IrInstSrcIdCtz: - case IrInstSrcIdPopCount: - case IrInstSrcIdBswap: - case IrInstSrcIdBitReverse: - case IrInstSrcIdSwitchVar: - case IrInstSrcIdSwitchElseVar: - case IrInstSrcIdSwitchTarget: - case IrInstSrcIdRef: - case IrInstSrcIdEmbedFile: - case IrInstSrcIdTruncate: - case IrInstSrcIdVectorType: - case IrInstSrcIdShuffleVector: - case IrInstSrcIdSplat: - case IrInstSrcIdBoolNot: - case IrInstSrcIdSlice: - case IrInstSrcIdAlignOf: - case IrInstSrcIdReturnAddress: - case IrInstSrcIdFrameAddress: - case IrInstSrcIdFrameHandle: - case IrInstSrcIdFrameType: - case IrInstSrcIdFrameSize: - case IrInstSrcIdTestErr: - case IrInstSrcIdFnProto: - case IrInstSrcIdTestComptime: - case IrInstSrcIdPtrCast: - case IrInstSrcIdBitCast: - case IrInstSrcIdPtrToInt: - case IrInstSrcIdIntToPtr: - case IrInstSrcIdIntToEnum: - case IrInstSrcIdIntToErr: - case IrInstSrcIdErrToInt: - case IrInstSrcIdDeclRef: - case IrInstSrcIdErrName: - case IrInstSrcIdTypeName: - case IrInstSrcIdTagName: - case IrInstSrcIdFieldParentPtr: - case IrInstSrcIdByteOffsetOf: - case IrInstSrcIdBitOffsetOf: - case IrInstSrcIdTypeInfo: - case IrInstSrcIdType: - case IrInstSrcIdHasField: - case IrInstSrcIdAlignCast: - case IrInstSrcIdImplicitCast: - case IrInstSrcIdResolveResult: - case IrInstSrcIdArgType: - case IrInstSrcIdTagType: - case IrInstSrcIdErrorReturnTrace: - case IrInstSrcIdErrorUnion: - case IrInstSrcIdFloatOp: - case IrInstSrcIdMulAdd: - case IrInstSrcIdAtomicLoad: - case IrInstSrcIdIntCast: - case IrInstSrcIdFloatCast: - case IrInstSrcIdErrSetCast: - case IrInstSrcIdIntToFloat: - case IrInstSrcIdFloatToInt: - case IrInstSrcIdBoolToInt: - case IrInstSrcIdEnumToInt: - case IrInstSrcIdHasDecl: - case IrInstSrcIdAlloca: - case IrInstSrcIdSpillEnd: - case IrInstSrcIdWasmMemorySize: - case IrInstSrcIdSrc: - return false; - - case IrInstSrcIdAsm: - { - IrInstSrcAsm *asm_instruction = (IrInstSrcAsm *)instruction; - return asm_instruction->has_side_effects; - } - - case IrInstSrcIdUnwrapErrPayload: - { - IrInstSrcUnwrapErrPayload *unwrap_err_payload_instruction = - (IrInstSrcUnwrapErrPayload *)instruction; - return unwrap_err_payload_instruction->safety_check_on || - unwrap_err_payload_instruction->initializing; - } - case IrInstSrcIdUnwrapErrCode: - return reinterpret_cast(instruction)->initializing; - } - zig_unreachable(); -} - -static ZigType *ir_resolve_lazy_fn_type(IrAnalyze *ira, AstNode *source_node, LazyValueFnType *lazy_fn_type) { - Error err; - AstNode *proto_node = lazy_fn_type->proto_node; - - FnTypeId fn_type_id = {0}; - init_fn_type_id(&fn_type_id, proto_node, lazy_fn_type->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 = proto_node->data.fn_proto.params.at(fn_type_id.next_param_index); - assert(param_node->type == NodeTypeParamDecl); - - bool param_is_var_args = param_node->data.param_decl.is_var_args; - if (param_is_var_args) { - if (fn_type_id.cc == CallingConventionC) { - fn_type_id.param_count = fn_type_id.next_param_index; - break; - } else { - ir_add_error_node(ira, param_node, - buf_sprintf("var args only allowed in functions with C calling convention")); - return nullptr; - } - } - FnTypeParamInfo *param_info = &fn_type_id.param_info[fn_type_id.next_param_index]; - param_info->is_noalias = param_node->data.param_decl.is_noalias; - - if (lazy_fn_type->param_types[fn_type_id.next_param_index] == nullptr) { - param_info->type = nullptr; - return get_generic_fn_type(ira->codegen, &fn_type_id); - } else { - IrInstGen *param_type_inst = lazy_fn_type->param_types[fn_type_id.next_param_index]; - ZigType *param_type = ir_resolve_type(ira, param_type_inst); - if (type_is_invalid(param_type)) - return nullptr; - - if(!is_valid_param_type(param_type)){ - if(param_type->id == ZigTypeIdOpaque){ - ir_add_error(ira, ¶m_type_inst->base, - buf_sprintf("parameter of opaque type '%s' not allowed", buf_ptr(¶m_type->name))); - } else { - ir_add_error(ira, ¶m_type_inst->base, - buf_sprintf("parameter of type '%s' not allowed", buf_ptr(¶m_type->name))); - } - - return nullptr; - } - - switch (type_requires_comptime(ira->codegen, param_type)) { - case ReqCompTimeYes: - if (!calling_convention_allows_zig_types(fn_type_id.cc)) { - ir_add_error(ira, ¶m_type_inst->base, - buf_sprintf("parameter of type '%s' not allowed in function with calling convention '%s'", - buf_ptr(¶m_type->name), calling_convention_name(fn_type_id.cc))); - return nullptr; - } - param_info->type = param_type; - fn_type_id.next_param_index += 1; - return get_generic_fn_type(ira->codegen, &fn_type_id); - case ReqCompTimeInvalid: - return nullptr; - case ReqCompTimeNo: - break; - } - if (!calling_convention_allows_zig_types(fn_type_id.cc)) { - bool has_bits; - if ((err = type_has_bits2(ira->codegen, param_type, &has_bits))) - return nullptr; - if (!has_bits) { - ir_add_error(ira, ¶m_type_inst->base, - buf_sprintf("parameter of type '%s' has 0 bits; not allowed in function with calling convention '%s'", - buf_ptr(¶m_type->name), calling_convention_name(fn_type_id.cc))); - return nullptr; - } - } - param_info->type = param_type; - } - } - - if (lazy_fn_type->align_inst != nullptr) { - if (!ir_resolve_align(ira, lazy_fn_type->align_inst, nullptr, &fn_type_id.alignment)) - return nullptr; - } - - fn_type_id.return_type = ir_resolve_type(ira, lazy_fn_type->return_type); - if (type_is_invalid(fn_type_id.return_type)) - return nullptr; - if (fn_type_id.return_type->id == ZigTypeIdOpaque) { - ir_add_error(ira, &lazy_fn_type->return_type->base, buf_create_from_str("return type cannot be opaque")); - return nullptr; - } - - return get_fn_type(ira->codegen, &fn_type_id); -} - -static Error ir_resolve_lazy_raw(AstNode *source_node, ZigValue *val) { - Error err; - if (val->special != ConstValSpecialLazy) - return ErrorNone; - switch (val->data.x_lazy->id) { - case LazyValueIdInvalid: - zig_unreachable(); - case LazyValueIdTypeInfoDecls: { - LazyValueTypeInfoDecls *type_info_decls = reinterpret_cast(val->data.x_lazy); - IrAnalyze *ira = type_info_decls->ira; - - if ((err = ir_make_type_info_decls(ira, type_info_decls->source_instr, val, type_info_decls->decls_scope, true))) - { - return err; - }; - - // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. - return ErrorNone; - } - case LazyValueIdAlignOf: { - LazyValueAlignOf *lazy_align_of = reinterpret_cast(val->data.x_lazy); - IrAnalyze *ira = lazy_align_of->ira; - - if (lazy_align_of->target_type->value->special == ConstValSpecialStatic) { - switch (lazy_align_of->target_type->value->data.x_type->id) { - case ZigTypeIdInvalid: - zig_unreachable(); - case ZigTypeIdMetaType: - case ZigTypeIdUnreachable: - case ZigTypeIdComptimeFloat: - case ZigTypeIdComptimeInt: - case ZigTypeIdEnumLiteral: - case ZigTypeIdUndefined: - case ZigTypeIdNull: - case ZigTypeIdBoundFn: - case ZigTypeIdVoid: - case ZigTypeIdOpaque: - ir_add_error(ira, &lazy_align_of->target_type->base, - buf_sprintf("no align available for type '%s'", - buf_ptr(&lazy_align_of->target_type->value->data.x_type->name))); - return ErrorSemanticAnalyzeFail; - 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 ZigTypeIdVector: - case ZigTypeIdFnFrame: - case ZigTypeIdAnyFrame: - break; - } - } - - uint32_t align_in_bytes; - if ((err = type_val_resolve_abi_align(ira->codegen, source_node, - lazy_align_of->target_type->value, &align_in_bytes))) - { - return err; - } - - val->special = ConstValSpecialStatic; - assert(val->type->id == ZigTypeIdComptimeInt || val->type->id == ZigTypeIdInt); - bigint_init_unsigned(&val->data.x_bigint, align_in_bytes); - - // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. - return ErrorNone; - } - case LazyValueIdSizeOf: { - LazyValueSizeOf *lazy_size_of = reinterpret_cast(val->data.x_lazy); - IrAnalyze *ira = lazy_size_of->ira; - - if (lazy_size_of->target_type->value->special == ConstValSpecialStatic) { - switch (lazy_size_of->target_type->value->data.x_type->id) { - case ZigTypeIdInvalid: // handled above - zig_unreachable(); - case ZigTypeIdUnreachable: - case ZigTypeIdUndefined: - case ZigTypeIdNull: - case ZigTypeIdBoundFn: - case ZigTypeIdOpaque: - ir_add_error(ira, &lazy_size_of->target_type->base, - buf_sprintf("no size available for type '%s'", - buf_ptr(&lazy_size_of->target_type->value->data.x_type->name))); - return ErrorSemanticAnalyzeFail; - case ZigTypeIdMetaType: - case ZigTypeIdEnumLiteral: - case ZigTypeIdComptimeFloat: - case ZigTypeIdComptimeInt: - 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 ZigTypeIdVector: - case ZigTypeIdFnFrame: - case ZigTypeIdAnyFrame: - break; - } - } - - size_t abi_size; - size_t size_in_bits; - if ((err = type_val_resolve_abi_size(ira->codegen, source_node, lazy_size_of->target_type->value, - &abi_size, &size_in_bits))) - { - return err; - } - - val->special = ConstValSpecialStatic; - assert(val->type->id == ZigTypeIdComptimeInt || val->type->id == ZigTypeIdInt); - if (lazy_size_of->bit_size) - bigint_init_unsigned(&val->data.x_bigint, size_in_bits); - else - bigint_init_unsigned(&val->data.x_bigint, abi_size); - - // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. - return ErrorNone; - } - case LazyValueIdSliceType: { - LazyValueSliceType *lazy_slice_type = reinterpret_cast(val->data.x_lazy); - IrAnalyze *ira = lazy_slice_type->ira; - - ZigType *elem_type = ir_resolve_type(ira, lazy_slice_type->elem_type); - if (type_is_invalid(elem_type)) - return ErrorSemanticAnalyzeFail; - - ZigValue *sentinel_val; - if (lazy_slice_type->sentinel != nullptr) { - if (type_is_invalid(lazy_slice_type->sentinel->value->type)) - return ErrorSemanticAnalyzeFail; - IrInstGen *sentinel = ir_implicit_cast(ira, lazy_slice_type->sentinel, elem_type); - if (type_is_invalid(sentinel->value->type)) - return ErrorSemanticAnalyzeFail; - sentinel_val = ir_resolve_const(ira, sentinel, UndefBad); - if (sentinel_val == nullptr) - return ErrorSemanticAnalyzeFail; - } else { - sentinel_val = nullptr; - } - - uint32_t align_bytes = 0; - if (lazy_slice_type->align_inst != nullptr) { - if (!ir_resolve_align(ira, lazy_slice_type->align_inst, elem_type, &align_bytes)) - return ErrorSemanticAnalyzeFail; - } - - switch (elem_type->id) { - case ZigTypeIdInvalid: // handled above - zig_unreachable(); - case ZigTypeIdUnreachable: - case ZigTypeIdUndefined: - case ZigTypeIdNull: - case ZigTypeIdOpaque: - ir_add_error(ira, &lazy_slice_type->elem_type->base, - buf_sprintf("slice of type '%s' not allowed", buf_ptr(&elem_type->name))); - return ErrorSemanticAnalyzeFail; - case ZigTypeIdMetaType: - case ZigTypeIdVoid: - case ZigTypeIdBool: - case ZigTypeIdInt: - case ZigTypeIdFloat: - case ZigTypeIdPointer: - case ZigTypeIdArray: - case ZigTypeIdStruct: - case ZigTypeIdComptimeFloat: - case ZigTypeIdComptimeInt: - case ZigTypeIdEnumLiteral: - case ZigTypeIdOptional: - case ZigTypeIdErrorUnion: - case ZigTypeIdErrorSet: - case ZigTypeIdEnum: - case ZigTypeIdUnion: - case ZigTypeIdFn: - case ZigTypeIdBoundFn: - case ZigTypeIdVector: - case ZigTypeIdFnFrame: - case ZigTypeIdAnyFrame: - break; - } - - ResolveStatus needed_status = (align_bytes == 0) ? - ResolveStatusZeroBitsKnown : ResolveStatusAlignmentKnown; - if ((err = type_resolve(ira->codegen, elem_type, needed_status))) - return err; - ZigType *slice_ptr_type = get_pointer_to_type_extra2(ira->codegen, elem_type, - lazy_slice_type->is_const, lazy_slice_type->is_volatile, - PtrLenUnknown, - align_bytes, - 0, 0, lazy_slice_type->is_allowzero, - VECTOR_INDEX_NONE, nullptr, sentinel_val); - val->special = ConstValSpecialStatic; - assert(val->type->id == ZigTypeIdMetaType); - val->data.x_type = get_slice_type(ira->codegen, slice_ptr_type); - - // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. - return ErrorNone; - } - case LazyValueIdPtrType: { - LazyValuePtrType *lazy_ptr_type = reinterpret_cast(val->data.x_lazy); - IrAnalyze *ira = lazy_ptr_type->ira; - - ZigType *elem_type = ir_resolve_type(ira, lazy_ptr_type->elem_type); - if (type_is_invalid(elem_type)) - return ErrorSemanticAnalyzeFail; - - ZigValue *sentinel_val; - if (lazy_ptr_type->sentinel != nullptr) { - if (type_is_invalid(lazy_ptr_type->sentinel->value->type)) - return ErrorSemanticAnalyzeFail; - IrInstGen *sentinel = ir_implicit_cast(ira, lazy_ptr_type->sentinel, elem_type); - if (type_is_invalid(sentinel->value->type)) - return ErrorSemanticAnalyzeFail; - sentinel_val = ir_resolve_const(ira, sentinel, UndefBad); - if (sentinel_val == nullptr) - return ErrorSemanticAnalyzeFail; - } else { - sentinel_val = nullptr; - } - - uint32_t align_bytes = 0; - if (lazy_ptr_type->align_inst != nullptr) { - if (!ir_resolve_align(ira, lazy_ptr_type->align_inst, elem_type, &align_bytes)) - return ErrorSemanticAnalyzeFail; - } - - if (elem_type->id == ZigTypeIdUnreachable) { - ir_add_error(ira, &lazy_ptr_type->elem_type->base, - buf_create_from_str("pointer to noreturn not allowed")); - return ErrorSemanticAnalyzeFail; - } else if (elem_type->id == ZigTypeIdOpaque && lazy_ptr_type->ptr_len == PtrLenUnknown) { - ir_add_error(ira, &lazy_ptr_type->elem_type->base, - buf_create_from_str("unknown-length pointer to opaque")); - return ErrorSemanticAnalyzeFail; - } else if (lazy_ptr_type->ptr_len == PtrLenC) { - bool ok_type; - if ((err = type_allowed_in_extern(ira->codegen, elem_type, &ok_type))) - return err; - if (!ok_type) { - ir_add_error(ira, &lazy_ptr_type->elem_type->base, - buf_sprintf("C pointers cannot point to non-C-ABI-compatible type '%s'", - buf_ptr(&elem_type->name))); - return ErrorSemanticAnalyzeFail; - } else if (elem_type->id == ZigTypeIdOpaque) { - ir_add_error(ira, &lazy_ptr_type->elem_type->base, - buf_sprintf("C pointers cannot point to opaque types")); - return ErrorSemanticAnalyzeFail; - } else if (lazy_ptr_type->is_allowzero) { - ir_add_error(ira, &lazy_ptr_type->elem_type->base, - buf_sprintf("C pointers always allow address zero")); - return ErrorSemanticAnalyzeFail; - } - } - - if (align_bytes != 0) { - if ((err = type_resolve(ira->codegen, elem_type, ResolveStatusAlignmentKnown))) - return err; - if (!type_has_bits(ira->codegen, elem_type)) - align_bytes = 0; - } - bool allow_zero = lazy_ptr_type->is_allowzero || lazy_ptr_type->ptr_len == PtrLenC; - assert(val->type->id == ZigTypeIdMetaType); - val->data.x_type = get_pointer_to_type_extra2(ira->codegen, elem_type, - lazy_ptr_type->is_const, lazy_ptr_type->is_volatile, lazy_ptr_type->ptr_len, align_bytes, - lazy_ptr_type->bit_offset_in_host, lazy_ptr_type->host_int_bytes, - allow_zero, VECTOR_INDEX_NONE, nullptr, sentinel_val); - val->special = ConstValSpecialStatic; - - // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. - return ErrorNone; - } - case LazyValueIdArrayType: { - LazyValueArrayType *lazy_array_type = reinterpret_cast(val->data.x_lazy); - IrAnalyze *ira = lazy_array_type->ira; - - ZigType *elem_type = ir_resolve_type(ira, lazy_array_type->elem_type); - if (type_is_invalid(elem_type)) - return ErrorSemanticAnalyzeFail; - - switch (elem_type->id) { - case ZigTypeIdInvalid: // handled above - zig_unreachable(); - case ZigTypeIdUnreachable: - case ZigTypeIdUndefined: - case ZigTypeIdNull: - case ZigTypeIdOpaque: - ir_add_error(ira, &lazy_array_type->elem_type->base, - buf_sprintf("array of type '%s' not allowed", - buf_ptr(&elem_type->name))); - return ErrorSemanticAnalyzeFail; - case ZigTypeIdMetaType: - case ZigTypeIdVoid: - case ZigTypeIdBool: - case ZigTypeIdInt: - case ZigTypeIdFloat: - case ZigTypeIdPointer: - case ZigTypeIdArray: - case ZigTypeIdStruct: - case ZigTypeIdComptimeFloat: - case ZigTypeIdComptimeInt: - case ZigTypeIdEnumLiteral: - case ZigTypeIdOptional: - case ZigTypeIdErrorUnion: - case ZigTypeIdErrorSet: - case ZigTypeIdEnum: - case ZigTypeIdUnion: - case ZigTypeIdFn: - case ZigTypeIdBoundFn: - case ZigTypeIdVector: - case ZigTypeIdFnFrame: - case ZigTypeIdAnyFrame: - break; - } - - if ((err = type_resolve(ira->codegen, elem_type, ResolveStatusSizeKnown))) - return err; - - ZigValue *sentinel_val = nullptr; - if (lazy_array_type->sentinel != nullptr) { - if (type_is_invalid(lazy_array_type->sentinel->value->type)) - return ErrorSemanticAnalyzeFail; - IrInstGen *sentinel = ir_implicit_cast(ira, lazy_array_type->sentinel, elem_type); - if (type_is_invalid(sentinel->value->type)) - return ErrorSemanticAnalyzeFail; - sentinel_val = ir_resolve_const(ira, sentinel, UndefBad); - if (sentinel_val == nullptr) - return ErrorSemanticAnalyzeFail; - } - - assert(val->type->id == ZigTypeIdMetaType); - val->data.x_type = get_array_type(ira->codegen, elem_type, lazy_array_type->length, sentinel_val); - val->special = ConstValSpecialStatic; - - // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. - return ErrorNone; - } - case LazyValueIdOptType: { - LazyValueOptType *lazy_opt_type = reinterpret_cast(val->data.x_lazy); - IrAnalyze *ira = lazy_opt_type->ira; - - ZigType *payload_type = ir_resolve_type(ira, lazy_opt_type->payload_type); - if (type_is_invalid(payload_type)) - return ErrorSemanticAnalyzeFail; - - if (payload_type->id == ZigTypeIdOpaque || payload_type->id == ZigTypeIdUnreachable) { - ir_add_error(ira, &lazy_opt_type->payload_type->base, - buf_sprintf("type '%s' cannot be optional", buf_ptr(&payload_type->name))); - return ErrorSemanticAnalyzeFail; - } - - if ((err = type_resolve(ira->codegen, payload_type, ResolveStatusSizeKnown))) - return err; - - assert(val->type->id == ZigTypeIdMetaType); - val->data.x_type = get_optional_type(ira->codegen, payload_type); - val->special = ConstValSpecialStatic; - - // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. - return ErrorNone; - } - case LazyValueIdFnType: { - LazyValueFnType *lazy_fn_type = reinterpret_cast(val->data.x_lazy); - IrAnalyze *ira = lazy_fn_type->ira; - ZigType *fn_type = ir_resolve_lazy_fn_type(ira, source_node, lazy_fn_type); - if (fn_type == nullptr) - return ErrorSemanticAnalyzeFail; - val->special = ConstValSpecialStatic; - assert(val->type->id == ZigTypeIdMetaType); - val->data.x_type = fn_type; - - // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. - return ErrorNone; - } - case LazyValueIdErrUnionType: { - LazyValueErrUnionType *lazy_err_union_type = - reinterpret_cast(val->data.x_lazy); - IrAnalyze *ira = lazy_err_union_type->ira; - - ZigType *err_set_type = ir_resolve_type(ira, lazy_err_union_type->err_set_type); - if (type_is_invalid(err_set_type)) - return ErrorSemanticAnalyzeFail; - - ZigType *payload_type = ir_resolve_type(ira, lazy_err_union_type->payload_type); - if (type_is_invalid(payload_type)) - return ErrorSemanticAnalyzeFail; - - if (err_set_type->id != ZigTypeIdErrorSet) { - ir_add_error(ira, &lazy_err_union_type->err_set_type->base, - buf_sprintf("expected error set type, found type '%s'", - buf_ptr(&err_set_type->name))); - return ErrorSemanticAnalyzeFail; - } - - if ((err = type_resolve(ira->codegen, payload_type, ResolveStatusSizeKnown))) - return ErrorSemanticAnalyzeFail; - - assert(val->type->id == ZigTypeIdMetaType); - val->data.x_type = get_error_union_type(ira->codegen, err_set_type, payload_type); - val->special = ConstValSpecialStatic; - - // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. - return ErrorNone; - } - } - zig_unreachable(); -} - -Error ir_resolve_lazy(CodeGen *codegen, AstNode *source_node, ZigValue *val) { - Error err; - if ((err = ir_resolve_lazy_raw(source_node, val))) { - if (codegen->trace_err != nullptr && source_node != nullptr && !source_node->already_traced_this_node) { - source_node->already_traced_this_node = true; - codegen->trace_err = add_error_note(codegen, codegen->trace_err, source_node, - buf_create_from_str("referenced here")); - } - return err; - } - if (type_is_invalid(val->type)) { - return ErrorSemanticAnalyzeFail; - } - return ErrorNone; -} - -void IrInst::src() { - IrInst *inst = this; - if (inst->source_node != nullptr) { - inst->source_node->src(); - } else { - fprintf(stderr, "(null source node)\n"); - } -} - -void IrInst::dump() { - this->src(); - fprintf(stderr, "IrInst(#%" PRIu32 ")\n", this->debug_id); -} - -void IrInstSrc::src() { - this->base.src(); -} - -void IrInstGen::src() { - this->base.src(); -} - -void IrInstSrc::dump() { - IrInstSrc *inst = this; - inst->src(); - if (inst->base.scope == nullptr) { - fprintf(stderr, "(null scope)\n"); - } else { - ir_print_inst_src(inst->base.scope->codegen, stderr, inst, 0); - fprintf(stderr, "-> "); - ir_print_inst_gen(inst->base.scope->codegen, stderr, inst->child, 0); - } -} -void IrInstGen::dump() { - IrInstGen *inst = this; - inst->src(); - if (inst->base.scope == nullptr) { - fprintf(stderr, "(null scope)\n"); - } else { - ir_print_inst_gen(inst->base.scope->codegen, stderr, inst, 0); - } -} - -void IrAnalyze::dump() { - ir_print_gen(this->codegen, stderr, this->new_irb.exec, 0); - if (this->new_irb.current_basic_block != nullptr) { - fprintf(stderr, "Current basic block:\n"); - ir_print_basic_block_gen(this->codegen, stderr, this->new_irb.current_basic_block, 1); - } -} - -void dbg_ir_break(const char *src_file, uint32_t line) { - dbg_ir_breakpoints_buf[dbg_ir_breakpoints_count] = {src_file, line}; - dbg_ir_breakpoints_count += 1; -} -void dbg_ir_clear(void) { - dbg_ir_breakpoints_count = 0; -} diff --git a/src/ir.hpp b/src/ir.hpp deleted file mode 100644 index 368677128754a5a4ce2ab91b40645410944b7610..0000000000000000000000000000000000000000 --- a/src/ir.hpp +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2016 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#ifndef ZIG_IR_HPP -#define ZIG_IR_HPP - -#include "all_types.hpp" - -bool ir_gen(CodeGen *g, AstNode *node, Scope *scope, IrExecutableSrc *ir_executable); -bool ir_gen_fn(CodeGen *g, ZigFn *fn_entry); - -IrInstGen *ir_create_alloca(CodeGen *g, Scope *scope, AstNode *source_node, ZigFn *fn, - ZigType *var_type, const char *name_hint); - -Error ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node, - ZigValue *return_ptr, size_t *backward_branch_count, size_t *backward_branch_quota, - ZigFn *fn_entry, Buf *c_import_buf, AstNode *source_node, Buf *exec_name, - IrExecutableGen *parent_exec, AstNode *expected_type_source_node, UndefAllowed undef); - -Error ir_resolve_lazy(CodeGen *codegen, AstNode *source_node, ZigValue *val); - -ZigType *ir_analyze(CodeGen *g, IrExecutableSrc *old_executable, IrExecutableGen *new_executable, - ZigType *expected_type, AstNode *expected_type_source_node, ZigValue *return_ptr); - -bool ir_inst_gen_has_side_effects(IrInstGen *inst); -bool ir_inst_src_has_side_effects(IrInstSrc *inst); - -struct IrAnalyze; -ZigValue *const_ptr_pointee(IrAnalyze *ira, CodeGen *codegen, ZigValue *const_val, - AstNode *source_node); - -// for debugging purposes -void dbg_ir_break(const char *src_file, uint32_t line); -void dbg_ir_clear(void); - -#endif diff --git a/src/ir.zig b/src/ir.zig new file mode 100644 index 0000000000000000000000000000000000000000..26afa52929e38592d1fd381cea25acc16115c315 --- /dev/null +++ b/src/ir.zig @@ -0,0 +1,465 @@ +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/ir_print.cpp b/src/ir_print.cpp deleted file mode 100644 index 18c2ca99f76dd40a77a7e5e994f51120a9a7e4ff..0000000000000000000000000000000000000000 --- a/src/ir_print.cpp +++ /dev/null @@ -1,3376 +0,0 @@ -/* - * Copyright (c) 2016 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#include "all_types.hpp" -#include "analyze.hpp" -#include "ir.hpp" -#include "ir_print.hpp" -#include "os.hpp" - -static uint32_t hash_inst_src_ptr(IrInstSrc* instruction) { - return (uint32_t)(uintptr_t)instruction; -} - -static uint32_t hash_inst_gen_ptr(IrInstGen* instruction) { - return (uint32_t)(uintptr_t)instruction; -} - -static bool inst_src_ptr_eql(IrInstSrc* a, IrInstSrc* b) { - return a == b; -} - -static bool inst_gen_ptr_eql(IrInstGen* a, IrInstGen* b) { - return a == b; -} - -using InstSetSrc = HashMap; -using InstSetGen = HashMap; -using InstListSrc = ZigList; -using InstListGen = ZigList; - -struct IrPrintSrc { - CodeGen *codegen; - FILE *f; - int indent; - int indent_size; -}; - -struct IrPrintGen { - CodeGen *codegen; - FILE *f; - int indent; - int indent_size; - - // When printing pass 2 instructions referenced var instructions are not - // present in the instruction list. Thus we track which instructions - // are printed (per executable) and after each pass 2 instruction those - // var instructions are rendered in a trailing fashion. - InstSetGen printed; - InstListGen pending; -}; - -static void ir_print_other_inst_src(IrPrintSrc *irp, IrInstSrc *inst); -static void ir_print_other_inst_gen(IrPrintGen *irp, IrInstGen *inst); - -static void ir_print_call_modifier(FILE *f, CallModifier modifier) { - switch (modifier) { - case CallModifierNone: - break; - case CallModifierNoSuspend: - fprintf(f, "nosuspend "); - break; - case CallModifierAsync: - fprintf(f, "async "); - break; - case CallModifierNeverTail: - fprintf(f, "notail "); - break; - case CallModifierNeverInline: - fprintf(f, "noinline "); - break; - case CallModifierAlwaysTail: - fprintf(f, "tail "); - break; - case CallModifierAlwaysInline: - fprintf(f, "inline "); - break; - case CallModifierCompileTime: - fprintf(f, "comptime "); - break; - case CallModifierBuiltin: - zig_unreachable(); - } -} - -const char* ir_inst_src_type_str(IrInstSrcId id) { - switch (id) { - case IrInstSrcIdInvalid: - return "SrcInvalid"; - case IrInstSrcIdShuffleVector: - return "SrcShuffle"; - case IrInstSrcIdSplat: - return "SrcSplat"; - case IrInstSrcIdDeclVar: - return "SrcDeclVar"; - case IrInstSrcIdBr: - return "SrcBr"; - case IrInstSrcIdCondBr: - return "SrcCondBr"; - case IrInstSrcIdSwitchBr: - return "SrcSwitchBr"; - case IrInstSrcIdSwitchVar: - return "SrcSwitchVar"; - case IrInstSrcIdSwitchElseVar: - return "SrcSwitchElseVar"; - case IrInstSrcIdSwitchTarget: - return "SrcSwitchTarget"; - case IrInstSrcIdPhi: - return "SrcPhi"; - case IrInstSrcIdUnOp: - return "SrcUnOp"; - case IrInstSrcIdBinOp: - return "SrcBinOp"; - case IrInstSrcIdMergeErrSets: - return "SrcMergeErrSets"; - case IrInstSrcIdLoadPtr: - return "SrcLoadPtr"; - case IrInstSrcIdStorePtr: - return "SrcStorePtr"; - case IrInstSrcIdFieldPtr: - return "SrcFieldPtr"; - case IrInstSrcIdElemPtr: - return "SrcElemPtr"; - case IrInstSrcIdVarPtr: - return "SrcVarPtr"; - case IrInstSrcIdCallExtra: - return "SrcCallExtra"; - case IrInstSrcIdAsyncCallExtra: - return "SrcAsyncCallExtra"; - case IrInstSrcIdCall: - return "SrcCall"; - case IrInstSrcIdCallArgs: - return "SrcCallArgs"; - case IrInstSrcIdConst: - return "SrcConst"; - case IrInstSrcIdReturn: - return "SrcReturn"; - case IrInstSrcIdContainerInitList: - return "SrcContainerInitList"; - case IrInstSrcIdContainerInitFields: - return "SrcContainerInitFields"; - case IrInstSrcIdUnreachable: - return "SrcUnreachable"; - case IrInstSrcIdTypeOf: - return "SrcTypeOf"; - case IrInstSrcIdSetCold: - return "SrcSetCold"; - case IrInstSrcIdSetRuntimeSafety: - return "SrcSetRuntimeSafety"; - case IrInstSrcIdSetFloatMode: - return "SrcSetFloatMode"; - case IrInstSrcIdArrayType: - return "SrcArrayType"; - case IrInstSrcIdAnyFrameType: - return "SrcAnyFrameType"; - case IrInstSrcIdSliceType: - return "SrcSliceType"; - case IrInstSrcIdAsm: - return "SrcAsm"; - case IrInstSrcIdSizeOf: - return "SrcSizeOf"; - case IrInstSrcIdTestNonNull: - return "SrcTestNonNull"; - case IrInstSrcIdOptionalUnwrapPtr: - return "SrcOptionalUnwrapPtr"; - case IrInstSrcIdClz: - return "SrcClz"; - case IrInstSrcIdCtz: - return "SrcCtz"; - case IrInstSrcIdPopCount: - return "SrcPopCount"; - case IrInstSrcIdBswap: - return "SrcBswap"; - case IrInstSrcIdBitReverse: - return "SrcBitReverse"; - case IrInstSrcIdImport: - return "SrcImport"; - case IrInstSrcIdCImport: - return "SrcCImport"; - case IrInstSrcIdCInclude: - return "SrcCInclude"; - case IrInstSrcIdCDefine: - return "SrcCDefine"; - case IrInstSrcIdCUndef: - return "SrcCUndef"; - case IrInstSrcIdRef: - return "SrcRef"; - case IrInstSrcIdCompileErr: - return "SrcCompileErr"; - case IrInstSrcIdCompileLog: - return "SrcCompileLog"; - case IrInstSrcIdErrName: - return "SrcErrName"; - case IrInstSrcIdEmbedFile: - return "SrcEmbedFile"; - case IrInstSrcIdCmpxchg: - return "SrcCmpxchg"; - case IrInstSrcIdFence: - return "SrcFence"; - case IrInstSrcIdTruncate: - return "SrcTruncate"; - case IrInstSrcIdIntCast: - return "SrcIntCast"; - case IrInstSrcIdFloatCast: - return "SrcFloatCast"; - case IrInstSrcIdIntToFloat: - return "SrcIntToFloat"; - case IrInstSrcIdFloatToInt: - return "SrcFloatToInt"; - case IrInstSrcIdBoolToInt: - return "SrcBoolToInt"; - case IrInstSrcIdVectorType: - return "SrcVectorType"; - case IrInstSrcIdBoolNot: - return "SrcBoolNot"; - case IrInstSrcIdMemset: - return "SrcMemset"; - case IrInstSrcIdMemcpy: - return "SrcMemcpy"; - case IrInstSrcIdSlice: - return "SrcSlice"; - case IrInstSrcIdBreakpoint: - return "SrcBreakpoint"; - case IrInstSrcIdReturnAddress: - return "SrcReturnAddress"; - case IrInstSrcIdFrameAddress: - return "SrcFrameAddress"; - case IrInstSrcIdFrameHandle: - return "SrcFrameHandle"; - case IrInstSrcIdFrameType: - return "SrcFrameType"; - case IrInstSrcIdFrameSize: - return "SrcFrameSize"; - case IrInstSrcIdAlignOf: - return "SrcAlignOf"; - case IrInstSrcIdOverflowOp: - return "SrcOverflowOp"; - case IrInstSrcIdTestErr: - return "SrcTestErr"; - case IrInstSrcIdMulAdd: - return "SrcMulAdd"; - case IrInstSrcIdFloatOp: - return "SrcFloatOp"; - case IrInstSrcIdUnwrapErrCode: - return "SrcUnwrapErrCode"; - case IrInstSrcIdUnwrapErrPayload: - return "SrcUnwrapErrPayload"; - case IrInstSrcIdFnProto: - return "SrcFnProto"; - case IrInstSrcIdTestComptime: - return "SrcTestComptime"; - case IrInstSrcIdPtrCast: - return "SrcPtrCast"; - case IrInstSrcIdBitCast: - return "SrcBitCast"; - case IrInstSrcIdIntToPtr: - return "SrcIntToPtr"; - case IrInstSrcIdPtrToInt: - return "SrcPtrToInt"; - case IrInstSrcIdIntToEnum: - return "SrcIntToEnum"; - case IrInstSrcIdEnumToInt: - return "SrcEnumToInt"; - case IrInstSrcIdIntToErr: - return "SrcIntToErr"; - case IrInstSrcIdErrToInt: - return "SrcErrToInt"; - case IrInstSrcIdCheckSwitchProngs: - return "SrcCheckSwitchProngs"; - case IrInstSrcIdCheckStatementIsVoid: - return "SrcCheckStatementIsVoid"; - case IrInstSrcIdTypeName: - return "SrcTypeName"; - case IrInstSrcIdDeclRef: - return "SrcDeclRef"; - case IrInstSrcIdPanic: - return "SrcPanic"; - case IrInstSrcIdTagName: - return "SrcTagName"; - case IrInstSrcIdTagType: - return "SrcTagType"; - case IrInstSrcIdFieldParentPtr: - return "SrcFieldParentPtr"; - case IrInstSrcIdByteOffsetOf: - return "SrcByteOffsetOf"; - case IrInstSrcIdBitOffsetOf: - return "SrcBitOffsetOf"; - case IrInstSrcIdTypeInfo: - return "SrcTypeInfo"; - case IrInstSrcIdType: - return "SrcType"; - case IrInstSrcIdHasField: - return "SrcHasField"; - case IrInstSrcIdSetEvalBranchQuota: - return "SrcSetEvalBranchQuota"; - case IrInstSrcIdPtrType: - return "SrcPtrType"; - case IrInstSrcIdAlignCast: - return "SrcAlignCast"; - case IrInstSrcIdImplicitCast: - return "SrcImplicitCast"; - case IrInstSrcIdResolveResult: - return "SrcResolveResult"; - case IrInstSrcIdResetResult: - return "SrcResetResult"; - case IrInstSrcIdSetAlignStack: - return "SrcSetAlignStack"; - case IrInstSrcIdArgType: - return "SrcArgType"; - case IrInstSrcIdExport: - return "SrcExport"; - case IrInstSrcIdErrorReturnTrace: - return "SrcErrorReturnTrace"; - case IrInstSrcIdErrorUnion: - return "SrcErrorUnion"; - case IrInstSrcIdAtomicRmw: - return "SrcAtomicRmw"; - case IrInstSrcIdAtomicLoad: - return "SrcAtomicLoad"; - case IrInstSrcIdAtomicStore: - return "SrcAtomicStore"; - case IrInstSrcIdSaveErrRetAddr: - return "SrcSaveErrRetAddr"; - case IrInstSrcIdAddImplicitReturnType: - return "SrcAddImplicitReturnType"; - case IrInstSrcIdErrSetCast: - return "SrcErrSetCast"; - case IrInstSrcIdCheckRuntimeScope: - return "SrcCheckRuntimeScope"; - case IrInstSrcIdHasDecl: - return "SrcHasDecl"; - case IrInstSrcIdUndeclaredIdent: - return "SrcUndeclaredIdent"; - case IrInstSrcIdAlloca: - return "SrcAlloca"; - case IrInstSrcIdEndExpr: - return "SrcEndExpr"; - case IrInstSrcIdUnionInitNamedField: - return "SrcUnionInitNamedField"; - case IrInstSrcIdSuspendBegin: - return "SrcSuspendBegin"; - case IrInstSrcIdSuspendFinish: - return "SrcSuspendFinish"; - case IrInstSrcIdAwait: - return "SrcAwaitSr"; - case IrInstSrcIdResume: - return "SrcResume"; - case IrInstSrcIdSpillBegin: - return "SrcSpillBegin"; - case IrInstSrcIdSpillEnd: - return "SrcSpillEnd"; - case IrInstSrcIdWasmMemorySize: - return "SrcWasmMemorySize"; - case IrInstSrcIdWasmMemoryGrow: - return "SrcWasmMemoryGrow"; - case IrInstSrcIdSrc: - return "SrcSrc"; - } - zig_unreachable(); -} - -const char* ir_inst_gen_type_str(IrInstGenId id) { - switch (id) { - case IrInstGenIdInvalid: - return "GenInvalid"; - case IrInstGenIdShuffleVector: - return "GenShuffle"; - case IrInstGenIdSplat: - return "GenSplat"; - case IrInstGenIdDeclVar: - return "GenDeclVar"; - case IrInstGenIdBr: - return "GenBr"; - case IrInstGenIdCondBr: - return "GenCondBr"; - case IrInstGenIdSwitchBr: - return "GenSwitchBr"; - case IrInstGenIdPhi: - return "GenPhi"; - case IrInstGenIdBinOp: - return "GenBinOp"; - case IrInstGenIdLoadPtr: - return "GenLoadPtr"; - case IrInstGenIdStorePtr: - return "GenStorePtr"; - case IrInstGenIdVectorStoreElem: - return "GenVectorStoreElem"; - case IrInstGenIdStructFieldPtr: - return "GenStructFieldPtr"; - case IrInstGenIdUnionFieldPtr: - return "GenUnionFieldPtr"; - case IrInstGenIdElemPtr: - return "GenElemPtr"; - case IrInstGenIdVarPtr: - return "GenVarPtr"; - case IrInstGenIdReturnPtr: - return "GenReturnPtr"; - case IrInstGenIdCall: - return "GenCall"; - case IrInstGenIdConst: - return "GenConst"; - case IrInstGenIdReturn: - return "GenReturn"; - case IrInstGenIdCast: - return "GenCast"; - case IrInstGenIdUnreachable: - return "GenUnreachable"; - case IrInstGenIdAsm: - return "GenAsm"; - case IrInstGenIdTestNonNull: - return "GenTestNonNull"; - case IrInstGenIdOptionalUnwrapPtr: - return "GenOptionalUnwrapPtr"; - case IrInstGenIdOptionalWrap: - return "GenOptionalWrap"; - case IrInstGenIdUnionTag: - return "GenUnionTag"; - case IrInstGenIdClz: - return "GenClz"; - case IrInstGenIdCtz: - return "GenCtz"; - case IrInstGenIdPopCount: - return "GenPopCount"; - case IrInstGenIdBswap: - return "GenBswap"; - case IrInstGenIdBitReverse: - return "GenBitReverse"; - case IrInstGenIdRef: - return "GenRef"; - case IrInstGenIdErrName: - return "GenErrName"; - case IrInstGenIdCmpxchg: - return "GenCmpxchg"; - case IrInstGenIdFence: - return "GenFence"; - case IrInstGenIdTruncate: - return "GenTruncate"; - case IrInstGenIdBoolNot: - return "GenBoolNot"; - case IrInstGenIdMemset: - return "GenMemset"; - case IrInstGenIdMemcpy: - return "GenMemcpy"; - case IrInstGenIdSlice: - return "GenSlice"; - case IrInstGenIdBreakpoint: - return "GenBreakpoint"; - case IrInstGenIdReturnAddress: - return "GenReturnAddress"; - case IrInstGenIdFrameAddress: - return "GenFrameAddress"; - case IrInstGenIdFrameHandle: - return "GenFrameHandle"; - case IrInstGenIdFrameSize: - return "GenFrameSize"; - case IrInstGenIdOverflowOp: - return "GenOverflowOp"; - case IrInstGenIdTestErr: - return "GenTestErr"; - case IrInstGenIdMulAdd: - return "GenMulAdd"; - case IrInstGenIdFloatOp: - return "GenFloatOp"; - case IrInstGenIdUnwrapErrCode: - return "GenUnwrapErrCode"; - case IrInstGenIdUnwrapErrPayload: - return "GenUnwrapErrPayload"; - case IrInstGenIdErrWrapCode: - return "GenErrWrapCode"; - case IrInstGenIdErrWrapPayload: - return "GenErrWrapPayload"; - case IrInstGenIdPtrCast: - return "GenPtrCast"; - case IrInstGenIdBitCast: - return "GenBitCast"; - case IrInstGenIdWidenOrShorten: - return "GenWidenOrShorten"; - case IrInstGenIdIntToPtr: - return "GenIntToPtr"; - case IrInstGenIdPtrToInt: - return "GenPtrToInt"; - case IrInstGenIdIntToEnum: - return "GenIntToEnum"; - case IrInstGenIdIntToErr: - return "GenIntToErr"; - case IrInstGenIdErrToInt: - return "GenErrToInt"; - case IrInstGenIdPanic: - return "GenPanic"; - case IrInstGenIdTagName: - return "GenTagName"; - case IrInstGenIdFieldParentPtr: - return "GenFieldParentPtr"; - case IrInstGenIdAlignCast: - return "GenAlignCast"; - case IrInstGenIdErrorReturnTrace: - return "GenErrorReturnTrace"; - case IrInstGenIdAtomicRmw: - return "GenAtomicRmw"; - case IrInstGenIdAtomicLoad: - return "GenAtomicLoad"; - case IrInstGenIdAtomicStore: - return "GenAtomicStore"; - case IrInstGenIdSaveErrRetAddr: - return "GenSaveErrRetAddr"; - case IrInstGenIdVectorToArray: - return "GenVectorToArray"; - case IrInstGenIdArrayToVector: - return "GenArrayToVector"; - case IrInstGenIdAssertZero: - return "GenAssertZero"; - case IrInstGenIdAssertNonNull: - return "GenAssertNonNull"; - case IrInstGenIdAlloca: - return "GenAlloca"; - case IrInstGenIdPtrOfArrayToSlice: - return "GenPtrOfArrayToSlice"; - case IrInstGenIdSuspendBegin: - return "GenSuspendBegin"; - case IrInstGenIdSuspendFinish: - return "GenSuspendFinish"; - case IrInstGenIdAwait: - return "GenAwait"; - case IrInstGenIdResume: - return "GenResume"; - case IrInstGenIdSpillBegin: - return "GenSpillBegin"; - case IrInstGenIdSpillEnd: - return "GenSpillEnd"; - case IrInstGenIdVectorExtractElem: - return "GenVectorExtractElem"; - case IrInstGenIdBinaryNot: - return "GenBinaryNot"; - case IrInstGenIdNegation: - return "GenNegation"; - case IrInstGenIdNegationWrapping: - return "GenNegationWrapping"; - case IrInstGenIdWasmMemorySize: - return "GenWasmMemorySize"; - case IrInstGenIdWasmMemoryGrow: - return "GenWasmMemoryGrow"; - } - zig_unreachable(); -} - -static void ir_print_indent_src(IrPrintSrc *irp) { - for (int i = 0; i < irp->indent; i += 1) { - fprintf(irp->f, " "); - } -} - -static void ir_print_indent_gen(IrPrintGen *irp) { - for (int i = 0; i < irp->indent; i += 1) { - fprintf(irp->f, " "); - } -} - -static void ir_print_prefix_src(IrPrintSrc *irp, IrInstSrc *instruction, bool trailing) { - ir_print_indent_src(irp); - const char mark = trailing ? ':' : '#'; - const char *type_name; - if (instruction->id == IrInstSrcIdConst) { - type_name = buf_ptr(&reinterpret_cast(instruction)->value->type->name); - } else if (instruction->is_noreturn) { - type_name = "noreturn"; - } else { - type_name = "(unknown)"; - } - const char *ref_count = ir_inst_src_has_side_effects(instruction) ? - "-" : buf_ptr(buf_sprintf("%" PRIu32 "", instruction->base.ref_count)); - fprintf(irp->f, "%c%-3" PRIu32 "| %-22s| %-12s| %-2s| ", mark, instruction->base.debug_id, - ir_inst_src_type_str(instruction->id), type_name, ref_count); -} - -static void ir_print_prefix_gen(IrPrintGen *irp, IrInstGen *instruction, bool trailing) { - ir_print_indent_gen(irp); - const char mark = trailing ? ':' : '#'; - const char *type_name = instruction->value->type ? buf_ptr(&instruction->value->type->name) : "(unknown)"; - const char *ref_count = ir_inst_gen_has_side_effects(instruction) ? - "-" : buf_ptr(buf_sprintf("%" PRIu32 "", instruction->base.ref_count)); - fprintf(irp->f, "%c%-3" PRIu32 "| %-22s| %-12s| %-2s| ", mark, instruction->base.debug_id, - ir_inst_gen_type_str(instruction->id), type_name, ref_count); -} - -static void ir_print_var_src(IrPrintSrc *irp, IrInstSrc *inst) { - fprintf(irp->f, "#%" PRIu32 "", inst->base.debug_id); -} - -static void ir_print_var_gen(IrPrintGen *irp, IrInstGen *inst) { - fprintf(irp->f, "#%" PRIu32 "", inst->base.debug_id); - if (irp->printed.maybe_get(inst) == nullptr) { - irp->printed.put(inst, 0); - irp->pending.append(inst); - } -} - -static void ir_print_other_inst_src(IrPrintSrc *irp, IrInstSrc *inst) { - if (inst == nullptr) { - fprintf(irp->f, "(null)"); - return; - } - ir_print_var_src(irp, inst); -} - -static void ir_print_const_value(CodeGen *g, FILE *f, ZigValue *const_val) { - Buf buf = BUF_INIT; - buf_resize(&buf, 0); - render_const_value(g, &buf, const_val); - fprintf(f, "%s", buf_ptr(&buf)); -} - -static void ir_print_other_inst_gen(IrPrintGen *irp, IrInstGen *inst) { - if (inst == nullptr) { - fprintf(irp->f, "(null)"); - } else { - ir_print_var_gen(irp, inst); - } -} - -static void ir_print_other_block(IrPrintSrc *irp, IrBasicBlockSrc *bb) { - if (bb == nullptr) { - fprintf(irp->f, "(null block)"); - } else { - fprintf(irp->f, "$%s_%" PRIu32 "", bb->name_hint, bb->debug_id); - } -} - -static void ir_print_other_block_gen(IrPrintGen *irp, IrBasicBlockGen *bb) { - if (bb == nullptr) { - fprintf(irp->f, "(null block)"); - } else { - fprintf(irp->f, "$%s_%" PRIu32 "", bb->name_hint, bb->debug_id); - } -} - -static void ir_print_return_src(IrPrintSrc *irp, IrInstSrcReturn *inst) { - fprintf(irp->f, "return "); - ir_print_other_inst_src(irp, inst->operand); -} - -static void ir_print_return_gen(IrPrintGen *irp, IrInstGenReturn *inst) { - fprintf(irp->f, "return "); - ir_print_other_inst_gen(irp, inst->operand); -} - -static void ir_print_const(IrPrintSrc *irp, IrInstSrcConst *const_instruction) { - ir_print_const_value(irp->codegen, irp->f, const_instruction->value); -} - -static void ir_print_const(IrPrintGen *irp, IrInstGenConst *const_instruction) { - ir_print_const_value(irp->codegen, irp->f, const_instruction->base.value); -} - -static const char *ir_bin_op_id_str(IrBinOp op_id) { - switch (op_id) { - case IrBinOpInvalid: - zig_unreachable(); - case IrBinOpBoolOr: - return "BoolOr"; - case IrBinOpBoolAnd: - return "BoolAnd"; - case IrBinOpCmpEq: - return "=="; - case IrBinOpCmpNotEq: - return "!="; - case IrBinOpCmpLessThan: - return "<"; - case IrBinOpCmpGreaterThan: - return ">"; - case IrBinOpCmpLessOrEq: - return "<="; - case IrBinOpCmpGreaterOrEq: - return ">="; - case IrBinOpBinOr: - return "|"; - case IrBinOpBinXor: - return "^"; - case IrBinOpBinAnd: - return "&"; - case IrBinOpBitShiftLeftLossy: - return "<<"; - case IrBinOpBitShiftLeftExact: - return "@shlExact"; - case IrBinOpBitShiftRightLossy: - return ">>"; - case IrBinOpBitShiftRightExact: - return "@shrExact"; - case IrBinOpAdd: - return "+"; - case IrBinOpAddWrap: - return "+%"; - case IrBinOpSub: - return "-"; - case IrBinOpSubWrap: - return "-%"; - case IrBinOpMult: - return "*"; - case IrBinOpMultWrap: - return "*%"; - case IrBinOpDivUnspecified: - return "/"; - case IrBinOpDivTrunc: - return "@divTrunc"; - case IrBinOpDivFloor: - return "@divFloor"; - case IrBinOpDivExact: - return "@divExact"; - case IrBinOpRemUnspecified: - return "%"; - case IrBinOpRemRem: - return "@rem"; - case IrBinOpRemMod: - return "@mod"; - case IrBinOpArrayCat: - return "++"; - case IrBinOpArrayMult: - return "**"; - } - zig_unreachable(); -} - -static const char *ir_un_op_id_str(IrUnOp op_id) { - switch (op_id) { - case IrUnOpInvalid: - zig_unreachable(); - case IrUnOpBinNot: - return "~"; - case IrUnOpNegation: - return "-"; - case IrUnOpNegationWrap: - return "-%"; - case IrUnOpDereference: - return "*"; - case IrUnOpOptional: - return "?"; - } - zig_unreachable(); -} - -static void ir_print_un_op(IrPrintSrc *irp, IrInstSrcUnOp *inst) { - fprintf(irp->f, "%s ", ir_un_op_id_str(inst->op_id)); - ir_print_other_inst_src(irp, inst->value); -} - -static void ir_print_bin_op(IrPrintSrc *irp, IrInstSrcBinOp *bin_op_instruction) { - ir_print_other_inst_src(irp, bin_op_instruction->op1); - fprintf(irp->f, " %s ", ir_bin_op_id_str(bin_op_instruction->op_id)); - ir_print_other_inst_src(irp, bin_op_instruction->op2); - if (!bin_op_instruction->safety_check_on) { - fprintf(irp->f, " // no safety"); - } -} - -static void ir_print_bin_op(IrPrintGen *irp, IrInstGenBinOp *bin_op_instruction) { - ir_print_other_inst_gen(irp, bin_op_instruction->op1); - fprintf(irp->f, " %s ", ir_bin_op_id_str(bin_op_instruction->op_id)); - ir_print_other_inst_gen(irp, bin_op_instruction->op2); - if (!bin_op_instruction->safety_check_on) { - fprintf(irp->f, " // no safety"); - } -} - -static void ir_print_merge_err_sets(IrPrintSrc *irp, IrInstSrcMergeErrSets *instruction) { - ir_print_other_inst_src(irp, instruction->op1); - fprintf(irp->f, " || "); - ir_print_other_inst_src(irp, instruction->op2); - if (instruction->type_name != nullptr) { - fprintf(irp->f, " // name=%s", buf_ptr(instruction->type_name)); - } -} - -static void ir_print_decl_var_src(IrPrintSrc *irp, IrInstSrcDeclVar *decl_var_instruction) { - const char *var_or_const = decl_var_instruction->var->gen_is_const ? "const" : "var"; - const char *name = decl_var_instruction->var->name; - if (decl_var_instruction->var_type) { - fprintf(irp->f, "%s %s: ", var_or_const, name); - ir_print_other_inst_src(irp, decl_var_instruction->var_type); - fprintf(irp->f, " "); - } else { - fprintf(irp->f, "%s %s ", var_or_const, name); - } - if (decl_var_instruction->align_value) { - fprintf(irp->f, "align "); - ir_print_other_inst_src(irp, decl_var_instruction->align_value); - fprintf(irp->f, " "); - } - fprintf(irp->f, "= "); - ir_print_other_inst_src(irp, decl_var_instruction->ptr); - if (decl_var_instruction->var->is_comptime != nullptr) { - fprintf(irp->f, " // comptime = "); - ir_print_other_inst_src(irp, decl_var_instruction->var->is_comptime); - } -} - -static const char *cast_op_str(CastOp op) { - switch (op) { - case CastOpNoCast: return "NoCast"; - case CastOpNoop: return "NoOp"; - case CastOpIntToFloat: return "IntToFloat"; - case CastOpFloatToInt: return "FloatToInt"; - case CastOpBoolToInt: return "BoolToInt"; - case CastOpNumLitToConcrete: return "NumLitToConcrate"; - case CastOpErrSet: return "ErrSet"; - case CastOpBitCast: return "BitCast"; - } - zig_unreachable(); -} - -static void ir_print_cast(IrPrintGen *irp, IrInstGenCast *cast_instruction) { - fprintf(irp->f, "%s cast ", cast_op_str(cast_instruction->cast_op)); - ir_print_other_inst_gen(irp, cast_instruction->value); -} - -static void ir_print_result_loc_var(IrPrintSrc *irp, ResultLocVar *result_loc_var) { - fprintf(irp->f, "var("); - ir_print_other_inst_src(irp, result_loc_var->base.source_instruction); - fprintf(irp->f, ")"); -} - -static void ir_print_result_loc_instruction(IrPrintSrc *irp, ResultLocInstruction *result_loc_inst) { - fprintf(irp->f, "inst("); - ir_print_other_inst_src(irp, result_loc_inst->base.source_instruction); - fprintf(irp->f, ")"); -} - -static void ir_print_result_loc_peer(IrPrintSrc *irp, ResultLocPeer *result_loc_peer) { - fprintf(irp->f, "peer(next="); - ir_print_other_block(irp, result_loc_peer->next_bb); - fprintf(irp->f, ")"); -} - -static void ir_print_result_loc_bit_cast(IrPrintSrc *irp, ResultLocBitCast *result_loc_bit_cast) { - fprintf(irp->f, "bitcast(ty="); - ir_print_other_inst_src(irp, result_loc_bit_cast->base.source_instruction); - fprintf(irp->f, ")"); -} - -static void ir_print_result_loc_cast(IrPrintSrc *irp, ResultLocCast *result_loc_cast) { - fprintf(irp->f, "cast(ty="); - ir_print_other_inst_src(irp, result_loc_cast->base.source_instruction); - fprintf(irp->f, ")"); -} - -static void ir_print_result_loc(IrPrintSrc *irp, ResultLoc *result_loc) { - switch (result_loc->id) { - case ResultLocIdInvalid: - zig_unreachable(); - case ResultLocIdNone: - fprintf(irp->f, "none"); - return; - case ResultLocIdReturn: - fprintf(irp->f, "return"); - return; - case ResultLocIdVar: - return ir_print_result_loc_var(irp, (ResultLocVar *)result_loc); - case ResultLocIdInstruction: - return ir_print_result_loc_instruction(irp, (ResultLocInstruction *)result_loc); - case ResultLocIdPeer: - return ir_print_result_loc_peer(irp, (ResultLocPeer *)result_loc); - case ResultLocIdBitCast: - return ir_print_result_loc_bit_cast(irp, (ResultLocBitCast *)result_loc); - case ResultLocIdCast: - return ir_print_result_loc_cast(irp, (ResultLocCast *)result_loc); - case ResultLocIdPeerParent: - fprintf(irp->f, "peer_parent"); - return; - } - zig_unreachable(); -} - -static void ir_print_call_extra(IrPrintSrc *irp, IrInstSrcCallExtra *instruction) { - fprintf(irp->f, "opts="); - ir_print_other_inst_src(irp, instruction->options); - fprintf(irp->f, ", fn="); - ir_print_other_inst_src(irp, instruction->fn_ref); - fprintf(irp->f, ", args="); - ir_print_other_inst_src(irp, instruction->args); - fprintf(irp->f, ", result="); - ir_print_result_loc(irp, instruction->result_loc); -} - -static void ir_print_async_call_extra(IrPrintSrc *irp, IrInstSrcAsyncCallExtra *instruction) { - fprintf(irp->f, "modifier="); - ir_print_call_modifier(irp->f, instruction->modifier); - fprintf(irp->f, ", fn="); - ir_print_other_inst_src(irp, instruction->fn_ref); - if (instruction->ret_ptr != nullptr) { - fprintf(irp->f, ", ret_ptr="); - ir_print_other_inst_src(irp, instruction->ret_ptr); - } - fprintf(irp->f, ", new_stack="); - ir_print_other_inst_src(irp, instruction->new_stack); - fprintf(irp->f, ", args="); - ir_print_other_inst_src(irp, instruction->args); - fprintf(irp->f, ", result="); - ir_print_result_loc(irp, instruction->result_loc); -} - -static void ir_print_call_args(IrPrintSrc *irp, IrInstSrcCallArgs *instruction) { - fprintf(irp->f, "opts="); - ir_print_other_inst_src(irp, instruction->options); - fprintf(irp->f, ", fn="); - ir_print_other_inst_src(irp, instruction->fn_ref); - fprintf(irp->f, ", args=("); - for (size_t i = 0; i < instruction->args_len; i += 1) { - IrInstSrc *arg = instruction->args_ptr[i]; - if (i != 0) - fprintf(irp->f, ", "); - ir_print_other_inst_src(irp, arg); - } - fprintf(irp->f, "), result="); - ir_print_result_loc(irp, instruction->result_loc); -} - -static void ir_print_call_src(IrPrintSrc *irp, IrInstSrcCall *call_instruction) { - ir_print_call_modifier(irp->f, call_instruction->modifier); - if (call_instruction->fn_entry) { - fprintf(irp->f, "%s", buf_ptr(&call_instruction->fn_entry->symbol_name)); - } else { - assert(call_instruction->fn_ref); - ir_print_other_inst_src(irp, call_instruction->fn_ref); - } - fprintf(irp->f, "("); - for (size_t i = 0; i < call_instruction->arg_count; i += 1) { - IrInstSrc *arg = call_instruction->args[i]; - if (i != 0) - fprintf(irp->f, ", "); - ir_print_other_inst_src(irp, arg); - } - fprintf(irp->f, ")result="); - ir_print_result_loc(irp, call_instruction->result_loc); -} - -static void ir_print_call_gen(IrPrintGen *irp, IrInstGenCall *call_instruction) { - ir_print_call_modifier(irp->f, call_instruction->modifier); - if (call_instruction->fn_entry) { - fprintf(irp->f, "%s", buf_ptr(&call_instruction->fn_entry->symbol_name)); - } else { - assert(call_instruction->fn_ref); - ir_print_other_inst_gen(irp, call_instruction->fn_ref); - } - fprintf(irp->f, "("); - for (size_t i = 0; i < call_instruction->arg_count; i += 1) { - IrInstGen *arg = call_instruction->args[i]; - if (i != 0) - fprintf(irp->f, ", "); - ir_print_other_inst_gen(irp, arg); - } - fprintf(irp->f, ")result="); - ir_print_other_inst_gen(irp, call_instruction->result_loc); -} - -static void ir_print_cond_br(IrPrintSrc *irp, IrInstSrcCondBr *inst) { - fprintf(irp->f, "if ("); - ir_print_other_inst_src(irp, inst->condition); - fprintf(irp->f, ") "); - ir_print_other_block(irp, inst->then_block); - fprintf(irp->f, " else "); - ir_print_other_block(irp, inst->else_block); - if (inst->is_comptime != nullptr) { - fprintf(irp->f, " // comptime = "); - ir_print_other_inst_src(irp, inst->is_comptime); - } -} - -static void ir_print_cond_br(IrPrintGen *irp, IrInstGenCondBr *inst) { - fprintf(irp->f, "if ("); - ir_print_other_inst_gen(irp, inst->condition); - fprintf(irp->f, ") "); - ir_print_other_block_gen(irp, inst->then_block); - fprintf(irp->f, " else "); - ir_print_other_block_gen(irp, inst->else_block); -} - -static void ir_print_br(IrPrintSrc *irp, IrInstSrcBr *br_instruction) { - fprintf(irp->f, "goto "); - ir_print_other_block(irp, br_instruction->dest_block); - if (br_instruction->is_comptime != nullptr) { - fprintf(irp->f, " // comptime = "); - ir_print_other_inst_src(irp, br_instruction->is_comptime); - } -} - -static void ir_print_br(IrPrintGen *irp, IrInstGenBr *inst) { - fprintf(irp->f, "goto "); - ir_print_other_block_gen(irp, inst->dest_block); -} - -static void ir_print_phi(IrPrintSrc *irp, IrInstSrcPhi *phi_instruction) { - assert(phi_instruction->incoming_count != 0); - assert(phi_instruction->incoming_count != SIZE_MAX); - for (size_t i = 0; i < phi_instruction->incoming_count; i += 1) { - IrBasicBlockSrc *incoming_block = phi_instruction->incoming_blocks[i]; - IrInstSrc *incoming_value = phi_instruction->incoming_values[i]; - if (i != 0) - fprintf(irp->f, " "); - ir_print_other_block(irp, incoming_block); - fprintf(irp->f, ":"); - ir_print_other_inst_src(irp, incoming_value); - } -} - -static void ir_print_phi(IrPrintGen *irp, IrInstGenPhi *phi_instruction) { - assert(phi_instruction->incoming_count != 0); - assert(phi_instruction->incoming_count != SIZE_MAX); - for (size_t i = 0; i < phi_instruction->incoming_count; i += 1) { - IrBasicBlockGen *incoming_block = phi_instruction->incoming_blocks[i]; - IrInstGen *incoming_value = phi_instruction->incoming_values[i]; - if (i != 0) - fprintf(irp->f, " "); - ir_print_other_block_gen(irp, incoming_block); - fprintf(irp->f, ":"); - ir_print_other_inst_gen(irp, incoming_value); - } -} - -static void ir_print_container_init_list(IrPrintSrc *irp, IrInstSrcContainerInitList *instruction) { - fprintf(irp->f, "{"); - if (instruction->item_count > 50) { - fprintf(irp->f, "...(%" ZIG_PRI_usize " items)...", instruction->item_count); - } else { - for (size_t i = 0; i < instruction->item_count; i += 1) { - IrInstSrc *result_loc = instruction->elem_result_loc_list[i]; - if (i != 0) - fprintf(irp->f, ", "); - ir_print_other_inst_src(irp, result_loc); - } - } - fprintf(irp->f, "}result="); - ir_print_other_inst_src(irp, instruction->result_loc); -} - -static void ir_print_container_init_fields(IrPrintSrc *irp, IrInstSrcContainerInitFields *instruction) { - fprintf(irp->f, "{"); - for (size_t i = 0; i < instruction->field_count; i += 1) { - IrInstSrcContainerInitFieldsField *field = &instruction->fields[i]; - const char *comma = (i == 0) ? "" : ", "; - fprintf(irp->f, "%s.%s = ", comma, buf_ptr(field->name)); - ir_print_other_inst_src(irp, field->result_loc); - } - fprintf(irp->f, "}result="); - ir_print_other_inst_src(irp, instruction->result_loc); -} - -static void ir_print_unreachable(IrPrintSrc *irp, IrInstSrcUnreachable *instruction) { - fprintf(irp->f, "unreachable"); -} - -static void ir_print_unreachable(IrPrintGen *irp, IrInstGenUnreachable *instruction) { - fprintf(irp->f, "unreachable"); -} - -static void ir_print_elem_ptr(IrPrintSrc *irp, IrInstSrcElemPtr *instruction) { - fprintf(irp->f, "&"); - ir_print_other_inst_src(irp, instruction->array_ptr); - fprintf(irp->f, "["); - ir_print_other_inst_src(irp, instruction->elem_index); - fprintf(irp->f, "]"); - if (!instruction->safety_check_on) { - fprintf(irp->f, " // no safety"); - } -} - -static void ir_print_elem_ptr(IrPrintGen *irp, IrInstGenElemPtr *instruction) { - fprintf(irp->f, "&"); - ir_print_other_inst_gen(irp, instruction->array_ptr); - fprintf(irp->f, "["); - ir_print_other_inst_gen(irp, instruction->elem_index); - fprintf(irp->f, "]"); - if (!instruction->safety_check_on) { - fprintf(irp->f, " // no safety"); - } -} - -static void ir_print_var_ptr(IrPrintSrc *irp, IrInstSrcVarPtr *instruction) { - fprintf(irp->f, "&%s", instruction->var->name); -} - -static void ir_print_var_ptr(IrPrintGen *irp, IrInstGenVarPtr *instruction) { - fprintf(irp->f, "&%s", instruction->var->name); -} - -static void ir_print_return_ptr(IrPrintGen *irp, IrInstGenReturnPtr *instruction) { - fprintf(irp->f, "@ReturnPtr"); -} - -static void ir_print_load_ptr(IrPrintSrc *irp, IrInstSrcLoadPtr *instruction) { - ir_print_other_inst_src(irp, instruction->ptr); - fprintf(irp->f, ".*"); -} - -static void ir_print_load_ptr_gen(IrPrintGen *irp, IrInstGenLoadPtr *instruction) { - fprintf(irp->f, "loadptr("); - ir_print_other_inst_gen(irp, instruction->ptr); - fprintf(irp->f, ")result="); - ir_print_other_inst_gen(irp, instruction->result_loc); -} - -static void ir_print_store_ptr(IrPrintSrc *irp, IrInstSrcStorePtr *instruction) { - fprintf(irp->f, "*"); - ir_print_var_src(irp, instruction->ptr); - fprintf(irp->f, " = "); - ir_print_other_inst_src(irp, instruction->value); -} - -static void ir_print_store_ptr(IrPrintGen *irp, IrInstGenStorePtr *instruction) { - fprintf(irp->f, "*"); - ir_print_var_gen(irp, instruction->ptr); - fprintf(irp->f, " = "); - ir_print_other_inst_gen(irp, instruction->value); -} - -static void ir_print_vector_store_elem(IrPrintGen *irp, IrInstGenVectorStoreElem *instruction) { - fprintf(irp->f, "vector_ptr="); - ir_print_var_gen(irp, instruction->vector_ptr); - fprintf(irp->f, ",index="); - ir_print_var_gen(irp, instruction->index); - fprintf(irp->f, ",value="); - ir_print_other_inst_gen(irp, instruction->value); -} - -static void ir_print_typeof(IrPrintSrc *irp, IrInstSrcTypeOf *instruction) { - fprintf(irp->f, "@TypeOf("); - if (instruction->value_count == 1) { - ir_print_other_inst_src(irp, instruction->value.scalar); - } else { - for (size_t i = 0; i < instruction->value_count; i += 1) { - ir_print_other_inst_src(irp, instruction->value.list[i]); - } - } - fprintf(irp->f, ")"); -} - -static void ir_print_binary_not(IrPrintGen *irp, IrInstGenBinaryNot *instruction) { - fprintf(irp->f, "~"); - ir_print_other_inst_gen(irp, instruction->operand); -} - -static void ir_print_negation(IrPrintGen *irp, IrInstGenNegation *instruction) { - fprintf(irp->f, "-"); - ir_print_other_inst_gen(irp, instruction->operand); -} - -static void ir_print_negation_wrapping(IrPrintGen *irp, IrInstGenNegationWrapping *instruction) { - fprintf(irp->f, "-%%"); - ir_print_other_inst_gen(irp, instruction->operand); -} - - -static void ir_print_field_ptr(IrPrintSrc *irp, IrInstSrcFieldPtr *instruction) { - if (instruction->field_name_buffer) { - fprintf(irp->f, "fieldptr "); - ir_print_other_inst_src(irp, instruction->container_ptr); - fprintf(irp->f, ".%s", buf_ptr(instruction->field_name_buffer)); - } else { - assert(instruction->field_name_expr); - fprintf(irp->f, "@field("); - ir_print_other_inst_src(irp, instruction->container_ptr); - fprintf(irp->f, ", "); - ir_print_other_inst_src(irp, instruction->field_name_expr); - fprintf(irp->f, ")"); - } -} - -static void ir_print_struct_field_ptr(IrPrintGen *irp, IrInstGenStructFieldPtr *instruction) { - fprintf(irp->f, "@StructFieldPtr(&"); - ir_print_other_inst_gen(irp, instruction->struct_ptr); - fprintf(irp->f, ".%s", buf_ptr(instruction->field->name)); - fprintf(irp->f, ")"); -} - -static void ir_print_union_field_ptr(IrPrintGen *irp, IrInstGenUnionFieldPtr *instruction) { - fprintf(irp->f, "@UnionFieldPtr(&"); - ir_print_other_inst_gen(irp, instruction->union_ptr); - fprintf(irp->f, ".%s", buf_ptr(instruction->field->enum_field->name)); - fprintf(irp->f, ")"); -} - -static void ir_print_set_cold(IrPrintSrc *irp, IrInstSrcSetCold *instruction) { - fprintf(irp->f, "@setCold("); - ir_print_other_inst_src(irp, instruction->is_cold); - fprintf(irp->f, ")"); -} - -static void ir_print_set_runtime_safety(IrPrintSrc *irp, IrInstSrcSetRuntimeSafety *instruction) { - fprintf(irp->f, "@setRuntimeSafety("); - ir_print_other_inst_src(irp, instruction->safety_on); - fprintf(irp->f, ")"); -} - -static void ir_print_set_float_mode(IrPrintSrc *irp, IrInstSrcSetFloatMode *instruction) { - fprintf(irp->f, "@setFloatMode("); - ir_print_other_inst_src(irp, instruction->scope_value); - fprintf(irp->f, ", "); - ir_print_other_inst_src(irp, instruction->mode_value); - fprintf(irp->f, ")"); -} - -static void ir_print_array_type(IrPrintSrc *irp, IrInstSrcArrayType *instruction) { - fprintf(irp->f, "["); - ir_print_other_inst_src(irp, instruction->size); - if (instruction->sentinel != nullptr) { - fprintf(irp->f, ":"); - ir_print_other_inst_src(irp, instruction->sentinel); - } - fprintf(irp->f, "]"); - ir_print_other_inst_src(irp, instruction->child_type); -} - -static void ir_print_slice_type(IrPrintSrc *irp, IrInstSrcSliceType *instruction) { - const char *const_kw = instruction->is_const ? "const " : ""; - fprintf(irp->f, "[]%s", const_kw); - ir_print_other_inst_src(irp, instruction->child_type); -} - -static void ir_print_any_frame_type(IrPrintSrc *irp, IrInstSrcAnyFrameType *instruction) { - if (instruction->payload_type == nullptr) { - fprintf(irp->f, "anyframe"); - } else { - fprintf(irp->f, "anyframe->"); - ir_print_other_inst_src(irp, instruction->payload_type); - } -} - -static void ir_print_asm_src(IrPrintSrc *irp, IrInstSrcAsm *instruction) { - assert(instruction->base.base.source_node->type == NodeTypeAsmExpr); - AstNodeAsmExpr *asm_expr = &instruction->base.base.source_node->data.asm_expr; - const char *volatile_kw = instruction->has_side_effects ? " volatile" : ""; - fprintf(irp->f, "asm%s (", volatile_kw); - ir_print_other_inst_src(irp, instruction->asm_template); - - 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(irp->f, ", "); - - fprintf(irp->f, "[%s] \"%s\" (", - buf_ptr(asm_output->asm_symbolic_name), - buf_ptr(asm_output->constraint)); - if (asm_output->return_type) { - fprintf(irp->f, "-> "); - ir_print_other_inst_src(irp, instruction->output_types[i]); - } else { - fprintf(irp->f, "%s", buf_ptr(asm_output->variable_name)); - } - fprintf(irp->f, ")"); - } - - fprintf(irp->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(irp->f, ", "); - fprintf(irp->f, "[%s] \"%s\" (", - buf_ptr(asm_input->asm_symbolic_name), - buf_ptr(asm_input->constraint)); - ir_print_other_inst_src(irp, instruction->input_list[i]); - fprintf(irp->f, ")"); - } - fprintf(irp->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(irp->f, ", "); - fprintf(irp->f, "\"%s\"", buf_ptr(reg_name)); - } - fprintf(irp->f, ")"); -} - -static void ir_print_asm_gen(IrPrintGen *irp, IrInstGenAsm *instruction) { - assert(instruction->base.base.source_node->type == NodeTypeAsmExpr); - AstNodeAsmExpr *asm_expr = &instruction->base.base.source_node->data.asm_expr; - const char *volatile_kw = instruction->has_side_effects ? " volatile" : ""; - fprintf(irp->f, "asm%s (\"%s\") : ", volatile_kw, buf_ptr(instruction->asm_template)); - - 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(irp->f, ", "); - - fprintf(irp->f, "[%s] \"%s\" (", - buf_ptr(asm_output->asm_symbolic_name), - buf_ptr(asm_output->constraint)); - if (asm_output->return_type) { - fprintf(irp->f, "-> "); - ir_print_other_inst_gen(irp, instruction->output_types[i]); - } else { - fprintf(irp->f, "%s", buf_ptr(asm_output->variable_name)); - } - fprintf(irp->f, ")"); - } - - fprintf(irp->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(irp->f, ", "); - fprintf(irp->f, "[%s] \"%s\" (", - buf_ptr(asm_input->asm_symbolic_name), - buf_ptr(asm_input->constraint)); - ir_print_other_inst_gen(irp, instruction->input_list[i]); - fprintf(irp->f, ")"); - } - fprintf(irp->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(irp->f, ", "); - fprintf(irp->f, "\"%s\"", buf_ptr(reg_name)); - } - fprintf(irp->f, ")"); -} - -static void ir_print_size_of(IrPrintSrc *irp, IrInstSrcSizeOf *instruction) { - if (instruction->bit_size) - fprintf(irp->f, "@bitSizeOf("); - else - fprintf(irp->f, "@sizeOf("); - ir_print_other_inst_src(irp, instruction->type_value); - fprintf(irp->f, ")"); -} - -static void ir_print_test_non_null(IrPrintSrc *irp, IrInstSrcTestNonNull *instruction) { - ir_print_other_inst_src(irp, instruction->value); - fprintf(irp->f, " != null"); -} - -static void ir_print_test_non_null(IrPrintGen *irp, IrInstGenTestNonNull *instruction) { - ir_print_other_inst_gen(irp, instruction->value); - fprintf(irp->f, " != null"); -} - -static void ir_print_optional_unwrap_ptr(IrPrintSrc *irp, IrInstSrcOptionalUnwrapPtr *instruction) { - fprintf(irp->f, "&"); - ir_print_other_inst_src(irp, instruction->base_ptr); - fprintf(irp->f, ".*.?"); - if (!instruction->safety_check_on) { - fprintf(irp->f, " // no safety"); - } -} - -static void ir_print_optional_unwrap_ptr(IrPrintGen *irp, IrInstGenOptionalUnwrapPtr *instruction) { - fprintf(irp->f, "&"); - ir_print_other_inst_gen(irp, instruction->base_ptr); - fprintf(irp->f, ".*.?"); - if (!instruction->safety_check_on) { - fprintf(irp->f, " // no safety"); - } -} - -static void ir_print_clz(IrPrintSrc *irp, IrInstSrcClz *instruction) { - fprintf(irp->f, "@clz("); - ir_print_other_inst_src(irp, instruction->type); - fprintf(irp->f, ","); - ir_print_other_inst_src(irp, instruction->op); - fprintf(irp->f, ")"); -} - -static void ir_print_clz(IrPrintGen *irp, IrInstGenClz *instruction) { - fprintf(irp->f, "@clz("); - ir_print_other_inst_gen(irp, instruction->op); - fprintf(irp->f, ")"); -} - -static void ir_print_ctz(IrPrintSrc *irp, IrInstSrcCtz *instruction) { - fprintf(irp->f, "@ctz("); - ir_print_other_inst_src(irp, instruction->type); - fprintf(irp->f, ","); - ir_print_other_inst_src(irp, instruction->op); - fprintf(irp->f, ")"); -} - -static void ir_print_ctz(IrPrintGen *irp, IrInstGenCtz *instruction) { - fprintf(irp->f, "@ctz("); - ir_print_other_inst_gen(irp, instruction->op); - fprintf(irp->f, ")"); -} - -static void ir_print_pop_count(IrPrintSrc *irp, IrInstSrcPopCount *instruction) { - fprintf(irp->f, "@popCount("); - ir_print_other_inst_src(irp, instruction->type); - fprintf(irp->f, ","); - ir_print_other_inst_src(irp, instruction->op); - fprintf(irp->f, ")"); -} - -static void ir_print_pop_count(IrPrintGen *irp, IrInstGenPopCount *instruction) { - fprintf(irp->f, "@popCount("); - ir_print_other_inst_gen(irp, instruction->op); - fprintf(irp->f, ")"); -} - -static void ir_print_bswap(IrPrintSrc *irp, IrInstSrcBswap *instruction) { - fprintf(irp->f, "@byteSwap("); - ir_print_other_inst_src(irp, instruction->type); - fprintf(irp->f, ","); - ir_print_other_inst_src(irp, instruction->op); - fprintf(irp->f, ")"); -} - -static void ir_print_bswap(IrPrintGen *irp, IrInstGenBswap *instruction) { - fprintf(irp->f, "@byteSwap("); - ir_print_other_inst_gen(irp, instruction->op); - fprintf(irp->f, ")"); -} - -static void ir_print_bit_reverse(IrPrintSrc *irp, IrInstSrcBitReverse *instruction) { - fprintf(irp->f, "@bitReverse("); - ir_print_other_inst_src(irp, instruction->type); - fprintf(irp->f, ","); - ir_print_other_inst_src(irp, instruction->op); - fprintf(irp->f, ")"); -} - -static void ir_print_bit_reverse(IrPrintGen *irp, IrInstGenBitReverse *instruction) { - fprintf(irp->f, "@bitReverse("); - ir_print_other_inst_gen(irp, instruction->op); - fprintf(irp->f, ")"); -} - -static void ir_print_switch_br(IrPrintSrc *irp, IrInstSrcSwitchBr *instruction) { - fprintf(irp->f, "switch ("); - ir_print_other_inst_src(irp, instruction->target_value); - fprintf(irp->f, ") "); - for (size_t i = 0; i < instruction->case_count; i += 1) { - IrInstSrcSwitchBrCase *this_case = &instruction->cases[i]; - ir_print_other_inst_src(irp, this_case->value); - fprintf(irp->f, " => "); - ir_print_other_block(irp, this_case->block); - fprintf(irp->f, ", "); - } - fprintf(irp->f, "else => "); - ir_print_other_block(irp, instruction->else_block); - if (instruction->is_comptime != nullptr) { - fprintf(irp->f, " // comptime = "); - ir_print_other_inst_src(irp, instruction->is_comptime); - } -} - -static void ir_print_switch_br(IrPrintGen *irp, IrInstGenSwitchBr *instruction) { - fprintf(irp->f, "switch ("); - ir_print_other_inst_gen(irp, instruction->target_value); - fprintf(irp->f, ") "); - for (size_t i = 0; i < instruction->case_count; i += 1) { - IrInstGenSwitchBrCase *this_case = &instruction->cases[i]; - ir_print_other_inst_gen(irp, this_case->value); - fprintf(irp->f, " => "); - ir_print_other_block_gen(irp, this_case->block); - fprintf(irp->f, ", "); - } - fprintf(irp->f, "else => "); - ir_print_other_block_gen(irp, instruction->else_block); -} - -static void ir_print_switch_var(IrPrintSrc *irp, IrInstSrcSwitchVar *instruction) { - fprintf(irp->f, "switchvar "); - ir_print_other_inst_src(irp, instruction->target_value_ptr); - for (size_t i = 0; i < instruction->prongs_len; i += 1) { - fprintf(irp->f, ", "); - ir_print_other_inst_src(irp, instruction->prongs_ptr[i]); - } -} - -static void ir_print_switch_else_var(IrPrintSrc *irp, IrInstSrcSwitchElseVar *instruction) { - fprintf(irp->f, "switchelsevar "); - ir_print_other_inst_src(irp, &instruction->switch_br->base); -} - -static void ir_print_switch_target(IrPrintSrc *irp, IrInstSrcSwitchTarget *instruction) { - fprintf(irp->f, "switchtarget "); - ir_print_other_inst_src(irp, instruction->target_value_ptr); -} - -static void ir_print_union_tag(IrPrintGen *irp, IrInstGenUnionTag *instruction) { - fprintf(irp->f, "uniontag "); - ir_print_other_inst_gen(irp, instruction->value); -} - -static void ir_print_import(IrPrintSrc *irp, IrInstSrcImport *instruction) { - fprintf(irp->f, "@import("); - ir_print_other_inst_src(irp, instruction->name); - fprintf(irp->f, ")"); -} - -static void ir_print_ref(IrPrintSrc *irp, IrInstSrcRef *instruction) { - fprintf(irp->f, "ref "); - ir_print_other_inst_src(irp, instruction->value); -} - -static void ir_print_ref_gen(IrPrintGen *irp, IrInstGenRef *instruction) { - fprintf(irp->f, "@ref("); - ir_print_other_inst_gen(irp, instruction->operand); - fprintf(irp->f, ")result="); - ir_print_other_inst_gen(irp, instruction->result_loc); -} - -static void ir_print_compile_err(IrPrintSrc *irp, IrInstSrcCompileErr *instruction) { - fprintf(irp->f, "@compileError("); - ir_print_other_inst_src(irp, instruction->msg); - fprintf(irp->f, ")"); -} - -static void ir_print_compile_log(IrPrintSrc *irp, IrInstSrcCompileLog *instruction) { - fprintf(irp->f, "@compileLog("); - for (size_t i = 0; i < instruction->msg_count; i += 1) { - if (i != 0) - fprintf(irp->f, ","); - IrInstSrc *msg = instruction->msg_list[i]; - ir_print_other_inst_src(irp, msg); - } - fprintf(irp->f, ")"); -} - -static void ir_print_err_name(IrPrintSrc *irp, IrInstSrcErrName *instruction) { - fprintf(irp->f, "@errorName("); - ir_print_other_inst_src(irp, instruction->value); - fprintf(irp->f, ")"); -} - -static void ir_print_err_name(IrPrintGen *irp, IrInstGenErrName *instruction) { - fprintf(irp->f, "@errorName("); - ir_print_other_inst_gen(irp, instruction->value); - fprintf(irp->f, ")"); -} - -static void ir_print_c_import(IrPrintSrc *irp, IrInstSrcCImport *instruction) { - fprintf(irp->f, "@cImport(...)"); -} - -static void ir_print_c_include(IrPrintSrc *irp, IrInstSrcCInclude *instruction) { - fprintf(irp->f, "@cInclude("); - ir_print_other_inst_src(irp, instruction->name); - fprintf(irp->f, ")"); -} - -static void ir_print_c_define(IrPrintSrc *irp, IrInstSrcCDefine *instruction) { - fprintf(irp->f, "@cDefine("); - ir_print_other_inst_src(irp, instruction->name); - fprintf(irp->f, ", "); - ir_print_other_inst_src(irp, instruction->value); - fprintf(irp->f, ")"); -} - -static void ir_print_c_undef(IrPrintSrc *irp, IrInstSrcCUndef *instruction) { - fprintf(irp->f, "@cUndef("); - ir_print_other_inst_src(irp, instruction->name); - fprintf(irp->f, ")"); -} - -static void ir_print_embed_file(IrPrintSrc *irp, IrInstSrcEmbedFile *instruction) { - fprintf(irp->f, "@embedFile("); - ir_print_other_inst_src(irp, instruction->name); - fprintf(irp->f, ")"); -} - -static void ir_print_cmpxchg_src(IrPrintSrc *irp, IrInstSrcCmpxchg *instruction) { - fprintf(irp->f, "@cmpxchg("); - ir_print_other_inst_src(irp, instruction->ptr); - fprintf(irp->f, ", "); - ir_print_other_inst_src(irp, instruction->cmp_value); - fprintf(irp->f, ", "); - ir_print_other_inst_src(irp, instruction->new_value); - fprintf(irp->f, ", "); - ir_print_other_inst_src(irp, instruction->success_order_value); - fprintf(irp->f, ", "); - ir_print_other_inst_src(irp, instruction->failure_order_value); - fprintf(irp->f, ")result="); - ir_print_result_loc(irp, instruction->result_loc); -} - -static void ir_print_cmpxchg_gen(IrPrintGen *irp, IrInstGenCmpxchg *instruction) { - fprintf(irp->f, "@cmpxchg("); - ir_print_other_inst_gen(irp, instruction->ptr); - fprintf(irp->f, ", "); - ir_print_other_inst_gen(irp, instruction->cmp_value); - fprintf(irp->f, ", "); - ir_print_other_inst_gen(irp, instruction->new_value); - fprintf(irp->f, ", TODO print atomic orders)result="); - ir_print_other_inst_gen(irp, instruction->result_loc); -} - -static void ir_print_fence(IrPrintSrc *irp, IrInstSrcFence *instruction) { - fprintf(irp->f, "@fence("); - ir_print_other_inst_src(irp, instruction->order); - fprintf(irp->f, ")"); -} - -static const char *atomic_order_str(AtomicOrder order) { - switch (order) { - case AtomicOrderUnordered: return "Unordered"; - case AtomicOrderMonotonic: return "Monotonic"; - case AtomicOrderAcquire: return "Acquire"; - case AtomicOrderRelease: return "Release"; - case AtomicOrderAcqRel: return "AcqRel"; - case AtomicOrderSeqCst: return "SeqCst"; - } - zig_unreachable(); -} - -static void ir_print_fence(IrPrintGen *irp, IrInstGenFence *instruction) { - fprintf(irp->f, "fence %s", atomic_order_str(instruction->order)); -} - -static void ir_print_truncate(IrPrintSrc *irp, IrInstSrcTruncate *instruction) { - fprintf(irp->f, "@truncate("); - ir_print_other_inst_src(irp, instruction->dest_type); - fprintf(irp->f, ", "); - ir_print_other_inst_src(irp, instruction->target); - fprintf(irp->f, ")"); -} - -static void ir_print_truncate(IrPrintGen *irp, IrInstGenTruncate *instruction) { - fprintf(irp->f, "@truncate("); - ir_print_other_inst_gen(irp, instruction->target); - fprintf(irp->f, ")"); -} - -static void ir_print_int_cast(IrPrintSrc *irp, IrInstSrcIntCast *instruction) { - fprintf(irp->f, "@intCast("); - ir_print_other_inst_src(irp, instruction->dest_type); - fprintf(irp->f, ", "); - ir_print_other_inst_src(irp, instruction->target); - fprintf(irp->f, ")"); -} - -static void ir_print_float_cast(IrPrintSrc *irp, IrInstSrcFloatCast *instruction) { - fprintf(irp->f, "@floatCast("); - ir_print_other_inst_src(irp, instruction->dest_type); - fprintf(irp->f, ", "); - ir_print_other_inst_src(irp, instruction->target); - fprintf(irp->f, ")"); -} - -static void ir_print_err_set_cast(IrPrintSrc *irp, IrInstSrcErrSetCast *instruction) { - fprintf(irp->f, "@errSetCast("); - ir_print_other_inst_src(irp, instruction->dest_type); - fprintf(irp->f, ", "); - ir_print_other_inst_src(irp, instruction->target); - fprintf(irp->f, ")"); -} - -static void ir_print_int_to_float(IrPrintSrc *irp, IrInstSrcIntToFloat *instruction) { - fprintf(irp->f, "@intToFloat("); - ir_print_other_inst_src(irp, instruction->dest_type); - fprintf(irp->f, ", "); - ir_print_other_inst_src(irp, instruction->target); - fprintf(irp->f, ")"); -} - -static void ir_print_float_to_int(IrPrintSrc *irp, IrInstSrcFloatToInt *instruction) { - fprintf(irp->f, "@floatToInt("); - ir_print_other_inst_src(irp, instruction->dest_type); - fprintf(irp->f, ", "); - ir_print_other_inst_src(irp, instruction->target); - fprintf(irp->f, ")"); -} - -static void ir_print_bool_to_int(IrPrintSrc *irp, IrInstSrcBoolToInt *instruction) { - fprintf(irp->f, "@boolToInt("); - ir_print_other_inst_src(irp, instruction->target); - fprintf(irp->f, ")"); -} - -static void ir_print_vector_type(IrPrintSrc *irp, IrInstSrcVectorType *instruction) { - fprintf(irp->f, "@Vector("); - ir_print_other_inst_src(irp, instruction->len); - fprintf(irp->f, ", "); - ir_print_other_inst_src(irp, instruction->elem_type); - fprintf(irp->f, ")"); -} - -static void ir_print_shuffle_vector(IrPrintSrc *irp, IrInstSrcShuffleVector *instruction) { - fprintf(irp->f, "@shuffle("); - ir_print_other_inst_src(irp, instruction->scalar_type); - fprintf(irp->f, ", "); - ir_print_other_inst_src(irp, instruction->a); - fprintf(irp->f, ", "); - ir_print_other_inst_src(irp, instruction->b); - fprintf(irp->f, ", "); - ir_print_other_inst_src(irp, instruction->mask); - fprintf(irp->f, ")"); -} - -static void ir_print_shuffle_vector(IrPrintGen *irp, IrInstGenShuffleVector *instruction) { - fprintf(irp->f, "@shuffle("); - ir_print_other_inst_gen(irp, instruction->a); - fprintf(irp->f, ", "); - ir_print_other_inst_gen(irp, instruction->b); - fprintf(irp->f, ", "); - ir_print_other_inst_gen(irp, instruction->mask); - fprintf(irp->f, ")"); -} - -static void ir_print_splat_src(IrPrintSrc *irp, IrInstSrcSplat *instruction) { - fprintf(irp->f, "@splat("); - ir_print_other_inst_src(irp, instruction->len); - fprintf(irp->f, ", "); - ir_print_other_inst_src(irp, instruction->scalar); - fprintf(irp->f, ")"); -} - -static void ir_print_splat_gen(IrPrintGen *irp, IrInstGenSplat *instruction) { - fprintf(irp->f, "@splat("); - ir_print_other_inst_gen(irp, instruction->scalar); - fprintf(irp->f, ")"); -} - -static void ir_print_bool_not(IrPrintSrc *irp, IrInstSrcBoolNot *instruction) { - fprintf(irp->f, "! "); - ir_print_other_inst_src(irp, instruction->value); -} - -static void ir_print_bool_not(IrPrintGen *irp, IrInstGenBoolNot *instruction) { - fprintf(irp->f, "! "); - ir_print_other_inst_gen(irp, instruction->value); -} - -static void ir_print_wasm_memory_size(IrPrintSrc *irp, IrInstSrcWasmMemorySize *instruction) { - fprintf(irp->f, "@wasmMemorySize("); - ir_print_other_inst_src(irp, instruction->index); - fprintf(irp->f, ")"); -} - -static void ir_print_wasm_memory_size(IrPrintGen *irp, IrInstGenWasmMemorySize *instruction) { - fprintf(irp->f, "@wasmMemorySize("); - ir_print_other_inst_gen(irp, instruction->index); - fprintf(irp->f, ")"); -} - -static void ir_print_wasm_memory_grow(IrPrintSrc *irp, IrInstSrcWasmMemoryGrow *instruction) { - fprintf(irp->f, "@wasmMemoryGrow("); - ir_print_other_inst_src(irp, instruction->index); - fprintf(irp->f, ", "); - ir_print_other_inst_src(irp, instruction->delta); - fprintf(irp->f, ")"); -} - -static void ir_print_wasm_memory_grow(IrPrintGen *irp, IrInstGenWasmMemoryGrow *instruction) { - fprintf(irp->f, "@wasmMemoryGrow("); - ir_print_other_inst_gen(irp, instruction->index); - fprintf(irp->f, ", "); - ir_print_other_inst_gen(irp, instruction->delta); - fprintf(irp->f, ")"); -} - -static void ir_print_builtin_src(IrPrintSrc *irp, IrInstSrcSrc *instruction) { - fprintf(irp->f, "@src()"); -} - -static void ir_print_memset(IrPrintSrc *irp, IrInstSrcMemset *instruction) { - fprintf(irp->f, "@memset("); - ir_print_other_inst_src(irp, instruction->dest_ptr); - fprintf(irp->f, ", "); - ir_print_other_inst_src(irp, instruction->byte); - fprintf(irp->f, ", "); - ir_print_other_inst_src(irp, instruction->count); - fprintf(irp->f, ")"); -} - -static void ir_print_memset(IrPrintGen *irp, IrInstGenMemset *instruction) { - fprintf(irp->f, "@memset("); - ir_print_other_inst_gen(irp, instruction->dest_ptr); - fprintf(irp->f, ", "); - ir_print_other_inst_gen(irp, instruction->byte); - fprintf(irp->f, ", "); - ir_print_other_inst_gen(irp, instruction->count); - fprintf(irp->f, ")"); -} - -static void ir_print_memcpy(IrPrintSrc *irp, IrInstSrcMemcpy *instruction) { - fprintf(irp->f, "@memcpy("); - ir_print_other_inst_src(irp, instruction->dest_ptr); - fprintf(irp->f, ", "); - ir_print_other_inst_src(irp, instruction->src_ptr); - fprintf(irp->f, ", "); - ir_print_other_inst_src(irp, instruction->count); - fprintf(irp->f, ")"); -} - -static void ir_print_memcpy(IrPrintGen *irp, IrInstGenMemcpy *instruction) { - fprintf(irp->f, "@memcpy("); - ir_print_other_inst_gen(irp, instruction->dest_ptr); - fprintf(irp->f, ", "); - ir_print_other_inst_gen(irp, instruction->src_ptr); - fprintf(irp->f, ", "); - ir_print_other_inst_gen(irp, instruction->count); - fprintf(irp->f, ")"); -} - -static void ir_print_slice_src(IrPrintSrc *irp, IrInstSrcSlice *instruction) { - ir_print_other_inst_src(irp, instruction->ptr); - fprintf(irp->f, "["); - ir_print_other_inst_src(irp, instruction->start); - fprintf(irp->f, ".."); - if (instruction->end) - ir_print_other_inst_src(irp, instruction->end); - fprintf(irp->f, "]result="); - ir_print_result_loc(irp, instruction->result_loc); -} - -static void ir_print_slice_gen(IrPrintGen *irp, IrInstGenSlice *instruction) { - ir_print_other_inst_gen(irp, instruction->ptr); - fprintf(irp->f, "["); - ir_print_other_inst_gen(irp, instruction->start); - fprintf(irp->f, ".."); - if (instruction->end) - ir_print_other_inst_gen(irp, instruction->end); - fprintf(irp->f, "]result="); - ir_print_other_inst_gen(irp, instruction->result_loc); -} - -static void ir_print_breakpoint(IrPrintSrc *irp, IrInstSrcBreakpoint *instruction) { - fprintf(irp->f, "@breakpoint()"); -} - -static void ir_print_breakpoint(IrPrintGen *irp, IrInstGenBreakpoint *instruction) { - fprintf(irp->f, "@breakpoint()"); -} - -static void ir_print_frame_address(IrPrintSrc *irp, IrInstSrcFrameAddress *instruction) { - fprintf(irp->f, "@frameAddress()"); -} - -static void ir_print_frame_address(IrPrintGen *irp, IrInstGenFrameAddress *instruction) { - fprintf(irp->f, "@frameAddress()"); -} - -static void ir_print_handle(IrPrintSrc *irp, IrInstSrcFrameHandle *instruction) { - fprintf(irp->f, "@frame()"); -} - -static void ir_print_handle(IrPrintGen *irp, IrInstGenFrameHandle *instruction) { - fprintf(irp->f, "@frame()"); -} - -static void ir_print_frame_type(IrPrintSrc *irp, IrInstSrcFrameType *instruction) { - fprintf(irp->f, "@Frame("); - ir_print_other_inst_src(irp, instruction->fn); - fprintf(irp->f, ")"); -} - -static void ir_print_frame_size_src(IrPrintSrc *irp, IrInstSrcFrameSize *instruction) { - fprintf(irp->f, "@frameSize("); - ir_print_other_inst_src(irp, instruction->fn); - fprintf(irp->f, ")"); -} - -static void ir_print_frame_size_gen(IrPrintGen *irp, IrInstGenFrameSize *instruction) { - fprintf(irp->f, "@frameSize("); - ir_print_other_inst_gen(irp, instruction->fn); - fprintf(irp->f, ")"); -} - -static void ir_print_return_address(IrPrintSrc *irp, IrInstSrcReturnAddress *instruction) { - fprintf(irp->f, "@returnAddress()"); -} - -static void ir_print_return_address(IrPrintGen *irp, IrInstGenReturnAddress *instruction) { - fprintf(irp->f, "@returnAddress()"); -} - -static void ir_print_align_of(IrPrintSrc *irp, IrInstSrcAlignOf *instruction) { - fprintf(irp->f, "@alignOf("); - ir_print_other_inst_src(irp, instruction->type_value); - fprintf(irp->f, ")"); -} - -static void ir_print_overflow_op(IrPrintSrc *irp, IrInstSrcOverflowOp *instruction) { - switch (instruction->op) { - case IrOverflowOpAdd: - fprintf(irp->f, "@addWithOverflow("); - break; - case IrOverflowOpSub: - fprintf(irp->f, "@subWithOverflow("); - break; - case IrOverflowOpMul: - fprintf(irp->f, "@mulWithOverflow("); - break; - case IrOverflowOpShl: - fprintf(irp->f, "@shlWithOverflow("); - break; - } - ir_print_other_inst_src(irp, instruction->type_value); - fprintf(irp->f, ", "); - ir_print_other_inst_src(irp, instruction->op1); - fprintf(irp->f, ", "); - ir_print_other_inst_src(irp, instruction->op2); - fprintf(irp->f, ", "); - ir_print_other_inst_src(irp, instruction->result_ptr); - fprintf(irp->f, ")"); -} - -static void ir_print_overflow_op(IrPrintGen *irp, IrInstGenOverflowOp *instruction) { - switch (instruction->op) { - case IrOverflowOpAdd: - fprintf(irp->f, "@addWithOverflow("); - break; - case IrOverflowOpSub: - fprintf(irp->f, "@subWithOverflow("); - break; - case IrOverflowOpMul: - fprintf(irp->f, "@mulWithOverflow("); - break; - case IrOverflowOpShl: - fprintf(irp->f, "@shlWithOverflow("); - break; - } - ir_print_other_inst_gen(irp, instruction->op1); - fprintf(irp->f, ", "); - ir_print_other_inst_gen(irp, instruction->op2); - fprintf(irp->f, ", "); - ir_print_other_inst_gen(irp, instruction->result_ptr); - fprintf(irp->f, ")"); -} - -static void ir_print_test_err_src(IrPrintSrc *irp, IrInstSrcTestErr *instruction) { - fprintf(irp->f, "@testError("); - ir_print_other_inst_src(irp, instruction->base_ptr); - fprintf(irp->f, ")"); -} - -static void ir_print_test_err_gen(IrPrintGen *irp, IrInstGenTestErr *instruction) { - fprintf(irp->f, "@testError("); - ir_print_other_inst_gen(irp, instruction->err_union); - fprintf(irp->f, ")"); -} - -static void ir_print_unwrap_err_code(IrPrintSrc *irp, IrInstSrcUnwrapErrCode *instruction) { - fprintf(irp->f, "UnwrapErrorCode("); - ir_print_other_inst_src(irp, instruction->err_union_ptr); - fprintf(irp->f, ")"); -} - -static void ir_print_unwrap_err_code(IrPrintGen *irp, IrInstGenUnwrapErrCode *instruction) { - fprintf(irp->f, "UnwrapErrorCode("); - ir_print_other_inst_gen(irp, instruction->err_union_ptr); - fprintf(irp->f, ")"); -} - -static void ir_print_unwrap_err_payload(IrPrintSrc *irp, IrInstSrcUnwrapErrPayload *instruction) { - fprintf(irp->f, "ErrorUnionFieldPayload("); - ir_print_other_inst_src(irp, instruction->value); - fprintf(irp->f, ")safety=%d,init=%d",instruction->safety_check_on, instruction->initializing); -} - -static void ir_print_unwrap_err_payload(IrPrintGen *irp, IrInstGenUnwrapErrPayload *instruction) { - fprintf(irp->f, "ErrorUnionFieldPayload("); - ir_print_other_inst_gen(irp, instruction->value); - fprintf(irp->f, ")safety=%d,init=%d",instruction->safety_check_on, instruction->initializing); -} - -static void ir_print_optional_wrap(IrPrintGen *irp, IrInstGenOptionalWrap *instruction) { - fprintf(irp->f, "@optionalWrap("); - ir_print_other_inst_gen(irp, instruction->operand); - fprintf(irp->f, ")result="); - ir_print_other_inst_gen(irp, instruction->result_loc); -} - -static void ir_print_err_wrap_code(IrPrintGen *irp, IrInstGenErrWrapCode *instruction) { - fprintf(irp->f, "@errWrapCode("); - ir_print_other_inst_gen(irp, instruction->operand); - fprintf(irp->f, ")result="); - ir_print_other_inst_gen(irp, instruction->result_loc); -} - -static void ir_print_err_wrap_payload(IrPrintGen *irp, IrInstGenErrWrapPayload *instruction) { - fprintf(irp->f, "@errWrapPayload("); - ir_print_other_inst_gen(irp, instruction->operand); - fprintf(irp->f, ")result="); - ir_print_other_inst_gen(irp, instruction->result_loc); -} - -static void ir_print_fn_proto(IrPrintSrc *irp, IrInstSrcFnProto *instruction) { - fprintf(irp->f, "fn("); - for (size_t i = 0; i < instruction->base.base.source_node->data.fn_proto.params.length; i += 1) { - if (i != 0) - fprintf(irp->f, ","); - if (instruction->is_var_args && i == instruction->base.base.source_node->data.fn_proto.params.length - 1) { - fprintf(irp->f, "..."); - } else { - ir_print_other_inst_src(irp, instruction->param_types[i]); - } - } - fprintf(irp->f, ")"); - if (instruction->align_value != nullptr) { - fprintf(irp->f, " align "); - ir_print_other_inst_src(irp, instruction->align_value); - fprintf(irp->f, " "); - } - fprintf(irp->f, "->"); - ir_print_other_inst_src(irp, instruction->return_type); -} - -static void ir_print_test_comptime(IrPrintSrc *irp, IrInstSrcTestComptime *instruction) { - fprintf(irp->f, "@testComptime("); - ir_print_other_inst_src(irp, instruction->value); - fprintf(irp->f, ")"); -} - -static void ir_print_ptr_cast_src(IrPrintSrc *irp, IrInstSrcPtrCast *instruction) { - fprintf(irp->f, "@ptrCast("); - if (instruction->dest_type) { - ir_print_other_inst_src(irp, instruction->dest_type); - } - fprintf(irp->f, ","); - ir_print_other_inst_src(irp, instruction->ptr); - fprintf(irp->f, ")"); -} - -static void ir_print_ptr_cast_gen(IrPrintGen *irp, IrInstGenPtrCast *instruction) { - fprintf(irp->f, "@ptrCast("); - ir_print_other_inst_gen(irp, instruction->ptr); - fprintf(irp->f, ")"); -} - -static void ir_print_implicit_cast(IrPrintSrc *irp, IrInstSrcImplicitCast *instruction) { - fprintf(irp->f, "@implicitCast("); - ir_print_other_inst_src(irp, instruction->operand); - fprintf(irp->f, ")result="); - ir_print_result_loc(irp, &instruction->result_loc_cast->base); -} - -static void ir_print_bit_cast_src(IrPrintSrc *irp, IrInstSrcBitCast *instruction) { - fprintf(irp->f, "@bitCast("); - ir_print_other_inst_src(irp, instruction->operand); - fprintf(irp->f, ")result="); - ir_print_result_loc(irp, &instruction->result_loc_bit_cast->base); -} - -static void ir_print_bit_cast_gen(IrPrintGen *irp, IrInstGenBitCast *instruction) { - fprintf(irp->f, "@bitCast("); - ir_print_other_inst_gen(irp, instruction->operand); - fprintf(irp->f, ")"); -} - -static void ir_print_widen_or_shorten(IrPrintGen *irp, IrInstGenWidenOrShorten *instruction) { - fprintf(irp->f, "WidenOrShorten("); - ir_print_other_inst_gen(irp, instruction->target); - fprintf(irp->f, ")"); -} - -static void ir_print_ptr_to_int(IrPrintSrc *irp, IrInstSrcPtrToInt *instruction) { - fprintf(irp->f, "@ptrToInt("); - ir_print_other_inst_src(irp, instruction->target); - fprintf(irp->f, ")"); -} - -static void ir_print_ptr_to_int(IrPrintGen *irp, IrInstGenPtrToInt *instruction) { - fprintf(irp->f, "@ptrToInt("); - ir_print_other_inst_gen(irp, instruction->target); - fprintf(irp->f, ")"); -} - -static void ir_print_int_to_ptr(IrPrintSrc *irp, IrInstSrcIntToPtr *instruction) { - fprintf(irp->f, "@intToPtr("); - ir_print_other_inst_src(irp, instruction->dest_type); - fprintf(irp->f, ","); - ir_print_other_inst_src(irp, instruction->target); - fprintf(irp->f, ")"); -} - -static void ir_print_int_to_ptr(IrPrintGen *irp, IrInstGenIntToPtr *instruction) { - fprintf(irp->f, "@intToPtr("); - ir_print_other_inst_gen(irp, instruction->target); - fprintf(irp->f, ")"); -} - -static void ir_print_int_to_enum(IrPrintSrc *irp, IrInstSrcIntToEnum *instruction) { - fprintf(irp->f, "@intToEnum("); - ir_print_other_inst_src(irp, instruction->dest_type); - fprintf(irp->f, ","); - ir_print_other_inst_src(irp, instruction->target); - fprintf(irp->f, ")"); -} - -static void ir_print_int_to_enum(IrPrintGen *irp, IrInstGenIntToEnum *instruction) { - fprintf(irp->f, "@intToEnum("); - ir_print_other_inst_gen(irp, instruction->target); - fprintf(irp->f, ")"); -} - -static void ir_print_enum_to_int(IrPrintSrc *irp, IrInstSrcEnumToInt *instruction) { - fprintf(irp->f, "@enumToInt("); - ir_print_other_inst_src(irp, instruction->target); - fprintf(irp->f, ")"); -} - -static void ir_print_check_runtime_scope(IrPrintSrc *irp, IrInstSrcCheckRuntimeScope *instruction) { - fprintf(irp->f, "@checkRuntimeScope("); - ir_print_other_inst_src(irp, instruction->scope_is_comptime); - fprintf(irp->f, ","); - ir_print_other_inst_src(irp, instruction->is_comptime); - fprintf(irp->f, ")"); -} - -static void ir_print_array_to_vector(IrPrintGen *irp, IrInstGenArrayToVector *instruction) { - fprintf(irp->f, "ArrayToVector("); - ir_print_other_inst_gen(irp, instruction->array); - fprintf(irp->f, ")"); -} - -static void ir_print_vector_to_array(IrPrintGen *irp, IrInstGenVectorToArray *instruction) { - fprintf(irp->f, "VectorToArray("); - ir_print_other_inst_gen(irp, instruction->vector); - fprintf(irp->f, ")result="); - ir_print_other_inst_gen(irp, instruction->result_loc); -} - -static void ir_print_ptr_of_array_to_slice(IrPrintGen *irp, IrInstGenPtrOfArrayToSlice *instruction) { - fprintf(irp->f, "PtrOfArrayToSlice("); - ir_print_other_inst_gen(irp, instruction->operand); - fprintf(irp->f, ")result="); - ir_print_other_inst_gen(irp, instruction->result_loc); -} - -static void ir_print_assert_zero(IrPrintGen *irp, IrInstGenAssertZero *instruction) { - fprintf(irp->f, "AssertZero("); - ir_print_other_inst_gen(irp, instruction->target); - fprintf(irp->f, ")"); -} - -static void ir_print_assert_non_null(IrPrintGen *irp, IrInstGenAssertNonNull *instruction) { - fprintf(irp->f, "AssertNonNull("); - ir_print_other_inst_gen(irp, instruction->target); - fprintf(irp->f, ")"); -} - -static void ir_print_alloca_src(IrPrintSrc *irp, IrInstSrcAlloca *instruction) { - fprintf(irp->f, "Alloca(align="); - ir_print_other_inst_src(irp, instruction->align); - fprintf(irp->f, ",name=%s)", instruction->name_hint); -} - -static void ir_print_alloca_gen(IrPrintGen *irp, IrInstGenAlloca *instruction) { - fprintf(irp->f, "Alloca(align=%" PRIu32 ",name=%s)", instruction->align, instruction->name_hint); -} - -static void ir_print_end_expr(IrPrintSrc *irp, IrInstSrcEndExpr *instruction) { - fprintf(irp->f, "EndExpr(result="); - ir_print_result_loc(irp, instruction->result_loc); - fprintf(irp->f, ",value="); - ir_print_other_inst_src(irp, instruction->value); - fprintf(irp->f, ")"); -} - -static void ir_print_int_to_err(IrPrintSrc *irp, IrInstSrcIntToErr *instruction) { - fprintf(irp->f, "inttoerr "); - ir_print_other_inst_src(irp, instruction->target); -} - -static void ir_print_int_to_err(IrPrintGen *irp, IrInstGenIntToErr *instruction) { - fprintf(irp->f, "inttoerr "); - ir_print_other_inst_gen(irp, instruction->target); -} - -static void ir_print_err_to_int(IrPrintSrc *irp, IrInstSrcErrToInt *instruction) { - fprintf(irp->f, "errtoint "); - ir_print_other_inst_src(irp, instruction->target); -} - -static void ir_print_err_to_int(IrPrintGen *irp, IrInstGenErrToInt *instruction) { - fprintf(irp->f, "errtoint "); - ir_print_other_inst_gen(irp, instruction->target); -} - -static void ir_print_check_switch_prongs(IrPrintSrc *irp, IrInstSrcCheckSwitchProngs *instruction) { - fprintf(irp->f, "@checkSwitchProngs("); - ir_print_other_inst_src(irp, instruction->target_value); - fprintf(irp->f, ","); - for (size_t i = 0; i < instruction->range_count; i += 1) { - if (i != 0) - fprintf(irp->f, ","); - ir_print_other_inst_src(irp, instruction->ranges[i].start); - fprintf(irp->f, "..."); - ir_print_other_inst_src(irp, instruction->ranges[i].end); - } - const char *have_else_str = instruction->else_prong != nullptr ? "yes" : "no"; - fprintf(irp->f, ")else:%s", have_else_str); -} - -static void ir_print_check_statement_is_void(IrPrintSrc *irp, IrInstSrcCheckStatementIsVoid *instruction) { - fprintf(irp->f, "@checkStatementIsVoid("); - ir_print_other_inst_src(irp, instruction->statement_value); - fprintf(irp->f, ")"); -} - -static void ir_print_type_name(IrPrintSrc *irp, IrInstSrcTypeName *instruction) { - fprintf(irp->f, "typename "); - ir_print_other_inst_src(irp, instruction->type_value); -} - -static void ir_print_tag_name(IrPrintSrc *irp, IrInstSrcTagName *instruction) { - fprintf(irp->f, "tagname "); - ir_print_other_inst_src(irp, instruction->target); -} - -static void ir_print_tag_name(IrPrintGen *irp, IrInstGenTagName *instruction) { - fprintf(irp->f, "tagname "); - ir_print_other_inst_gen(irp, instruction->target); -} - -static void ir_print_ptr_type(IrPrintSrc *irp, IrInstSrcPtrType *instruction) { - fprintf(irp->f, "&"); - if (instruction->align_value != nullptr) { - fprintf(irp->f, "align("); - ir_print_other_inst_src(irp, instruction->align_value); - fprintf(irp->f, ")"); - } - const char *const_str = instruction->is_const ? "const " : ""; - const char *volatile_str = instruction->is_volatile ? "volatile " : ""; - fprintf(irp->f, ":%" PRIu32 ":%" PRIu32 " %s%s", instruction->bit_offset_start, instruction->host_int_bytes, - const_str, volatile_str); - ir_print_other_inst_src(irp, instruction->child_type); -} - -static void ir_print_decl_ref(IrPrintSrc *irp, IrInstSrcDeclRef *instruction) { - const char *ptr_str = (instruction->lval != LValNone) ? "ptr " : ""; - fprintf(irp->f, "declref %s%s", ptr_str, buf_ptr(instruction->tld->name)); -} - -static void ir_print_panic(IrPrintSrc *irp, IrInstSrcPanic *instruction) { - fprintf(irp->f, "@panic("); - ir_print_other_inst_src(irp, instruction->msg); - fprintf(irp->f, ")"); -} - -static void ir_print_panic(IrPrintGen *irp, IrInstGenPanic *instruction) { - fprintf(irp->f, "@panic("); - ir_print_other_inst_gen(irp, instruction->msg); - fprintf(irp->f, ")"); -} - -static void ir_print_field_parent_ptr(IrPrintSrc *irp, IrInstSrcFieldParentPtr *instruction) { - fprintf(irp->f, "@fieldParentPtr("); - ir_print_other_inst_src(irp, instruction->type_value); - fprintf(irp->f, ","); - ir_print_other_inst_src(irp, instruction->field_name); - fprintf(irp->f, ","); - ir_print_other_inst_src(irp, instruction->field_ptr); - fprintf(irp->f, ")"); -} - -static void ir_print_field_parent_ptr(IrPrintGen *irp, IrInstGenFieldParentPtr *instruction) { - fprintf(irp->f, "@fieldParentPtr(%s,", buf_ptr(instruction->field->name)); - ir_print_other_inst_gen(irp, instruction->field_ptr); - fprintf(irp->f, ")"); -} - -static void ir_print_byte_offset_of(IrPrintSrc *irp, IrInstSrcByteOffsetOf *instruction) { - fprintf(irp->f, "@byte_offset_of("); - ir_print_other_inst_src(irp, instruction->type_value); - fprintf(irp->f, ","); - ir_print_other_inst_src(irp, instruction->field_name); - fprintf(irp->f, ")"); -} - -static void ir_print_bit_offset_of(IrPrintSrc *irp, IrInstSrcBitOffsetOf *instruction) { - fprintf(irp->f, "@bit_offset_of("); - ir_print_other_inst_src(irp, instruction->type_value); - fprintf(irp->f, ","); - ir_print_other_inst_src(irp, instruction->field_name); - fprintf(irp->f, ")"); -} - -static void ir_print_type_info(IrPrintSrc *irp, IrInstSrcTypeInfo *instruction) { - fprintf(irp->f, "@typeInfo("); - ir_print_other_inst_src(irp, instruction->type_value); - fprintf(irp->f, ")"); -} - -static void ir_print_type(IrPrintSrc *irp, IrInstSrcType *instruction) { - fprintf(irp->f, "@Type("); - ir_print_other_inst_src(irp, instruction->type_info); - fprintf(irp->f, ")"); -} - -static void ir_print_has_field(IrPrintSrc *irp, IrInstSrcHasField *instruction) { - fprintf(irp->f, "@hasField("); - ir_print_other_inst_src(irp, instruction->container_type); - fprintf(irp->f, ","); - ir_print_other_inst_src(irp, instruction->field_name); - fprintf(irp->f, ")"); -} - -static void ir_print_set_eval_branch_quota(IrPrintSrc *irp, IrInstSrcSetEvalBranchQuota *instruction) { - fprintf(irp->f, "@setEvalBranchQuota("); - ir_print_other_inst_src(irp, instruction->new_quota); - fprintf(irp->f, ")"); -} - -static void ir_print_align_cast(IrPrintSrc *irp, IrInstSrcAlignCast *instruction) { - fprintf(irp->f, "@alignCast("); - ir_print_other_inst_src(irp, instruction->align_bytes); - fprintf(irp->f, ","); - ir_print_other_inst_src(irp, instruction->target); - fprintf(irp->f, ")"); -} - -static void ir_print_align_cast(IrPrintGen *irp, IrInstGenAlignCast *instruction) { - fprintf(irp->f, "@alignCast("); - ir_print_other_inst_gen(irp, instruction->target); - fprintf(irp->f, ")"); -} - -static void ir_print_resolve_result(IrPrintSrc *irp, IrInstSrcResolveResult *instruction) { - fprintf(irp->f, "ResolveResult("); - ir_print_result_loc(irp, instruction->result_loc); - fprintf(irp->f, ")"); -} - -static void ir_print_reset_result(IrPrintSrc *irp, IrInstSrcResetResult *instruction) { - fprintf(irp->f, "ResetResult("); - ir_print_result_loc(irp, instruction->result_loc); - fprintf(irp->f, ")"); -} - -static void ir_print_set_align_stack(IrPrintSrc *irp, IrInstSrcSetAlignStack *instruction) { - fprintf(irp->f, "@setAlignStack("); - ir_print_other_inst_src(irp, instruction->align_bytes); - fprintf(irp->f, ")"); -} - -static void ir_print_arg_type(IrPrintSrc *irp, IrInstSrcArgType *instruction) { - fprintf(irp->f, "@ArgType("); - ir_print_other_inst_src(irp, instruction->fn_type); - fprintf(irp->f, ","); - ir_print_other_inst_src(irp, instruction->arg_index); - fprintf(irp->f, ")"); -} - -static void ir_print_enum_tag_type(IrPrintSrc *irp, IrInstSrcTagType *instruction) { - fprintf(irp->f, "@TagType("); - ir_print_other_inst_src(irp, instruction->target); - fprintf(irp->f, ")"); -} - -static void ir_print_export(IrPrintSrc *irp, IrInstSrcExport *instruction) { - fprintf(irp->f, "@export("); - ir_print_other_inst_src(irp, instruction->target); - fprintf(irp->f, ","); - ir_print_other_inst_src(irp, instruction->options); - fprintf(irp->f, ")"); -} - -static void ir_print_error_return_trace(IrPrintSrc *irp, IrInstSrcErrorReturnTrace *instruction) { - fprintf(irp->f, "@errorReturnTrace("); - switch (instruction->optional) { - case IrInstErrorReturnTraceNull: - fprintf(irp->f, "Null"); - break; - case IrInstErrorReturnTraceNonNull: - fprintf(irp->f, "NonNull"); - break; - } - fprintf(irp->f, ")"); -} - -static void ir_print_error_return_trace(IrPrintGen *irp, IrInstGenErrorReturnTrace *instruction) { - fprintf(irp->f, "@errorReturnTrace("); - switch (instruction->optional) { - case IrInstErrorReturnTraceNull: - fprintf(irp->f, "Null"); - break; - case IrInstErrorReturnTraceNonNull: - fprintf(irp->f, "NonNull"); - break; - } - fprintf(irp->f, ")"); -} - -static void ir_print_error_union(IrPrintSrc *irp, IrInstSrcErrorUnion *instruction) { - ir_print_other_inst_src(irp, instruction->err_set); - fprintf(irp->f, "!"); - ir_print_other_inst_src(irp, instruction->payload); -} - -static void ir_print_atomic_rmw(IrPrintSrc *irp, IrInstSrcAtomicRmw *instruction) { - fprintf(irp->f, "@atomicRmw("); - ir_print_other_inst_src(irp, instruction->operand_type); - fprintf(irp->f, ","); - ir_print_other_inst_src(irp, instruction->ptr); - fprintf(irp->f, ","); - ir_print_other_inst_src(irp, instruction->op); - fprintf(irp->f, ","); - ir_print_other_inst_src(irp, instruction->operand); - fprintf(irp->f, ","); - ir_print_other_inst_src(irp, instruction->ordering); - fprintf(irp->f, ")"); -} - -static void ir_print_atomic_rmw(IrPrintGen *irp, IrInstGenAtomicRmw *instruction) { - fprintf(irp->f, "@atomicRmw("); - ir_print_other_inst_gen(irp, instruction->ptr); - fprintf(irp->f, ",[TODO print op],"); - ir_print_other_inst_gen(irp, instruction->operand); - fprintf(irp->f, ",%s)", atomic_order_str(instruction->ordering)); -} - -static void ir_print_atomic_load(IrPrintSrc *irp, IrInstSrcAtomicLoad *instruction) { - fprintf(irp->f, "@atomicLoad("); - ir_print_other_inst_src(irp, instruction->operand_type); - fprintf(irp->f, ","); - ir_print_other_inst_src(irp, instruction->ptr); - fprintf(irp->f, ","); - ir_print_other_inst_src(irp, instruction->ordering); - fprintf(irp->f, ")"); -} - -static void ir_print_atomic_load(IrPrintGen *irp, IrInstGenAtomicLoad *instruction) { - fprintf(irp->f, "@atomicLoad("); - ir_print_other_inst_gen(irp, instruction->ptr); - fprintf(irp->f, ",%s)", atomic_order_str(instruction->ordering)); -} - -static void ir_print_atomic_store(IrPrintSrc *irp, IrInstSrcAtomicStore *instruction) { - fprintf(irp->f, "@atomicStore("); - ir_print_other_inst_src(irp, instruction->operand_type); - fprintf(irp->f, ","); - ir_print_other_inst_src(irp, instruction->ptr); - fprintf(irp->f, ","); - ir_print_other_inst_src(irp, instruction->value); - fprintf(irp->f, ","); - ir_print_other_inst_src(irp, instruction->ordering); - fprintf(irp->f, ")"); -} - -static void ir_print_atomic_store(IrPrintGen *irp, IrInstGenAtomicStore *instruction) { - fprintf(irp->f, "@atomicStore("); - ir_print_other_inst_gen(irp, instruction->ptr); - fprintf(irp->f, ","); - ir_print_other_inst_gen(irp, instruction->value); - fprintf(irp->f, ",%s)", atomic_order_str(instruction->ordering)); -} - - -static void ir_print_save_err_ret_addr(IrPrintSrc *irp, IrInstSrcSaveErrRetAddr *instruction) { - fprintf(irp->f, "@saveErrRetAddr()"); -} - -static void ir_print_save_err_ret_addr(IrPrintGen *irp, IrInstGenSaveErrRetAddr *instruction) { - fprintf(irp->f, "@saveErrRetAddr()"); -} - -static void ir_print_add_implicit_return_type(IrPrintSrc *irp, IrInstSrcAddImplicitReturnType *instruction) { - fprintf(irp->f, "@addImplicitReturnType("); - ir_print_other_inst_src(irp, instruction->value); - fprintf(irp->f, ")"); -} - -static void ir_print_float_op(IrPrintSrc *irp, IrInstSrcFloatOp *instruction) { - fprintf(irp->f, "@%s(", float_op_to_name(instruction->fn_id)); - ir_print_other_inst_src(irp, instruction->operand); - fprintf(irp->f, ")"); -} - -static void ir_print_float_op(IrPrintGen *irp, IrInstGenFloatOp *instruction) { - fprintf(irp->f, "@%s(", float_op_to_name(instruction->fn_id)); - ir_print_other_inst_gen(irp, instruction->operand); - fprintf(irp->f, ")"); -} - -static void ir_print_mul_add(IrPrintSrc *irp, IrInstSrcMulAdd *instruction) { - fprintf(irp->f, "@mulAdd("); - ir_print_other_inst_src(irp, instruction->type_value); - fprintf(irp->f, ","); - ir_print_other_inst_src(irp, instruction->op1); - fprintf(irp->f, ","); - ir_print_other_inst_src(irp, instruction->op2); - fprintf(irp->f, ","); - ir_print_other_inst_src(irp, instruction->op3); - fprintf(irp->f, ")"); -} - -static void ir_print_mul_add(IrPrintGen *irp, IrInstGenMulAdd *instruction) { - fprintf(irp->f, "@mulAdd("); - ir_print_other_inst_gen(irp, instruction->op1); - fprintf(irp->f, ","); - ir_print_other_inst_gen(irp, instruction->op2); - fprintf(irp->f, ","); - ir_print_other_inst_gen(irp, instruction->op3); - fprintf(irp->f, ")"); -} - -static void ir_print_decl_var_gen(IrPrintGen *irp, IrInstGenDeclVar *decl_var_instruction) { - ZigVar *var = decl_var_instruction->var; - const char *var_or_const = decl_var_instruction->var->gen_is_const ? "const" : "var"; - const char *name = decl_var_instruction->var->name; - fprintf(irp->f, "%s %s: %s align(%u) = ", var_or_const, name, buf_ptr(&var->var_type->name), - var->align_bytes); - - ir_print_other_inst_gen(irp, decl_var_instruction->var_ptr); -} - -static void ir_print_has_decl(IrPrintSrc *irp, IrInstSrcHasDecl *instruction) { - fprintf(irp->f, "@hasDecl("); - ir_print_other_inst_src(irp, instruction->container); - fprintf(irp->f, ","); - ir_print_other_inst_src(irp, instruction->name); - fprintf(irp->f, ")"); -} - -static void ir_print_undeclared_ident(IrPrintSrc *irp, IrInstSrcUndeclaredIdent *instruction) { - fprintf(irp->f, "@undeclaredIdent(%s)", buf_ptr(instruction->name)); -} - -static void ir_print_union_init_named_field(IrPrintSrc *irp, IrInstSrcUnionInitNamedField *instruction) { - fprintf(irp->f, "@unionInit("); - ir_print_other_inst_src(irp, instruction->union_type); - fprintf(irp->f, ", "); - ir_print_other_inst_src(irp, instruction->field_name); - fprintf(irp->f, ", "); - ir_print_other_inst_src(irp, instruction->field_result_loc); - fprintf(irp->f, ", "); - ir_print_other_inst_src(irp, instruction->result_loc); - fprintf(irp->f, ")"); -} - -static void ir_print_suspend_begin(IrPrintSrc *irp, IrInstSrcSuspendBegin *instruction) { - fprintf(irp->f, "@suspendBegin()"); -} - -static void ir_print_suspend_begin(IrPrintGen *irp, IrInstGenSuspendBegin *instruction) { - fprintf(irp->f, "@suspendBegin()"); -} - -static void ir_print_suspend_finish(IrPrintSrc *irp, IrInstSrcSuspendFinish *instruction) { - fprintf(irp->f, "@suspendFinish()"); -} - -static void ir_print_suspend_finish(IrPrintGen *irp, IrInstGenSuspendFinish *instruction) { - fprintf(irp->f, "@suspendFinish()"); -} - -static void ir_print_resume(IrPrintSrc *irp, IrInstSrcResume *instruction) { - fprintf(irp->f, "resume "); - ir_print_other_inst_src(irp, instruction->frame); -} - -static void ir_print_resume(IrPrintGen *irp, IrInstGenResume *instruction) { - fprintf(irp->f, "resume "); - ir_print_other_inst_gen(irp, instruction->frame); -} - -static void ir_print_await_src(IrPrintSrc *irp, IrInstSrcAwait *instruction) { - fprintf(irp->f, "@await("); - ir_print_other_inst_src(irp, instruction->frame); - fprintf(irp->f, ","); - ir_print_result_loc(irp, instruction->result_loc); - fprintf(irp->f, ")"); -} - -static void ir_print_await_gen(IrPrintGen *irp, IrInstGenAwait *instruction) { - fprintf(irp->f, "@await("); - ir_print_other_inst_gen(irp, instruction->frame); - fprintf(irp->f, ","); - ir_print_other_inst_gen(irp, instruction->result_loc); - fprintf(irp->f, ")"); -} - -static void ir_print_spill_begin(IrPrintSrc *irp, IrInstSrcSpillBegin *instruction) { - fprintf(irp->f, "@spillBegin("); - ir_print_other_inst_src(irp, instruction->operand); - fprintf(irp->f, ")"); -} - -static void ir_print_spill_begin(IrPrintGen *irp, IrInstGenSpillBegin *instruction) { - fprintf(irp->f, "@spillBegin("); - ir_print_other_inst_gen(irp, instruction->operand); - fprintf(irp->f, ")"); -} - -static void ir_print_spill_end(IrPrintSrc *irp, IrInstSrcSpillEnd *instruction) { - fprintf(irp->f, "@spillEnd("); - ir_print_other_inst_src(irp, &instruction->begin->base); - fprintf(irp->f, ")"); -} - -static void ir_print_spill_end(IrPrintGen *irp, IrInstGenSpillEnd *instruction) { - fprintf(irp->f, "@spillEnd("); - ir_print_other_inst_gen(irp, &instruction->begin->base); - fprintf(irp->f, ")"); -} - -static void ir_print_vector_extract_elem(IrPrintGen *irp, IrInstGenVectorExtractElem *instruction) { - fprintf(irp->f, "@vectorExtractElem("); - ir_print_other_inst_gen(irp, instruction->vector); - fprintf(irp->f, ","); - ir_print_other_inst_gen(irp, instruction->index); - fprintf(irp->f, ")"); -} - -static void ir_print_inst_src(IrPrintSrc *irp, IrInstSrc *instruction, bool trailing) { - ir_print_prefix_src(irp, instruction, trailing); - switch (instruction->id) { - case IrInstSrcIdInvalid: - zig_unreachable(); - case IrInstSrcIdReturn: - ir_print_return_src(irp, (IrInstSrcReturn *)instruction); - break; - case IrInstSrcIdConst: - ir_print_const(irp, (IrInstSrcConst *)instruction); - break; - case IrInstSrcIdBinOp: - ir_print_bin_op(irp, (IrInstSrcBinOp *)instruction); - break; - case IrInstSrcIdMergeErrSets: - ir_print_merge_err_sets(irp, (IrInstSrcMergeErrSets *)instruction); - break; - case IrInstSrcIdDeclVar: - ir_print_decl_var_src(irp, (IrInstSrcDeclVar *)instruction); - break; - case IrInstSrcIdCallExtra: - ir_print_call_extra(irp, (IrInstSrcCallExtra *)instruction); - break; - case IrInstSrcIdAsyncCallExtra: - ir_print_async_call_extra(irp, (IrInstSrcAsyncCallExtra *)instruction); - break; - case IrInstSrcIdCall: - ir_print_call_src(irp, (IrInstSrcCall *)instruction); - break; - case IrInstSrcIdCallArgs: - ir_print_call_args(irp, (IrInstSrcCallArgs *)instruction); - break; - case IrInstSrcIdUnOp: - ir_print_un_op(irp, (IrInstSrcUnOp *)instruction); - break; - case IrInstSrcIdCondBr: - ir_print_cond_br(irp, (IrInstSrcCondBr *)instruction); - break; - case IrInstSrcIdBr: - ir_print_br(irp, (IrInstSrcBr *)instruction); - break; - case IrInstSrcIdPhi: - ir_print_phi(irp, (IrInstSrcPhi *)instruction); - break; - case IrInstSrcIdContainerInitList: - ir_print_container_init_list(irp, (IrInstSrcContainerInitList *)instruction); - break; - case IrInstSrcIdContainerInitFields: - ir_print_container_init_fields(irp, (IrInstSrcContainerInitFields *)instruction); - break; - case IrInstSrcIdUnreachable: - ir_print_unreachable(irp, (IrInstSrcUnreachable *)instruction); - break; - case IrInstSrcIdElemPtr: - ir_print_elem_ptr(irp, (IrInstSrcElemPtr *)instruction); - break; - case IrInstSrcIdVarPtr: - ir_print_var_ptr(irp, (IrInstSrcVarPtr *)instruction); - break; - case IrInstSrcIdLoadPtr: - ir_print_load_ptr(irp, (IrInstSrcLoadPtr *)instruction); - break; - case IrInstSrcIdStorePtr: - ir_print_store_ptr(irp, (IrInstSrcStorePtr *)instruction); - break; - case IrInstSrcIdTypeOf: - ir_print_typeof(irp, (IrInstSrcTypeOf *)instruction); - break; - case IrInstSrcIdFieldPtr: - ir_print_field_ptr(irp, (IrInstSrcFieldPtr *)instruction); - break; - case IrInstSrcIdSetCold: - ir_print_set_cold(irp, (IrInstSrcSetCold *)instruction); - break; - case IrInstSrcIdSetRuntimeSafety: - ir_print_set_runtime_safety(irp, (IrInstSrcSetRuntimeSafety *)instruction); - break; - case IrInstSrcIdSetFloatMode: - ir_print_set_float_mode(irp, (IrInstSrcSetFloatMode *)instruction); - break; - case IrInstSrcIdArrayType: - ir_print_array_type(irp, (IrInstSrcArrayType *)instruction); - break; - case IrInstSrcIdSliceType: - ir_print_slice_type(irp, (IrInstSrcSliceType *)instruction); - break; - case IrInstSrcIdAnyFrameType: - ir_print_any_frame_type(irp, (IrInstSrcAnyFrameType *)instruction); - break; - case IrInstSrcIdAsm: - ir_print_asm_src(irp, (IrInstSrcAsm *)instruction); - break; - case IrInstSrcIdSizeOf: - ir_print_size_of(irp, (IrInstSrcSizeOf *)instruction); - break; - case IrInstSrcIdTestNonNull: - ir_print_test_non_null(irp, (IrInstSrcTestNonNull *)instruction); - break; - case IrInstSrcIdOptionalUnwrapPtr: - ir_print_optional_unwrap_ptr(irp, (IrInstSrcOptionalUnwrapPtr *)instruction); - break; - case IrInstSrcIdPopCount: - ir_print_pop_count(irp, (IrInstSrcPopCount *)instruction); - break; - case IrInstSrcIdCtz: - ir_print_ctz(irp, (IrInstSrcCtz *)instruction); - break; - case IrInstSrcIdBswap: - ir_print_bswap(irp, (IrInstSrcBswap *)instruction); - break; - case IrInstSrcIdBitReverse: - ir_print_bit_reverse(irp, (IrInstSrcBitReverse *)instruction); - break; - case IrInstSrcIdSwitchBr: - ir_print_switch_br(irp, (IrInstSrcSwitchBr *)instruction); - break; - case IrInstSrcIdSwitchVar: - ir_print_switch_var(irp, (IrInstSrcSwitchVar *)instruction); - break; - case IrInstSrcIdSwitchElseVar: - ir_print_switch_else_var(irp, (IrInstSrcSwitchElseVar *)instruction); - break; - case IrInstSrcIdSwitchTarget: - ir_print_switch_target(irp, (IrInstSrcSwitchTarget *)instruction); - break; - case IrInstSrcIdImport: - ir_print_import(irp, (IrInstSrcImport *)instruction); - break; - case IrInstSrcIdRef: - ir_print_ref(irp, (IrInstSrcRef *)instruction); - break; - case IrInstSrcIdCompileErr: - ir_print_compile_err(irp, (IrInstSrcCompileErr *)instruction); - break; - case IrInstSrcIdCompileLog: - ir_print_compile_log(irp, (IrInstSrcCompileLog *)instruction); - break; - case IrInstSrcIdErrName: - ir_print_err_name(irp, (IrInstSrcErrName *)instruction); - break; - case IrInstSrcIdCImport: - ir_print_c_import(irp, (IrInstSrcCImport *)instruction); - break; - case IrInstSrcIdCInclude: - ir_print_c_include(irp, (IrInstSrcCInclude *)instruction); - break; - case IrInstSrcIdCDefine: - ir_print_c_define(irp, (IrInstSrcCDefine *)instruction); - break; - case IrInstSrcIdCUndef: - ir_print_c_undef(irp, (IrInstSrcCUndef *)instruction); - break; - case IrInstSrcIdEmbedFile: - ir_print_embed_file(irp, (IrInstSrcEmbedFile *)instruction); - break; - case IrInstSrcIdCmpxchg: - ir_print_cmpxchg_src(irp, (IrInstSrcCmpxchg *)instruction); - break; - case IrInstSrcIdFence: - ir_print_fence(irp, (IrInstSrcFence *)instruction); - break; - case IrInstSrcIdTruncate: - ir_print_truncate(irp, (IrInstSrcTruncate *)instruction); - break; - case IrInstSrcIdIntCast: - ir_print_int_cast(irp, (IrInstSrcIntCast *)instruction); - break; - case IrInstSrcIdFloatCast: - ir_print_float_cast(irp, (IrInstSrcFloatCast *)instruction); - break; - case IrInstSrcIdErrSetCast: - ir_print_err_set_cast(irp, (IrInstSrcErrSetCast *)instruction); - break; - case IrInstSrcIdIntToFloat: - ir_print_int_to_float(irp, (IrInstSrcIntToFloat *)instruction); - break; - case IrInstSrcIdFloatToInt: - ir_print_float_to_int(irp, (IrInstSrcFloatToInt *)instruction); - break; - case IrInstSrcIdBoolToInt: - ir_print_bool_to_int(irp, (IrInstSrcBoolToInt *)instruction); - break; - case IrInstSrcIdVectorType: - ir_print_vector_type(irp, (IrInstSrcVectorType *)instruction); - break; - case IrInstSrcIdShuffleVector: - ir_print_shuffle_vector(irp, (IrInstSrcShuffleVector *)instruction); - break; - case IrInstSrcIdSplat: - ir_print_splat_src(irp, (IrInstSrcSplat *)instruction); - break; - case IrInstSrcIdBoolNot: - ir_print_bool_not(irp, (IrInstSrcBoolNot *)instruction); - break; - case IrInstSrcIdMemset: - ir_print_memset(irp, (IrInstSrcMemset *)instruction); - break; - case IrInstSrcIdMemcpy: - ir_print_memcpy(irp, (IrInstSrcMemcpy *)instruction); - break; - case IrInstSrcIdSlice: - ir_print_slice_src(irp, (IrInstSrcSlice *)instruction); - break; - case IrInstSrcIdBreakpoint: - ir_print_breakpoint(irp, (IrInstSrcBreakpoint *)instruction); - break; - case IrInstSrcIdReturnAddress: - ir_print_return_address(irp, (IrInstSrcReturnAddress *)instruction); - break; - case IrInstSrcIdFrameAddress: - ir_print_frame_address(irp, (IrInstSrcFrameAddress *)instruction); - break; - case IrInstSrcIdFrameHandle: - ir_print_handle(irp, (IrInstSrcFrameHandle *)instruction); - break; - case IrInstSrcIdFrameType: - ir_print_frame_type(irp, (IrInstSrcFrameType *)instruction); - break; - case IrInstSrcIdFrameSize: - ir_print_frame_size_src(irp, (IrInstSrcFrameSize *)instruction); - break; - case IrInstSrcIdAlignOf: - ir_print_align_of(irp, (IrInstSrcAlignOf *)instruction); - break; - case IrInstSrcIdOverflowOp: - ir_print_overflow_op(irp, (IrInstSrcOverflowOp *)instruction); - break; - case IrInstSrcIdTestErr: - ir_print_test_err_src(irp, (IrInstSrcTestErr *)instruction); - break; - case IrInstSrcIdUnwrapErrCode: - ir_print_unwrap_err_code(irp, (IrInstSrcUnwrapErrCode *)instruction); - break; - case IrInstSrcIdUnwrapErrPayload: - ir_print_unwrap_err_payload(irp, (IrInstSrcUnwrapErrPayload *)instruction); - break; - case IrInstSrcIdFnProto: - ir_print_fn_proto(irp, (IrInstSrcFnProto *)instruction); - break; - case IrInstSrcIdTestComptime: - ir_print_test_comptime(irp, (IrInstSrcTestComptime *)instruction); - break; - case IrInstSrcIdPtrCast: - ir_print_ptr_cast_src(irp, (IrInstSrcPtrCast *)instruction); - break; - case IrInstSrcIdBitCast: - ir_print_bit_cast_src(irp, (IrInstSrcBitCast *)instruction); - break; - case IrInstSrcIdPtrToInt: - ir_print_ptr_to_int(irp, (IrInstSrcPtrToInt *)instruction); - break; - case IrInstSrcIdIntToPtr: - ir_print_int_to_ptr(irp, (IrInstSrcIntToPtr *)instruction); - break; - case IrInstSrcIdIntToEnum: - ir_print_int_to_enum(irp, (IrInstSrcIntToEnum *)instruction); - break; - case IrInstSrcIdIntToErr: - ir_print_int_to_err(irp, (IrInstSrcIntToErr *)instruction); - break; - case IrInstSrcIdErrToInt: - ir_print_err_to_int(irp, (IrInstSrcErrToInt *)instruction); - break; - case IrInstSrcIdCheckSwitchProngs: - ir_print_check_switch_prongs(irp, (IrInstSrcCheckSwitchProngs *)instruction); - break; - case IrInstSrcIdCheckStatementIsVoid: - ir_print_check_statement_is_void(irp, (IrInstSrcCheckStatementIsVoid *)instruction); - break; - case IrInstSrcIdTypeName: - ir_print_type_name(irp, (IrInstSrcTypeName *)instruction); - break; - case IrInstSrcIdTagName: - ir_print_tag_name(irp, (IrInstSrcTagName *)instruction); - break; - case IrInstSrcIdPtrType: - ir_print_ptr_type(irp, (IrInstSrcPtrType *)instruction); - break; - case IrInstSrcIdDeclRef: - ir_print_decl_ref(irp, (IrInstSrcDeclRef *)instruction); - break; - case IrInstSrcIdPanic: - ir_print_panic(irp, (IrInstSrcPanic *)instruction); - break; - case IrInstSrcIdFieldParentPtr: - ir_print_field_parent_ptr(irp, (IrInstSrcFieldParentPtr *)instruction); - break; - case IrInstSrcIdByteOffsetOf: - ir_print_byte_offset_of(irp, (IrInstSrcByteOffsetOf *)instruction); - break; - case IrInstSrcIdBitOffsetOf: - ir_print_bit_offset_of(irp, (IrInstSrcBitOffsetOf *)instruction); - break; - case IrInstSrcIdTypeInfo: - ir_print_type_info(irp, (IrInstSrcTypeInfo *)instruction); - break; - case IrInstSrcIdType: - ir_print_type(irp, (IrInstSrcType *)instruction); - break; - case IrInstSrcIdHasField: - ir_print_has_field(irp, (IrInstSrcHasField *)instruction); - break; - case IrInstSrcIdSetEvalBranchQuota: - ir_print_set_eval_branch_quota(irp, (IrInstSrcSetEvalBranchQuota *)instruction); - break; - case IrInstSrcIdAlignCast: - ir_print_align_cast(irp, (IrInstSrcAlignCast *)instruction); - break; - case IrInstSrcIdImplicitCast: - ir_print_implicit_cast(irp, (IrInstSrcImplicitCast *)instruction); - break; - case IrInstSrcIdResolveResult: - ir_print_resolve_result(irp, (IrInstSrcResolveResult *)instruction); - break; - case IrInstSrcIdResetResult: - ir_print_reset_result(irp, (IrInstSrcResetResult *)instruction); - break; - case IrInstSrcIdSetAlignStack: - ir_print_set_align_stack(irp, (IrInstSrcSetAlignStack *)instruction); - break; - case IrInstSrcIdArgType: - ir_print_arg_type(irp, (IrInstSrcArgType *)instruction); - break; - case IrInstSrcIdTagType: - ir_print_enum_tag_type(irp, (IrInstSrcTagType *)instruction); - break; - case IrInstSrcIdExport: - ir_print_export(irp, (IrInstSrcExport *)instruction); - break; - case IrInstSrcIdErrorReturnTrace: - ir_print_error_return_trace(irp, (IrInstSrcErrorReturnTrace *)instruction); - break; - case IrInstSrcIdErrorUnion: - ir_print_error_union(irp, (IrInstSrcErrorUnion *)instruction); - break; - case IrInstSrcIdAtomicRmw: - ir_print_atomic_rmw(irp, (IrInstSrcAtomicRmw *)instruction); - break; - case IrInstSrcIdSaveErrRetAddr: - ir_print_save_err_ret_addr(irp, (IrInstSrcSaveErrRetAddr *)instruction); - break; - case IrInstSrcIdAddImplicitReturnType: - ir_print_add_implicit_return_type(irp, (IrInstSrcAddImplicitReturnType *)instruction); - break; - case IrInstSrcIdFloatOp: - ir_print_float_op(irp, (IrInstSrcFloatOp *)instruction); - break; - case IrInstSrcIdMulAdd: - ir_print_mul_add(irp, (IrInstSrcMulAdd *)instruction); - break; - case IrInstSrcIdAtomicLoad: - ir_print_atomic_load(irp, (IrInstSrcAtomicLoad *)instruction); - break; - case IrInstSrcIdAtomicStore: - ir_print_atomic_store(irp, (IrInstSrcAtomicStore *)instruction); - break; - case IrInstSrcIdEnumToInt: - ir_print_enum_to_int(irp, (IrInstSrcEnumToInt *)instruction); - break; - case IrInstSrcIdCheckRuntimeScope: - ir_print_check_runtime_scope(irp, (IrInstSrcCheckRuntimeScope *)instruction); - break; - case IrInstSrcIdHasDecl: - ir_print_has_decl(irp, (IrInstSrcHasDecl *)instruction); - break; - case IrInstSrcIdUndeclaredIdent: - ir_print_undeclared_ident(irp, (IrInstSrcUndeclaredIdent *)instruction); - break; - case IrInstSrcIdAlloca: - ir_print_alloca_src(irp, (IrInstSrcAlloca *)instruction); - break; - case IrInstSrcIdEndExpr: - ir_print_end_expr(irp, (IrInstSrcEndExpr *)instruction); - break; - case IrInstSrcIdUnionInitNamedField: - ir_print_union_init_named_field(irp, (IrInstSrcUnionInitNamedField *)instruction); - break; - case IrInstSrcIdSuspendBegin: - ir_print_suspend_begin(irp, (IrInstSrcSuspendBegin *)instruction); - break; - case IrInstSrcIdSuspendFinish: - ir_print_suspend_finish(irp, (IrInstSrcSuspendFinish *)instruction); - break; - case IrInstSrcIdResume: - ir_print_resume(irp, (IrInstSrcResume *)instruction); - break; - case IrInstSrcIdAwait: - ir_print_await_src(irp, (IrInstSrcAwait *)instruction); - break; - case IrInstSrcIdSpillBegin: - ir_print_spill_begin(irp, (IrInstSrcSpillBegin *)instruction); - break; - case IrInstSrcIdSpillEnd: - ir_print_spill_end(irp, (IrInstSrcSpillEnd *)instruction); - break; - case IrInstSrcIdClz: - ir_print_clz(irp, (IrInstSrcClz *)instruction); - break; - case IrInstSrcIdWasmMemorySize: - ir_print_wasm_memory_size(irp, (IrInstSrcWasmMemorySize *)instruction); - break; - case IrInstSrcIdWasmMemoryGrow: - ir_print_wasm_memory_grow(irp, (IrInstSrcWasmMemoryGrow *)instruction); - break; - case IrInstSrcIdSrc: - ir_print_builtin_src(irp, (IrInstSrcSrc *)instruction); - break; - } - fprintf(irp->f, "\n"); -} - -static void ir_print_inst_gen(IrPrintGen *irp, IrInstGen *instruction, bool trailing) { - ir_print_prefix_gen(irp, instruction, trailing); - switch (instruction->id) { - case IrInstGenIdInvalid: - zig_unreachable(); - case IrInstGenIdReturn: - ir_print_return_gen(irp, (IrInstGenReturn *)instruction); - break; - case IrInstGenIdConst: - ir_print_const(irp, (IrInstGenConst *)instruction); - break; - case IrInstGenIdBinOp: - ir_print_bin_op(irp, (IrInstGenBinOp *)instruction); - break; - case IrInstGenIdDeclVar: - ir_print_decl_var_gen(irp, (IrInstGenDeclVar *)instruction); - break; - case IrInstGenIdCast: - ir_print_cast(irp, (IrInstGenCast *)instruction); - break; - case IrInstGenIdCall: - ir_print_call_gen(irp, (IrInstGenCall *)instruction); - break; - case IrInstGenIdCondBr: - ir_print_cond_br(irp, (IrInstGenCondBr *)instruction); - break; - case IrInstGenIdBr: - ir_print_br(irp, (IrInstGenBr *)instruction); - break; - case IrInstGenIdPhi: - ir_print_phi(irp, (IrInstGenPhi *)instruction); - break; - case IrInstGenIdUnreachable: - ir_print_unreachable(irp, (IrInstGenUnreachable *)instruction); - break; - case IrInstGenIdElemPtr: - ir_print_elem_ptr(irp, (IrInstGenElemPtr *)instruction); - break; - case IrInstGenIdVarPtr: - ir_print_var_ptr(irp, (IrInstGenVarPtr *)instruction); - break; - case IrInstGenIdReturnPtr: - ir_print_return_ptr(irp, (IrInstGenReturnPtr *)instruction); - break; - case IrInstGenIdLoadPtr: - ir_print_load_ptr_gen(irp, (IrInstGenLoadPtr *)instruction); - break; - case IrInstGenIdStorePtr: - ir_print_store_ptr(irp, (IrInstGenStorePtr *)instruction); - break; - case IrInstGenIdStructFieldPtr: - ir_print_struct_field_ptr(irp, (IrInstGenStructFieldPtr *)instruction); - break; - case IrInstGenIdUnionFieldPtr: - ir_print_union_field_ptr(irp, (IrInstGenUnionFieldPtr *)instruction); - break; - case IrInstGenIdAsm: - ir_print_asm_gen(irp, (IrInstGenAsm *)instruction); - break; - case IrInstGenIdTestNonNull: - ir_print_test_non_null(irp, (IrInstGenTestNonNull *)instruction); - break; - case IrInstGenIdOptionalUnwrapPtr: - ir_print_optional_unwrap_ptr(irp, (IrInstGenOptionalUnwrapPtr *)instruction); - break; - case IrInstGenIdPopCount: - ir_print_pop_count(irp, (IrInstGenPopCount *)instruction); - break; - case IrInstGenIdClz: - ir_print_clz(irp, (IrInstGenClz *)instruction); - break; - case IrInstGenIdCtz: - ir_print_ctz(irp, (IrInstGenCtz *)instruction); - break; - case IrInstGenIdBswap: - ir_print_bswap(irp, (IrInstGenBswap *)instruction); - break; - case IrInstGenIdBitReverse: - ir_print_bit_reverse(irp, (IrInstGenBitReverse *)instruction); - break; - case IrInstGenIdSwitchBr: - ir_print_switch_br(irp, (IrInstGenSwitchBr *)instruction); - break; - case IrInstGenIdUnionTag: - ir_print_union_tag(irp, (IrInstGenUnionTag *)instruction); - break; - case IrInstGenIdRef: - ir_print_ref_gen(irp, (IrInstGenRef *)instruction); - break; - case IrInstGenIdErrName: - ir_print_err_name(irp, (IrInstGenErrName *)instruction); - break; - case IrInstGenIdCmpxchg: - ir_print_cmpxchg_gen(irp, (IrInstGenCmpxchg *)instruction); - break; - case IrInstGenIdFence: - ir_print_fence(irp, (IrInstGenFence *)instruction); - break; - case IrInstGenIdTruncate: - ir_print_truncate(irp, (IrInstGenTruncate *)instruction); - break; - case IrInstGenIdShuffleVector: - ir_print_shuffle_vector(irp, (IrInstGenShuffleVector *)instruction); - break; - case IrInstGenIdSplat: - ir_print_splat_gen(irp, (IrInstGenSplat *)instruction); - break; - case IrInstGenIdBoolNot: - ir_print_bool_not(irp, (IrInstGenBoolNot *)instruction); - break; - case IrInstGenIdMemset: - ir_print_memset(irp, (IrInstGenMemset *)instruction); - break; - case IrInstGenIdMemcpy: - ir_print_memcpy(irp, (IrInstGenMemcpy *)instruction); - break; - case IrInstGenIdSlice: - ir_print_slice_gen(irp, (IrInstGenSlice *)instruction); - break; - case IrInstGenIdBreakpoint: - ir_print_breakpoint(irp, (IrInstGenBreakpoint *)instruction); - break; - case IrInstGenIdReturnAddress: - ir_print_return_address(irp, (IrInstGenReturnAddress *)instruction); - break; - case IrInstGenIdFrameAddress: - ir_print_frame_address(irp, (IrInstGenFrameAddress *)instruction); - break; - case IrInstGenIdFrameHandle: - ir_print_handle(irp, (IrInstGenFrameHandle *)instruction); - break; - case IrInstGenIdFrameSize: - ir_print_frame_size_gen(irp, (IrInstGenFrameSize *)instruction); - break; - case IrInstGenIdOverflowOp: - ir_print_overflow_op(irp, (IrInstGenOverflowOp *)instruction); - break; - case IrInstGenIdTestErr: - ir_print_test_err_gen(irp, (IrInstGenTestErr *)instruction); - break; - case IrInstGenIdUnwrapErrCode: - ir_print_unwrap_err_code(irp, (IrInstGenUnwrapErrCode *)instruction); - break; - case IrInstGenIdUnwrapErrPayload: - ir_print_unwrap_err_payload(irp, (IrInstGenUnwrapErrPayload *)instruction); - break; - case IrInstGenIdOptionalWrap: - ir_print_optional_wrap(irp, (IrInstGenOptionalWrap *)instruction); - break; - case IrInstGenIdErrWrapCode: - ir_print_err_wrap_code(irp, (IrInstGenErrWrapCode *)instruction); - break; - case IrInstGenIdErrWrapPayload: - ir_print_err_wrap_payload(irp, (IrInstGenErrWrapPayload *)instruction); - break; - case IrInstGenIdPtrCast: - ir_print_ptr_cast_gen(irp, (IrInstGenPtrCast *)instruction); - break; - case IrInstGenIdBitCast: - ir_print_bit_cast_gen(irp, (IrInstGenBitCast *)instruction); - break; - case IrInstGenIdWidenOrShorten: - ir_print_widen_or_shorten(irp, (IrInstGenWidenOrShorten *)instruction); - break; - case IrInstGenIdPtrToInt: - ir_print_ptr_to_int(irp, (IrInstGenPtrToInt *)instruction); - break; - case IrInstGenIdIntToPtr: - ir_print_int_to_ptr(irp, (IrInstGenIntToPtr *)instruction); - break; - case IrInstGenIdIntToEnum: - ir_print_int_to_enum(irp, (IrInstGenIntToEnum *)instruction); - break; - case IrInstGenIdIntToErr: - ir_print_int_to_err(irp, (IrInstGenIntToErr *)instruction); - break; - case IrInstGenIdErrToInt: - ir_print_err_to_int(irp, (IrInstGenErrToInt *)instruction); - break; - case IrInstGenIdTagName: - ir_print_tag_name(irp, (IrInstGenTagName *)instruction); - break; - case IrInstGenIdPanic: - ir_print_panic(irp, (IrInstGenPanic *)instruction); - break; - case IrInstGenIdFieldParentPtr: - ir_print_field_parent_ptr(irp, (IrInstGenFieldParentPtr *)instruction); - break; - case IrInstGenIdAlignCast: - ir_print_align_cast(irp, (IrInstGenAlignCast *)instruction); - break; - case IrInstGenIdErrorReturnTrace: - ir_print_error_return_trace(irp, (IrInstGenErrorReturnTrace *)instruction); - break; - case IrInstGenIdAtomicRmw: - ir_print_atomic_rmw(irp, (IrInstGenAtomicRmw *)instruction); - break; - case IrInstGenIdSaveErrRetAddr: - ir_print_save_err_ret_addr(irp, (IrInstGenSaveErrRetAddr *)instruction); - break; - case IrInstGenIdFloatOp: - ir_print_float_op(irp, (IrInstGenFloatOp *)instruction); - break; - case IrInstGenIdMulAdd: - ir_print_mul_add(irp, (IrInstGenMulAdd *)instruction); - break; - case IrInstGenIdAtomicLoad: - ir_print_atomic_load(irp, (IrInstGenAtomicLoad *)instruction); - break; - case IrInstGenIdAtomicStore: - ir_print_atomic_store(irp, (IrInstGenAtomicStore *)instruction); - break; - case IrInstGenIdArrayToVector: - ir_print_array_to_vector(irp, (IrInstGenArrayToVector *)instruction); - break; - case IrInstGenIdVectorToArray: - ir_print_vector_to_array(irp, (IrInstGenVectorToArray *)instruction); - break; - case IrInstGenIdPtrOfArrayToSlice: - ir_print_ptr_of_array_to_slice(irp, (IrInstGenPtrOfArrayToSlice *)instruction); - break; - case IrInstGenIdAssertZero: - ir_print_assert_zero(irp, (IrInstGenAssertZero *)instruction); - break; - case IrInstGenIdAssertNonNull: - ir_print_assert_non_null(irp, (IrInstGenAssertNonNull *)instruction); - break; - case IrInstGenIdAlloca: - ir_print_alloca_gen(irp, (IrInstGenAlloca *)instruction); - break; - case IrInstGenIdSuspendBegin: - ir_print_suspend_begin(irp, (IrInstGenSuspendBegin *)instruction); - break; - case IrInstGenIdSuspendFinish: - ir_print_suspend_finish(irp, (IrInstGenSuspendFinish *)instruction); - break; - case IrInstGenIdResume: - ir_print_resume(irp, (IrInstGenResume *)instruction); - break; - case IrInstGenIdAwait: - ir_print_await_gen(irp, (IrInstGenAwait *)instruction); - break; - case IrInstGenIdSpillBegin: - ir_print_spill_begin(irp, (IrInstGenSpillBegin *)instruction); - break; - case IrInstGenIdSpillEnd: - ir_print_spill_end(irp, (IrInstGenSpillEnd *)instruction); - break; - case IrInstGenIdVectorExtractElem: - ir_print_vector_extract_elem(irp, (IrInstGenVectorExtractElem *)instruction); - break; - case IrInstGenIdVectorStoreElem: - ir_print_vector_store_elem(irp, (IrInstGenVectorStoreElem *)instruction); - break; - case IrInstGenIdBinaryNot: - ir_print_binary_not(irp, (IrInstGenBinaryNot *)instruction); - break; - case IrInstGenIdNegation: - ir_print_negation(irp, (IrInstGenNegation *)instruction); - break; - case IrInstGenIdNegationWrapping: - ir_print_negation_wrapping(irp, (IrInstGenNegationWrapping *)instruction); - break; - case IrInstGenIdWasmMemorySize: - ir_print_wasm_memory_size(irp, (IrInstGenWasmMemorySize *)instruction); - break; - case IrInstGenIdWasmMemoryGrow: - ir_print_wasm_memory_grow(irp, (IrInstGenWasmMemoryGrow *)instruction); - break; - } - fprintf(irp->f, "\n"); -} - -static void irp_print_basic_block_src(IrPrintSrc *irp, IrBasicBlockSrc *current_block) { - fprintf(irp->f, "%s_%" PRIu32 ":\n", current_block->name_hint, current_block->debug_id); - for (size_t instr_i = 0; instr_i < current_block->instruction_list.length; instr_i += 1) { - IrInstSrc *instruction = current_block->instruction_list.at(instr_i); - ir_print_inst_src(irp, instruction, false); - } -} - -static void irp_print_basic_block_gen(IrPrintGen *irp, IrBasicBlockGen *current_block) { - fprintf(irp->f, "%s_%" PRIu32 ":\n", current_block->name_hint, current_block->debug_id); - for (size_t instr_i = 0; instr_i < current_block->instruction_list.length; instr_i += 1) { - IrInstGen *instruction = current_block->instruction_list.at(instr_i); - irp->printed.put(instruction, 0); - irp->pending.clear(); - ir_print_inst_gen(irp, instruction, false); - for (size_t j = 0; j < irp->pending.length; ++j) - ir_print_inst_gen(irp, irp->pending.at(j), true); - } -} - -void ir_print_basic_block_src(CodeGen *codegen, FILE *f, IrBasicBlockSrc *bb, int indent_size) { - IrPrintSrc ir_print = {}; - ir_print.codegen = codegen; - ir_print.f = f; - ir_print.indent = indent_size; - ir_print.indent_size = indent_size; - - irp_print_basic_block_src(&ir_print, bb); -} - -void ir_print_basic_block_gen(CodeGen *codegen, FILE *f, IrBasicBlockGen *bb, int indent_size) { - IrPrintGen ir_print = {}; - ir_print.codegen = codegen; - ir_print.f = f; - ir_print.indent = indent_size; - ir_print.indent_size = indent_size; - ir_print.printed = {}; - ir_print.printed.init(64); - ir_print.pending = {}; - - irp_print_basic_block_gen(&ir_print, bb); - - ir_print.pending.deinit(); - ir_print.printed.deinit(); -} - -void ir_print_src(CodeGen *codegen, FILE *f, IrExecutableSrc *executable, int indent_size) { - IrPrintSrc ir_print = {}; - IrPrintSrc *irp = &ir_print; - irp->codegen = codegen; - irp->f = f; - irp->indent = indent_size; - irp->indent_size = indent_size; - - for (size_t bb_i = 0; bb_i < executable->basic_block_list.length; bb_i += 1) { - irp_print_basic_block_src(irp, executable->basic_block_list.at(bb_i)); - } -} - -void ir_print_gen(CodeGen *codegen, FILE *f, IrExecutableGen *executable, int indent_size) { - IrPrintGen ir_print = {}; - IrPrintGen *irp = &ir_print; - irp->codegen = codegen; - irp->f = f; - irp->indent = indent_size; - irp->indent_size = indent_size; - irp->printed = {}; - irp->printed.init(64); - irp->pending = {}; - - for (size_t bb_i = 0; bb_i < executable->basic_block_list.length; bb_i += 1) { - irp_print_basic_block_gen(irp, executable->basic_block_list.at(bb_i)); - } - - irp->pending.deinit(); - irp->printed.deinit(); -} - -void ir_print_inst_src(CodeGen *codegen, FILE *f, IrInstSrc *instruction, int indent_size) { - IrPrintSrc ir_print = {}; - IrPrintSrc *irp = &ir_print; - irp->codegen = codegen; - irp->f = f; - irp->indent = indent_size; - irp->indent_size = indent_size; - - ir_print_inst_src(irp, instruction, false); -} - -void ir_print_inst_gen(CodeGen *codegen, FILE *f, IrInstGen *instruction, int indent_size) { - IrPrintGen ir_print = {}; - IrPrintGen *irp = &ir_print; - irp->codegen = codegen; - irp->f = f; - irp->indent = indent_size; - irp->indent_size = indent_size; - irp->printed = {}; - irp->printed.init(4); - irp->pending = {}; - - ir_print_inst_gen(irp, instruction, false); -} diff --git a/src/ir_print.hpp b/src/ir_print.hpp deleted file mode 100644 index dde5aaea67e40804f173f1deb681c1a23f617aad..0000000000000000000000000000000000000000 --- a/src/ir_print.hpp +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright (c) 2016 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#ifndef ZIG_IR_PRINT_HPP -#define ZIG_IR_PRINT_HPP - -#include "all_types.hpp" - -#include - -void ir_print_src(CodeGen *codegen, FILE *f, IrExecutableSrc *executable, int indent_size); -void ir_print_gen(CodeGen *codegen, FILE *f, IrExecutableGen *executable, int indent_size); -void ir_print_inst_src(CodeGen *codegen, FILE *f, IrInstSrc *inst, int indent_size); -void ir_print_inst_gen(CodeGen *codegen, FILE *f, IrInstGen *inst, int indent_size); -void ir_print_basic_block_src(CodeGen *codegen, FILE *f, IrBasicBlockSrc *bb, int indent_size); -void ir_print_basic_block_gen(CodeGen *codegen, FILE *f, IrBasicBlockGen *bb, int indent_size); - -const char* ir_inst_src_type_str(IrInstSrcId id); -const char* ir_inst_gen_type_str(IrInstGenId id); - -#endif diff --git a/src/libc_installation.zig b/src/libc_installation.zig new file mode 100644 index 0000000000000000000000000000000000000000..535892ce745b52b4632ace9683ded03d48544545 --- /dev/null +++ b/src/libc_installation.zig @@ -0,0 +1,629 @@ +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 build_options = @import("build_options"); + +const is_darwin = Target.current.isDarwin(); +const is_windows = Target.current.os.tag == .windows; +const is_gnu = Target.current.isGnu(); + +const log = std.log.scoped(.libc_installation); + +usingnamespace @import("windows_sdk.zig"); + +// TODO https://github.com/ziglang/zig/issues/6345 + +/// See the render function implementation for documentation of the fields. +pub const LibCInstallation = struct { + include_dir: ?[]const u8 = null, + 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) { + if (!build_options.have_llvm) + return error.WindowsSdkNotFound; + 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/libcxx.zig b/src/libcxx.zig new file mode 100644 index 0000000000000000000000000000000000000000..19987082aafabc34ff0528067c5b5ff132d70c56 --- /dev/null +++ b/src/libcxx.zig @@ -0,0 +1,315 @@ +const std = @import("std"); +const path = std.fs.path; +const assert = std.debug.assert; + +const target_util = @import("target.zig"); +const Compilation = @import("Compilation.zig"); +const build_options = @import("build_options"); +const trace = @import("tracy.zig").trace; + +const libcxxabi_files = [_][]const u8{ + "src/abort_message.cpp", + "src/cxa_aux_runtime.cpp", + "src/cxa_default_handlers.cpp", + "src/cxa_demangle.cpp", + "src/cxa_exception.cpp", + "src/cxa_exception_storage.cpp", + "src/cxa_guard.cpp", + "src/cxa_handlers.cpp", + "src/cxa_noexception.cpp", + "src/cxa_personality.cpp", + "src/cxa_thread_atexit.cpp", + "src/cxa_unexpected.cpp", + "src/cxa_vector.cpp", + "src/cxa_virtual.cpp", + "src/fallback_malloc.cpp", + "src/private_typeinfo.cpp", + "src/stdlib_exception.cpp", + "src/stdlib_stdexcept.cpp", + "src/stdlib_typeinfo.cpp", +}; + +const libcxx_files = [_][]const u8{ + "src/algorithm.cpp", + "src/any.cpp", + "src/bind.cpp", + "src/charconv.cpp", + "src/chrono.cpp", + "src/condition_variable.cpp", + "src/condition_variable_destructor.cpp", + "src/debug.cpp", + "src/exception.cpp", + "src/experimental/memory_resource.cpp", + "src/filesystem/directory_iterator.cpp", + "src/filesystem/operations.cpp", + "src/functional.cpp", + "src/future.cpp", + "src/hash.cpp", + "src/ios.cpp", + "src/iostream.cpp", + "src/locale.cpp", + "src/memory.cpp", + "src/mutex.cpp", + "src/mutex_destructor.cpp", + "src/new.cpp", + "src/optional.cpp", + "src/random.cpp", + "src/regex.cpp", + "src/shared_mutex.cpp", + "src/stdexcept.cpp", + "src/string.cpp", + "src/strstream.cpp", + "src/support/solaris/xlocale.cpp", + "src/support/win32/locale_win32.cpp", + "src/support/win32/support.cpp", + "src/support/win32/thread_win32.cpp", + "src/system_error.cpp", + "src/thread.cpp", + "src/typeinfo.cpp", + "src/utility.cpp", + "src/valarray.cpp", + "src/variant.cpp", + "src/vector.cpp", +}; + +pub fn buildLibCXX(comp: *Compilation) !void { + if (!build_options.have_llvm) { + return error.ZigCompilerNotBuiltWithLLVMExtensions; + } + + const tracy = trace(@src()); + defer tracy.end(); + + var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); + defer arena_allocator.deinit(); + const arena = &arena_allocator.allocator; + + const root_name = "c++"; + const output_mode = .Lib; + const link_mode = .Static; + const target = comp.getTarget(); + const basename = try std.zig.binNameAlloc(arena, .{ + .root_name = root_name, + .target = target, + .output_mode = output_mode, + .link_mode = link_mode, + }); + + const emit_bin = Compilation.EmitLoc{ + .directory = null, // Put it in the cache directory. + .basename = basename, + }; + + const cxxabi_include_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxxabi", "include" }); + const cxx_include_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxx", "include" }); + var c_source_files = std.ArrayList(Compilation.CSourceFile).init(arena); + try c_source_files.ensureCapacity(libcxx_files.len); + + for (libcxx_files) |cxx_src| { + var cflags = std.ArrayList([]const u8).init(arena); + + if (target.os.tag == .windows) { + // Filesystem stuff isn't supported on Windows. + if (std.mem.startsWith(u8, cxx_src, "src/filesystem/")) + continue; + } else { + if (std.mem.startsWith(u8, cxx_src, "src/support/win32/")) + continue; + } + + try cflags.append("-DNDEBUG"); + try cflags.append("-D_LIBCPP_BUILDING_LIBRARY"); + try cflags.append("-D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER"); + try cflags.append("-DLIBCXX_BUILDING_LIBCXXABI"); + try cflags.append("-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS"); + try cflags.append("-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS"); + + if (target.abi.isMusl()) { + try cflags.append("-D_LIBCPP_HAS_MUSL_LIBC"); + } + + try cflags.append("-I"); + try cflags.append(cxx_include_path); + + try cflags.append("-I"); + try cflags.append(cxxabi_include_path); + + try cflags.append("-O3"); + try cflags.append("-DNDEBUG"); + if (target_util.supports_fpic(target)) { + try cflags.append("-fPIC"); + } + try cflags.append("-nostdinc++"); + try cflags.append("-fvisibility-inlines-hidden"); + try cflags.append("-std=c++14"); + try cflags.append("-Wno-user-defined-literals"); + + c_source_files.appendAssumeCapacity(.{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxx", cxx_src }), + .extra_flags = cflags.items, + }); + } + + const sub_compilation = try Compilation.create(comp.gpa, .{ + .local_cache_directory = comp.global_cache_directory, + .global_cache_directory = comp.global_cache_directory, + .zig_lib_directory = comp.zig_lib_directory, + .target = target, + .root_name = root_name, + .root_pkg = null, + .output_mode = output_mode, + .rand = comp.rand, + .libc_installation = comp.bin_file.options.libc_installation, + .emit_bin = emit_bin, + .optimize_mode = comp.bin_file.options.optimize_mode, + .link_mode = link_mode, + .want_sanitize_c = false, + .want_stack_check = false, + .want_valgrind = false, + .want_pic = comp.bin_file.options.pic, + .emit_h = null, + .strip = comp.bin_file.options.strip, + .is_native_os = comp.bin_file.options.is_native_os, + .self_exe_path = comp.self_exe_path, + .c_source_files = c_source_files.items, + .verbose_cc = comp.verbose_cc, + .verbose_link = comp.bin_file.options.verbose_link, + .verbose_tokenize = comp.verbose_tokenize, + .verbose_ast = comp.verbose_ast, + .verbose_ir = comp.verbose_ir, + .verbose_llvm_ir = comp.verbose_llvm_ir, + .verbose_cimport = comp.verbose_cimport, + .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, + .clang_passthrough_mode = comp.clang_passthrough_mode, + .link_libc = true, + }); + defer sub_compilation.destroy(); + + try sub_compilation.updateSubCompilation(); + + assert(comp.libcxx_static_lib == null); + comp.libcxx_static_lib = Compilation.CRTFile{ + .full_object_path = try sub_compilation.bin_file.options.emit.?.directory.join( + comp.gpa, + &[_][]const u8{basename}, + ), + .lock = sub_compilation.bin_file.toOwnedLock(), + }; +} + +pub fn buildLibCXXABI(comp: *Compilation) !void { + if (!build_options.have_llvm) { + return error.ZigCompilerNotBuiltWithLLVMExtensions; + } + + const tracy = trace(@src()); + defer tracy.end(); + + var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); + defer arena_allocator.deinit(); + const arena = &arena_allocator.allocator; + + const root_name = "c++abi"; + const output_mode = .Lib; + const link_mode = .Static; + const target = comp.getTarget(); + const basename = try std.zig.binNameAlloc(arena, .{ + .root_name = root_name, + .target = target, + .output_mode = output_mode, + .link_mode = link_mode, + }); + + const emit_bin = Compilation.EmitLoc{ + .directory = null, // Put it in the cache directory. + .basename = basename, + }; + + const cxxabi_include_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxxabi", "include" }); + const cxx_include_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxx", "include" }); + + var c_source_files: [libcxxabi_files.len]Compilation.CSourceFile = undefined; + for (libcxxabi_files) |cxxabi_src, i| { + var cflags = std.ArrayList([]const u8).init(arena); + + try cflags.append("-DHAVE___CXA_THREAD_ATEXIT_IMPL"); + try cflags.append("-D_LIBCPP_DISABLE_EXTERN_TEMPLATE"); + try cflags.append("-D_LIBCPP_ENABLE_CXX17_REMOVED_UNEXPECTED_FUNCTIONS"); + try cflags.append("-D_LIBCXXABI_BUILDING_LIBRARY"); + try cflags.append("-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS"); + try cflags.append("-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS"); + + if (target.abi.isMusl()) { + try cflags.append("-D_LIBCPP_HAS_MUSL_LIBC"); + } + + try cflags.append("-I"); + try cflags.append(cxxabi_include_path); + + try cflags.append("-I"); + try cflags.append(cxx_include_path); + + try cflags.append("-O3"); + try cflags.append("-DNDEBUG"); + if (target_util.supports_fpic(target)) { + try cflags.append("-fPIC"); + } + try cflags.append("-nostdinc++"); + try cflags.append("-fstrict-aliasing"); + try cflags.append("-funwind-tables"); + try cflags.append("-D_DEBUG"); + try cflags.append("-UNDEBUG"); + try cflags.append("-std=c++11"); + + c_source_files[i] = .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxxabi", cxxabi_src }), + .extra_flags = cflags.items, + }; + } + + const sub_compilation = try Compilation.create(comp.gpa, .{ + .local_cache_directory = comp.global_cache_directory, + .global_cache_directory = comp.global_cache_directory, + .zig_lib_directory = comp.zig_lib_directory, + .target = target, + .root_name = root_name, + .root_pkg = null, + .output_mode = output_mode, + .rand = comp.rand, + .libc_installation = comp.bin_file.options.libc_installation, + .emit_bin = emit_bin, + .optimize_mode = comp.bin_file.options.optimize_mode, + .link_mode = link_mode, + .want_sanitize_c = false, + .want_stack_check = false, + .want_valgrind = false, + .want_pic = comp.bin_file.options.pic, + .emit_h = null, + .strip = comp.bin_file.options.strip, + .is_native_os = comp.bin_file.options.is_native_os, + .self_exe_path = comp.self_exe_path, + .c_source_files = &c_source_files, + .verbose_cc = comp.verbose_cc, + .verbose_link = comp.bin_file.options.verbose_link, + .verbose_tokenize = comp.verbose_tokenize, + .verbose_ast = comp.verbose_ast, + .verbose_ir = comp.verbose_ir, + .verbose_llvm_ir = comp.verbose_llvm_ir, + .verbose_cimport = comp.verbose_cimport, + .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, + .clang_passthrough_mode = comp.clang_passthrough_mode, + .link_libc = true, + }); + defer sub_compilation.destroy(); + + try sub_compilation.updateSubCompilation(); + + assert(comp.libcxxabi_static_lib == null); + comp.libcxxabi_static_lib = Compilation.CRTFile{ + .full_object_path = try sub_compilation.bin_file.options.emit.?.directory.join( + comp.gpa, + &[_][]const u8{basename}, + ), + .lock = sub_compilation.bin_file.toOwnedLock(), + }; +} diff --git a/src/libunwind.zig b/src/libunwind.zig new file mode 100644 index 0000000000000000000000000000000000000000..d47eed40dd08b4bae900ea4d3b2386de262a42bb --- /dev/null +++ b/src/libunwind.zig @@ -0,0 +1,135 @@ +const std = @import("std"); +const path = std.fs.path; +const assert = std.debug.assert; + +const target_util = @import("target.zig"); +const Compilation = @import("Compilation.zig"); +const build_options = @import("build_options"); +const trace = @import("tracy.zig").trace; + +pub fn buildStaticLib(comp: *Compilation) !void { + if (!build_options.have_llvm) { + return error.ZigCompilerNotBuiltWithLLVMExtensions; + } + + const tracy = trace(@src()); + defer tracy.end(); + + var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); + defer arena_allocator.deinit(); + const arena = &arena_allocator.allocator; + + const root_name = "unwind"; + const output_mode = .Lib; + const link_mode = .Static; + const target = comp.getTarget(); + const basename = try std.zig.binNameAlloc(arena, .{ + .root_name = root_name, + .target = target, + .output_mode = output_mode, + .link_mode = link_mode, + }); + const emit_bin = Compilation.EmitLoc{ + .directory = null, // Put it in the cache directory. + .basename = basename, + }; + const unwind_src_list = [_][]const u8{ + "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "libunwind.cpp", + "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "Unwind-EHABI.cpp", + "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "Unwind-seh.cpp", + "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "UnwindLevel1.c", + "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "UnwindLevel1-gcc-ext.c", + "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "Unwind-sjlj.c", + "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "UnwindRegistersRestore.S", + "libunwind" ++ path.sep_str ++ "src" ++ path.sep_str ++ "UnwindRegistersSave.S", + }; + var c_source_files: [unwind_src_list.len]Compilation.CSourceFile = undefined; + for (unwind_src_list) |unwind_src, i| { + var cflags = std.ArrayList([]const u8).init(arena); + + switch (Compilation.classifyFileExt(unwind_src)) { + .c => { + try cflags.append("-std=c99"); + }, + .cpp => { + try cflags.appendSlice(&[_][]const u8{ + "-fno-rtti", + "-I", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libcxx", "include" }), + }); + }, + .assembly => {}, + else => unreachable, // You can see the entire list of files just above. + } + try cflags.append("-I"); + try cflags.append(try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libunwind", "include" })); + if (target_util.supports_fpic(target)) { + try cflags.append("-fPIC"); + } + try cflags.append("-D_LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS"); + try cflags.append("-Wa,--noexecstack"); + + // This is intentionally always defined because the macro definition means, should it only + // build for the target specified by compiler defines. Since we pass -target the compiler + // defines will be correct. + try cflags.append("-D_LIBUNWIND_IS_NATIVE_ONLY"); + + if (comp.bin_file.options.optimize_mode == .Debug) { + try cflags.append("-D_DEBUG"); + } + if (comp.bin_file.options.single_threaded) { + try cflags.append("-D_LIBUNWIND_HAS_NO_THREADS"); + } + try cflags.append("-Wno-bitwise-conditional-parentheses"); + + c_source_files[i] = .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{unwind_src}), + .extra_flags = cflags.items, + }; + } + const sub_compilation = try Compilation.create(comp.gpa, .{ + .local_cache_directory = comp.global_cache_directory, + .global_cache_directory = comp.global_cache_directory, + .zig_lib_directory = comp.zig_lib_directory, + .target = target, + .root_name = root_name, + .root_pkg = null, + .output_mode = output_mode, + .rand = comp.rand, + .libc_installation = comp.bin_file.options.libc_installation, + .emit_bin = emit_bin, + .optimize_mode = comp.bin_file.options.optimize_mode, + .link_mode = link_mode, + .want_sanitize_c = false, + .want_stack_check = false, + .want_valgrind = false, + .want_pic = comp.bin_file.options.pic, + .emit_h = null, + .strip = comp.bin_file.options.strip, + .is_native_os = comp.bin_file.options.is_native_os, + .self_exe_path = comp.self_exe_path, + .c_source_files = &c_source_files, + .verbose_cc = comp.verbose_cc, + .verbose_link = comp.bin_file.options.verbose_link, + .verbose_tokenize = comp.verbose_tokenize, + .verbose_ast = comp.verbose_ast, + .verbose_ir = comp.verbose_ir, + .verbose_llvm_ir = comp.verbose_llvm_ir, + .verbose_cimport = comp.verbose_cimport, + .verbose_llvm_cpu_features = comp.verbose_llvm_cpu_features, + .clang_passthrough_mode = comp.clang_passthrough_mode, + .link_libc = true, + }); + defer sub_compilation.destroy(); + + try sub_compilation.updateSubCompilation(); + + assert(comp.libunwind_static_lib == null); + comp.libunwind_static_lib = Compilation.CRTFile{ + .full_object_path = try sub_compilation.bin_file.options.emit.?.directory.join( + comp.gpa, + &[_][]const u8{basename}, + ), + .lock = sub_compilation.bin_file.toOwnedLock(), + }; +} diff --git a/src/link.cpp b/src/link.cpp deleted file mode 100644 index b54fdf6b93175fa753a023adf60d5f3e5bd5702e..0000000000000000000000000000000000000000 --- a/src/link.cpp +++ /dev/null @@ -1,2984 +0,0 @@ -/* - * Copyright (c) 2015 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#include "os.hpp" -#include "config.h" -#include "codegen.hpp" -#include "analyze.hpp" -#include "compiler.hpp" -#include "install_files.h" -#include "glibc.hpp" - -static const char *msvcrt_common_src[] = { - "misc" OS_SEP "_create_locale.c", - "misc" OS_SEP "_free_locale.c", - "misc" OS_SEP "onexit_table.c", - "misc" OS_SEP "register_tls_atexit.c", - "stdio" OS_SEP "acrt_iob_func.c", - "misc" OS_SEP "_configthreadlocale.c", - "misc" OS_SEP "_get_current_locale.c", - "misc" OS_SEP "invalid_parameter_handler.c", - "misc" OS_SEP "output_format.c", - "misc" OS_SEP "purecall.c", - "secapi" OS_SEP "_access_s.c", - "secapi" OS_SEP "_cgets_s.c", - "secapi" OS_SEP "_cgetws_s.c", - "secapi" OS_SEP "_chsize_s.c", - "secapi" OS_SEP "_controlfp_s.c", - "secapi" OS_SEP "_cprintf_s.c", - "secapi" OS_SEP "_cprintf_s_l.c", - "secapi" OS_SEP "_ctime32_s.c", - "secapi" OS_SEP "_ctime64_s.c", - "secapi" OS_SEP "_cwprintf_s.c", - "secapi" OS_SEP "_cwprintf_s_l.c", - "secapi" OS_SEP "_gmtime32_s.c", - "secapi" OS_SEP "_gmtime64_s.c", - "secapi" OS_SEP "_localtime32_s.c", - "secapi" OS_SEP "_localtime64_s.c", - "secapi" OS_SEP "_mktemp_s.c", - "secapi" OS_SEP "_sopen_s.c", - "secapi" OS_SEP "_strdate_s.c", - "secapi" OS_SEP "_strtime_s.c", - "secapi" OS_SEP "_umask_s.c", - "secapi" OS_SEP "_vcprintf_s.c", - "secapi" OS_SEP "_vcprintf_s_l.c", - "secapi" OS_SEP "_vcwprintf_s.c", - "secapi" OS_SEP "_vcwprintf_s_l.c", - "secapi" OS_SEP "_vscprintf_p.c", - "secapi" OS_SEP "_vscwprintf_p.c", - "secapi" OS_SEP "_vswprintf_p.c", - "secapi" OS_SEP "_waccess_s.c", - "secapi" OS_SEP "_wasctime_s.c", - "secapi" OS_SEP "_wctime32_s.c", - "secapi" OS_SEP "_wctime64_s.c", - "secapi" OS_SEP "_wstrtime_s.c", - "secapi" OS_SEP "_wmktemp_s.c", - "secapi" OS_SEP "_wstrdate_s.c", - "secapi" OS_SEP "asctime_s.c", - "secapi" OS_SEP "memcpy_s.c", - "secapi" OS_SEP "memmove_s.c", - "secapi" OS_SEP "rand_s.c", - "secapi" OS_SEP "sprintf_s.c", - "secapi" OS_SEP "strerror_s.c", - "secapi" OS_SEP "vsprintf_s.c", - "secapi" OS_SEP "wmemcpy_s.c", - "secapi" OS_SEP "wmemmove_s.c", - "stdio" OS_SEP "mingw_lock.c", -}; - -static const char *msvcrt_i386_src[] = { - "misc" OS_SEP "lc_locale_func.c", - "misc" OS_SEP "___mb_cur_max_func.c", -}; - -static const char *msvcrt_other_src[] = { - "misc" OS_SEP "__p___argv.c", - "misc" OS_SEP "__p__acmdln.c", - "misc" OS_SEP "__p__fmode.c", - "misc" OS_SEP "__p__wcmdln.c", -}; - -static const char *mingwex_generic_src[] = { - "complex" OS_SEP "_cabs.c", - "complex" OS_SEP "cabs.c", - "complex" OS_SEP "cabsf.c", - "complex" OS_SEP "cabsl.c", - "complex" OS_SEP "cacos.c", - "complex" OS_SEP "cacosf.c", - "complex" OS_SEP "cacosl.c", - "complex" OS_SEP "carg.c", - "complex" OS_SEP "cargf.c", - "complex" OS_SEP "cargl.c", - "complex" OS_SEP "casin.c", - "complex" OS_SEP "casinf.c", - "complex" OS_SEP "casinl.c", - "complex" OS_SEP "catan.c", - "complex" OS_SEP "catanf.c", - "complex" OS_SEP "catanl.c", - "complex" OS_SEP "ccos.c", - "complex" OS_SEP "ccosf.c", - "complex" OS_SEP "ccosl.c", - "complex" OS_SEP "cexp.c", - "complex" OS_SEP "cexpf.c", - "complex" OS_SEP "cexpl.c", - "complex" OS_SEP "cimag.c", - "complex" OS_SEP "cimagf.c", - "complex" OS_SEP "cimagl.c", - "complex" OS_SEP "clog.c", - "complex" OS_SEP "clog10.c", - "complex" OS_SEP "clog10f.c", - "complex" OS_SEP "clog10l.c", - "complex" OS_SEP "clogf.c", - "complex" OS_SEP "clogl.c", - "complex" OS_SEP "conj.c", - "complex" OS_SEP "conjf.c", - "complex" OS_SEP "conjl.c", - "complex" OS_SEP "cpow.c", - "complex" OS_SEP "cpowf.c", - "complex" OS_SEP "cpowl.c", - "complex" OS_SEP "cproj.c", - "complex" OS_SEP "cprojf.c", - "complex" OS_SEP "cprojl.c", - "complex" OS_SEP "creal.c", - "complex" OS_SEP "crealf.c", - "complex" OS_SEP "creall.c", - "complex" OS_SEP "csin.c", - "complex" OS_SEP "csinf.c", - "complex" OS_SEP "csinl.c", - "complex" OS_SEP "csqrt.c", - "complex" OS_SEP "csqrtf.c", - "complex" OS_SEP "csqrtl.c", - "complex" OS_SEP "ctan.c", - "complex" OS_SEP "ctanf.c", - "complex" OS_SEP "ctanl.c", - "crt" OS_SEP "dllentry.c", - "crt" OS_SEP "dllmain.c", - "gdtoa" OS_SEP "arithchk.c", - "gdtoa" OS_SEP "dmisc.c", - "gdtoa" OS_SEP "dtoa.c", - "gdtoa" OS_SEP "g__fmt.c", - "gdtoa" OS_SEP "g_dfmt.c", - "gdtoa" OS_SEP "g_ffmt.c", - "gdtoa" OS_SEP "g_xfmt.c", - "gdtoa" OS_SEP "gdtoa.c", - "gdtoa" OS_SEP "gethex.c", - "gdtoa" OS_SEP "gmisc.c", - "gdtoa" OS_SEP "hd_init.c", - "gdtoa" OS_SEP "hexnan.c", - "gdtoa" OS_SEP "misc.c", - "gdtoa" OS_SEP "qnan.c", - "gdtoa" OS_SEP "smisc.c", - "gdtoa" OS_SEP "strtodg.c", - "gdtoa" OS_SEP "strtodnrp.c", - "gdtoa" OS_SEP "strtof.c", - "gdtoa" OS_SEP "strtopx.c", - "gdtoa" OS_SEP "sum.c", - "gdtoa" OS_SEP "ulp.c", - "math" OS_SEP "abs64.c", - "math" OS_SEP "cbrt.c", - "math" OS_SEP "cbrtf.c", - "math" OS_SEP "cbrtl.c", - "math" OS_SEP "cephes_emath.c", - "math" OS_SEP "copysign.c", - "math" OS_SEP "copysignf.c", - "math" OS_SEP "coshf.c", - "math" OS_SEP "coshl.c", - "math" OS_SEP "erfl.c", - "math" OS_SEP "expf.c", - "math" OS_SEP "fabs.c", - "math" OS_SEP "fabsf.c", - "math" OS_SEP "fabsl.c", - "math" OS_SEP "fdim.c", - "math" OS_SEP "fdimf.c", - "math" OS_SEP "fdiml.c", - "math" OS_SEP "fma.c", - "math" OS_SEP "fmaf.c", - "math" OS_SEP "fmal.c", - "math" OS_SEP "fmax.c", - "math" OS_SEP "fmaxf.c", - "math" OS_SEP "fmaxl.c", - "math" OS_SEP "fmin.c", - "math" OS_SEP "fminf.c", - "math" OS_SEP "fminl.c", - "math" OS_SEP "fp_consts.c", - "math" OS_SEP "fp_constsf.c", - "math" OS_SEP "fp_constsl.c", - "math" OS_SEP "fpclassify.c", - "math" OS_SEP "fpclassifyf.c", - "math" OS_SEP "fpclassifyl.c", - "math" OS_SEP "frexpf.c", - "math" OS_SEP "hypot.c", - "math" OS_SEP "hypotf.c", - "math" OS_SEP "hypotl.c", - "math" OS_SEP "isnan.c", - "math" OS_SEP "isnanf.c", - "math" OS_SEP "isnanl.c", - "math" OS_SEP "ldexpf.c", - "math" OS_SEP "lgamma.c", - "math" OS_SEP "lgammaf.c", - "math" OS_SEP "lgammal.c", - "math" OS_SEP "llrint.c", - "math" OS_SEP "llrintf.c", - "math" OS_SEP "llrintl.c", - "math" OS_SEP "llround.c", - "math" OS_SEP "llroundf.c", - "math" OS_SEP "llroundl.c", - "math" OS_SEP "log10f.c", - "math" OS_SEP "logf.c", - "math" OS_SEP "lrint.c", - "math" OS_SEP "lrintf.c", - "math" OS_SEP "lrintl.c", - "math" OS_SEP "lround.c", - "math" OS_SEP "lroundf.c", - "math" OS_SEP "lroundl.c", - "math" OS_SEP "modf.c", - "math" OS_SEP "modff.c", - "math" OS_SEP "modfl.c", - "math" OS_SEP "nextafterf.c", - "math" OS_SEP "nextafterl.c", - "math" OS_SEP "nexttoward.c", - "math" OS_SEP "nexttowardf.c", - "math" OS_SEP "powf.c", - "math" OS_SEP "powi.c", - "math" OS_SEP "powif.c", - "math" OS_SEP "powil.c", - "math" OS_SEP "rint.c", - "math" OS_SEP "rintf.c", - "math" OS_SEP "rintl.c", - "math" OS_SEP "round.c", - "math" OS_SEP "roundf.c", - "math" OS_SEP "roundl.c", - "math" OS_SEP "s_erf.c", - "math" OS_SEP "sf_erf.c", - "math" OS_SEP "signbit.c", - "math" OS_SEP "signbitf.c", - "math" OS_SEP "signbitl.c", - "math" OS_SEP "signgam.c", - "math" OS_SEP "sinhf.c", - "math" OS_SEP "sinhl.c", - "math" OS_SEP "sqrt.c", - "math" OS_SEP "sqrtf.c", - "math" OS_SEP "sqrtl.c", - "math" OS_SEP "tanhf.c", - "math" OS_SEP "tanhl.c", - "math" OS_SEP "tgamma.c", - "math" OS_SEP "tgammaf.c", - "math" OS_SEP "tgammal.c", - "math" OS_SEP "truncl.c", - "misc" OS_SEP "alarm.c", - "misc" OS_SEP "basename.c", - "misc" OS_SEP "btowc.c", - "misc" OS_SEP "delay-f.c", - "misc" OS_SEP "delay-n.c", - "misc" OS_SEP "delayimp.c", - "misc" OS_SEP "dirent.c", - "misc" OS_SEP "dirname.c", - "misc" OS_SEP "feclearexcept.c", - "misc" OS_SEP "fegetenv.c", - "misc" OS_SEP "fegetexceptflag.c", - "misc" OS_SEP "fegetround.c", - "misc" OS_SEP "feholdexcept.c", - "misc" OS_SEP "feraiseexcept.c", - "misc" OS_SEP "fesetenv.c", - "misc" OS_SEP "fesetexceptflag.c", - "misc" OS_SEP "fesetround.c", - "misc" OS_SEP "fetestexcept.c", - "misc" OS_SEP "feupdateenv.c", - "misc" OS_SEP "ftruncate.c", - "misc" OS_SEP "ftw.c", - "misc" OS_SEP "ftw64.c", - "misc" OS_SEP "fwide.c", - "misc" OS_SEP "getlogin.c", - "misc" OS_SEP "getopt.c", - "misc" OS_SEP "gettimeofday.c", - "misc" OS_SEP "imaxabs.c", - "misc" OS_SEP "imaxdiv.c", - "misc" OS_SEP "isblank.c", - "misc" OS_SEP "iswblank.c", - "misc" OS_SEP "mbrtowc.c", - "misc" OS_SEP "mbsinit.c", - "misc" OS_SEP "mempcpy.c", - "misc" OS_SEP "mingw-aligned-malloc.c", - "misc" OS_SEP "mingw-fseek.c", - "misc" OS_SEP "mingw_getsp.S", - "misc" OS_SEP "mingw_matherr.c", - "misc" OS_SEP "mingw_mbwc_convert.c", - "misc" OS_SEP "mingw_usleep.c", - "misc" OS_SEP "mingw_wcstod.c", - "misc" OS_SEP "mingw_wcstof.c", - "misc" OS_SEP "mingw_wcstold.c", - "misc" OS_SEP "mkstemp.c", - "misc" OS_SEP "seterrno.c", - "misc" OS_SEP "sleep.c", - "misc" OS_SEP "strnlen.c", - "misc" OS_SEP "strsafe.c", - "misc" OS_SEP "strtoimax.c", - "misc" OS_SEP "strtold.c", - "misc" OS_SEP "strtoumax.c", - "misc" OS_SEP "tdelete.c", - "misc" OS_SEP "tfind.c", - "misc" OS_SEP "tsearch.c", - "misc" OS_SEP "twalk.c", - "misc" OS_SEP "uchar_c16rtomb.c", - "misc" OS_SEP "uchar_c32rtomb.c", - "misc" OS_SEP "uchar_mbrtoc16.c", - "misc" OS_SEP "uchar_mbrtoc32.c", - "misc" OS_SEP "wassert.c", - "misc" OS_SEP "wcrtomb.c", - "misc" OS_SEP "wcsnlen.c", - "misc" OS_SEP "wcstof.c", - "misc" OS_SEP "wcstoimax.c", - "misc" OS_SEP "wcstold.c", - "misc" OS_SEP "wcstoumax.c", - "misc" OS_SEP "wctob.c", - "misc" OS_SEP "wctrans.c", - "misc" OS_SEP "wctype.c", - "misc" OS_SEP "wdirent.c", - "misc" OS_SEP "winbs_uint64.c", - "misc" OS_SEP "winbs_ulong.c", - "misc" OS_SEP "winbs_ushort.c", - "misc" OS_SEP "wmemchr.c", - "misc" OS_SEP "wmemcmp.c", - "misc" OS_SEP "wmemcpy.c", - "misc" OS_SEP "wmemmove.c", - "misc" OS_SEP "wmempcpy.c", - "misc" OS_SEP "wmemset.c", - "stdio" OS_SEP "_Exit.c", - "stdio" OS_SEP "_findfirst64i32.c", - "stdio" OS_SEP "_findnext64i32.c", - "stdio" OS_SEP "_fstat.c", - "stdio" OS_SEP "_fstat64i32.c", - "stdio" OS_SEP "_ftime.c", - "stdio" OS_SEP "_getc_nolock.c", - "stdio" OS_SEP "_getwc_nolock.c", - "stdio" OS_SEP "_putc_nolock.c", - "stdio" OS_SEP "_putwc_nolock.c", - "stdio" OS_SEP "_stat.c", - "stdio" OS_SEP "_stat64i32.c", - "stdio" OS_SEP "_wfindfirst64i32.c", - "stdio" OS_SEP "_wfindnext64i32.c", - "stdio" OS_SEP "_wstat.c", - "stdio" OS_SEP "_wstat64i32.c", - "stdio" OS_SEP "asprintf.c", - "stdio" OS_SEP "atoll.c", - "stdio" OS_SEP "fgetpos64.c", - "stdio" OS_SEP "fopen64.c", - "stdio" OS_SEP "fseeko32.c", - "stdio" OS_SEP "fseeko64.c", - "stdio" OS_SEP "fsetpos64.c", - "stdio" OS_SEP "ftello.c", - "stdio" OS_SEP "ftello64.c", - "stdio" OS_SEP "ftruncate64.c", - "stdio" OS_SEP "lltoa.c", - "stdio" OS_SEP "lltow.c", - "stdio" OS_SEP "lseek64.c", - "stdio" OS_SEP "mingw_asprintf.c", - "stdio" OS_SEP "mingw_fprintf.c", - "stdio" OS_SEP "mingw_fprintfw.c", - "stdio" OS_SEP "mingw_fscanf.c", - "stdio" OS_SEP "mingw_fwscanf.c", - "stdio" OS_SEP "mingw_pformat.c", - "stdio" OS_SEP "mingw_pformatw.c", - "stdio" OS_SEP "mingw_printf.c", - "stdio" OS_SEP "mingw_printfw.c", - "stdio" OS_SEP "mingw_scanf.c", - "stdio" OS_SEP "mingw_snprintf.c", - "stdio" OS_SEP "mingw_snprintfw.c", - "stdio" OS_SEP "mingw_sprintf.c", - "stdio" OS_SEP "mingw_sprintfw.c", - "stdio" OS_SEP "mingw_sscanf.c", - "stdio" OS_SEP "mingw_swscanf.c", - "stdio" OS_SEP "mingw_vasprintf.c", - "stdio" OS_SEP "mingw_vfprintf.c", - "stdio" OS_SEP "mingw_vfprintfw.c", - "stdio" OS_SEP "mingw_vfscanf.c", - "stdio" OS_SEP "mingw_vprintf.c", - "stdio" OS_SEP "mingw_vprintfw.c", - "stdio" OS_SEP "mingw_vsnprintf.c", - "stdio" OS_SEP "mingw_vsnprintfw.c", - "stdio" OS_SEP "mingw_vsprintf.c", - "stdio" OS_SEP "mingw_vsprintfw.c", - "stdio" OS_SEP "mingw_wscanf.c", - "stdio" OS_SEP "mingw_wvfscanf.c", - "stdio" OS_SEP "scanf.S", - "stdio" OS_SEP "snprintf.c", - "stdio" OS_SEP "snwprintf.c", - "stdio" OS_SEP "strtof.c", - "stdio" OS_SEP "strtok_r.c", - "stdio" OS_SEP "truncate.c", - "stdio" OS_SEP "ulltoa.c", - "stdio" OS_SEP "ulltow.c", - "stdio" OS_SEP "vasprintf.c", - "stdio" OS_SEP "vfscanf.c", - "stdio" OS_SEP "vfscanf2.S", - "stdio" OS_SEP "vfwscanf.c", - "stdio" OS_SEP "vfwscanf2.S", - "stdio" OS_SEP "vscanf.c", - "stdio" OS_SEP "vscanf2.S", - "stdio" OS_SEP "vsnprintf.c", - "stdio" OS_SEP "vsnwprintf.c", - "stdio" OS_SEP "vsscanf.c", - "stdio" OS_SEP "vsscanf2.S", - "stdio" OS_SEP "vswscanf.c", - "stdio" OS_SEP "vswscanf2.S", - "stdio" OS_SEP "vwscanf.c", - "stdio" OS_SEP "vwscanf2.S", - "stdio" OS_SEP "wtoll.c", -}; - -static const char *mingwex_x86_src[] = { - "math" OS_SEP "x86" OS_SEP "acosf.c", - "math" OS_SEP "x86" OS_SEP "acosh.c", - "math" OS_SEP "x86" OS_SEP "acoshf.c", - "math" OS_SEP "x86" OS_SEP "acoshl.c", - "math" OS_SEP "x86" OS_SEP "acosl.c", - "math" OS_SEP "x86" OS_SEP "asinf.c", - "math" OS_SEP "x86" OS_SEP "asinh.c", - "math" OS_SEP "x86" OS_SEP "asinhf.c", - "math" OS_SEP "x86" OS_SEP "asinhl.c", - "math" OS_SEP "x86" OS_SEP "asinl.c", - "math" OS_SEP "x86" OS_SEP "atan2.c", - "math" OS_SEP "x86" OS_SEP "atan2f.c", - "math" OS_SEP "x86" OS_SEP "atan2l.c", - "math" OS_SEP "x86" OS_SEP "atanf.c", - "math" OS_SEP "x86" OS_SEP "atanh.c", - "math" OS_SEP "x86" OS_SEP "atanhf.c", - "math" OS_SEP "x86" OS_SEP "atanhl.c", - "math" OS_SEP "x86" OS_SEP "atanl.c", - "math" OS_SEP "x86" OS_SEP "ceilf.S", - "math" OS_SEP "x86" OS_SEP "ceill.S", - "math" OS_SEP "x86" OS_SEP "ceil.S", - "math" OS_SEP "x86" OS_SEP "_chgsignl.S", - "math" OS_SEP "x86" OS_SEP "copysignl.S", - "math" OS_SEP "x86" OS_SEP "cos.c", - "math" OS_SEP "x86" OS_SEP "cosf.c", - "math" OS_SEP "x86" OS_SEP "cosl.c", - "math" OS_SEP "x86" OS_SEP "cosl_internal.S", - "math" OS_SEP "x86" OS_SEP "cossin.c", - "math" OS_SEP "x86" OS_SEP "exp2f.S", - "math" OS_SEP "x86" OS_SEP "exp2l.S", - "math" OS_SEP "x86" OS_SEP "exp2.S", - "math" OS_SEP "x86" OS_SEP "exp.c", - "math" OS_SEP "x86" OS_SEP "expl.c", - "math" OS_SEP "x86" OS_SEP "expm1.c", - "math" OS_SEP "x86" OS_SEP "expm1f.c", - "math" OS_SEP "x86" OS_SEP "expm1l.c", - "math" OS_SEP "x86" OS_SEP "floorf.S", - "math" OS_SEP "x86" OS_SEP "floorl.S", - "math" OS_SEP "x86" OS_SEP "floor.S", - "math" OS_SEP "x86" OS_SEP "fmod.c", - "math" OS_SEP "x86" OS_SEP "fmodf.c", - "math" OS_SEP "x86" OS_SEP "fmodl.c", - "math" OS_SEP "x86" OS_SEP "fucom.c", - "math" OS_SEP "x86" OS_SEP "ilogbf.S", - "math" OS_SEP "x86" OS_SEP "ilogbl.S", - "math" OS_SEP "x86" OS_SEP "ilogb.S", - "math" OS_SEP "x86" OS_SEP "internal_logl.S", - "math" OS_SEP "x86" OS_SEP "ldexp.c", - "math" OS_SEP "x86" OS_SEP "ldexpl.c", - "math" OS_SEP "x86" OS_SEP "log10l.S", - "math" OS_SEP "x86" OS_SEP "log1pf.S", - "math" OS_SEP "x86" OS_SEP "log1pl.S", - "math" OS_SEP "x86" OS_SEP "log1p.S", - "math" OS_SEP "x86" OS_SEP "log2f.S", - "math" OS_SEP "x86" OS_SEP "log2l.S", - "math" OS_SEP "x86" OS_SEP "log2.S", - "math" OS_SEP "x86" OS_SEP "logb.c", - "math" OS_SEP "x86" OS_SEP "logbf.c", - "math" OS_SEP "x86" OS_SEP "logbl.c", - "math" OS_SEP "x86" OS_SEP "log.c", - "math" OS_SEP "x86" OS_SEP "logl.c", - "math" OS_SEP "x86" OS_SEP "nearbyintf.S", - "math" OS_SEP "x86" OS_SEP "nearbyintl.S", - "math" OS_SEP "x86" OS_SEP "nearbyint.S", - "math" OS_SEP "x86" OS_SEP "pow.c", - "math" OS_SEP "x86" OS_SEP "powl.c", - "math" OS_SEP "x86" OS_SEP "remainderf.S", - "math" OS_SEP "x86" OS_SEP "remainderl.S", - "math" OS_SEP "x86" OS_SEP "remainder.S", - "math" OS_SEP "x86" OS_SEP "remquof.S", - "math" OS_SEP "x86" OS_SEP "remquol.S", - "math" OS_SEP "x86" OS_SEP "remquo.S", - "math" OS_SEP "x86" OS_SEP "scalbnf.S", - "math" OS_SEP "x86" OS_SEP "scalbnl.S", - "math" OS_SEP "x86" OS_SEP "scalbn.S", - "math" OS_SEP "x86" OS_SEP "sin.c", - "math" OS_SEP "x86" OS_SEP "sinf.c", - "math" OS_SEP "x86" OS_SEP "sinl.c", - "math" OS_SEP "x86" OS_SEP "sinl_internal.S", - "math" OS_SEP "x86" OS_SEP "tanf.c", - "math" OS_SEP "x86" OS_SEP "tanl.S", - "math" OS_SEP "x86" OS_SEP "truncf.S", - "math" OS_SEP "x86" OS_SEP "trunc.S", -}; - -static const char *mingwex_arm32_src[] = { - "math" OS_SEP "arm" OS_SEP "_chgsignl.S", - "math" OS_SEP "arm" OS_SEP "exp2.c", - "math" OS_SEP "arm" OS_SEP "nearbyint.S", - "math" OS_SEP "arm" OS_SEP "nearbyintf.S", - "math" OS_SEP "arm" OS_SEP "nearbyintl.S", - "math" OS_SEP "arm" OS_SEP "trunc.S", - "math" OS_SEP "arm" OS_SEP "truncf.S", -}; - -static const char *mingwex_arm64_src[] = { - "math" OS_SEP "arm64" OS_SEP "_chgsignl.S", - "math" OS_SEP "arm64" OS_SEP "exp2f.S", - "math" OS_SEP "arm64" OS_SEP "exp2.S", - "math" OS_SEP "arm64" OS_SEP "nearbyintf.S", - "math" OS_SEP "arm64" OS_SEP "nearbyintl.S", - "math" OS_SEP "arm64" OS_SEP "nearbyint.S", - "math" OS_SEP "arm64" OS_SEP "truncf.S", - "math" OS_SEP "arm64" OS_SEP "trunc.S", -}; - -static const char *mingw_uuid_src[] = { - "libsrc/ativscp-uuid.c", - "libsrc/atsmedia-uuid.c", - "libsrc/bth-uuid.c", - "libsrc/cguid-uuid.c", - "libsrc/comcat-uuid.c", - "libsrc/devguid.c", - "libsrc/docobj-uuid.c", - "libsrc/dxva-uuid.c", - "libsrc/exdisp-uuid.c", - "libsrc/extras-uuid.c", - "libsrc/fwp-uuid.c", - "libsrc/guid_nul.c", - "libsrc/hlguids-uuid.c", - "libsrc/hlink-uuid.c", - "libsrc/mlang-uuid.c", - "libsrc/msctf-uuid.c", - "libsrc/mshtmhst-uuid.c", - "libsrc/mshtml-uuid.c", - "libsrc/msxml-uuid.c", - "libsrc/netcon-uuid.c", - "libsrc/ntddkbd-uuid.c", - "libsrc/ntddmou-uuid.c", - "libsrc/ntddpar-uuid.c", - "libsrc/ntddscsi-uuid.c", - "libsrc/ntddser-uuid.c", - "libsrc/ntddstor-uuid.c", - "libsrc/ntddvdeo-uuid.c", - "libsrc/oaidl-uuid.c", - "libsrc/objidl-uuid.c", - "libsrc/objsafe-uuid.c", - "libsrc/ocidl-uuid.c", - "libsrc/oleacc-uuid.c", - "libsrc/olectlid-uuid.c", - "libsrc/oleidl-uuid.c", - "libsrc/power-uuid.c", - "libsrc/powrprof-uuid.c", - "libsrc/uianimation-uuid.c", - "libsrc/usbcamdi-uuid.c", - "libsrc/usbiodef-uuid.c", - "libsrc/uuid.c", - "libsrc/vds-uuid.c", - "libsrc/virtdisk-uuid.c", - "libsrc/wia-uuid.c", -}; - -struct MinGWDef { - const char *name; - bool always_link; -}; -static const MinGWDef mingw_def_list[] = { - {"advapi32",true}, - {"bcrypt", false}, - {"comctl32",false}, - {"comdlg32",false}, - {"crypt32", false}, - {"cryptnet",false}, - {"gdi32", false}, - {"imm32", false}, - {"kernel32",true}, - {"lz32", false}, - {"mpr", false}, - {"msvcrt", true}, - {"mswsock", false}, - {"ncrypt", false}, - {"netapi32",false}, - {"ntdll", true}, - {"ole32", false}, - {"oleaut32",false}, - {"opengl32",false}, - {"psapi", false}, - {"rpcns4", false}, - {"rpcrt4", false}, - {"scarddlg",false}, - {"setupapi",false}, - {"shell32", true}, - {"shlwapi", false}, - {"urlmon", false}, - {"user32", true}, - {"version", false}, - {"winmm", false}, - {"winscard",false}, - {"winspool",false}, - {"wintrust",false}, - {"ws2_32", false}, -}; - -struct LinkJob { - CodeGen *codegen; - ZigList args; - bool link_in_crt; - HashMap rpath_table; - Stage2ProgressNode *build_dep_prog_node; -}; - -static const char *build_libc_object(CodeGen *parent_gen, const char *name, CFile *c_file, - Stage2ProgressNode *progress_node) -{ - CodeGen *child_gen = create_child_codegen(parent_gen, nullptr, OutTypeObj, nullptr, name, progress_node); - child_gen->root_out_name = buf_create_from_str(name); - ZigList c_source_files = {0}; - c_source_files.append(c_file); - child_gen->c_source_files = c_source_files; - codegen_build_and_link(child_gen); - return buf_ptr(&child_gen->bin_file_output_path); -} - -static const char *path_from_zig_lib(CodeGen *g, const char *dir, const char *subpath) { - Buf *dir1 = buf_alloc(); - os_path_join(g->zig_lib_dir, buf_create_from_str(dir), dir1); - Buf *result = buf_alloc(); - os_path_join(dir1, buf_create_from_str(subpath), result); - return buf_ptr(result); -} - -static const char *path_from_libc(CodeGen *g, const char *subpath) { - return path_from_zig_lib(g, "libc", subpath); -} - -static const char *path_from_libunwind(CodeGen *g, const char *subpath) { - return path_from_zig_lib(g, "libunwind", subpath); -} - -static const char *build_libunwind(CodeGen *parent, Stage2ProgressNode *progress_node) { - CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr, "unwind", progress_node); - LinkLib *new_link_lib = codegen_add_link_lib(child_gen, buf_create_from_str("c")); - new_link_lib->provided_explicitly = false; - enum SrcKind { - SrcCpp, - SrcC, - SrcAsm, - }; - static const struct { - const char *path; - SrcKind kind; - } unwind_src[] = { - {"src" OS_SEP "libunwind.cpp", SrcCpp}, - {"src" OS_SEP "Unwind-EHABI.cpp", SrcCpp}, - {"src" OS_SEP "Unwind-seh.cpp", SrcCpp}, - - {"src" OS_SEP "UnwindLevel1.c", SrcC}, - {"src" OS_SEP "UnwindLevel1-gcc-ext.c", SrcC}, - {"src" OS_SEP "Unwind-sjlj.c", SrcC}, - - {"src" OS_SEP "UnwindRegistersRestore.S", SrcAsm}, - {"src" OS_SEP "UnwindRegistersSave.S", SrcAsm}, - }; - ZigList c_source_files = {0}; - for (size_t i = 0; i < array_length(unwind_src); i += 1) { - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = path_from_libunwind(parent, unwind_src[i].path); - switch (unwind_src[i].kind) { - case SrcC: - c_file->args.append("-std=c99"); - break; - case SrcCpp: - c_file->args.append("-fno-rtti"); - c_file->args.append("-I"); - c_file->args.append(path_from_zig_lib(parent, "libcxx", "include")); - break; - case SrcAsm: - break; - } - c_file->args.append("-I"); - c_file->args.append(path_from_libunwind(parent, "include")); - if (target_supports_fpic(parent->zig_target)) { - c_file->args.append("-fPIC"); - } - c_file->args.append("-D_LIBUNWIND_DISABLE_VISIBILITY_ANNOTATIONS"); - c_file->args.append("-Wa,--noexecstack"); - - // This is intentionally always defined because the macro definition means, should it only - // build for the target specified by compiler defines. Since we pass -target the compiler - // defines will be correct. - c_file->args.append("-D_LIBUNWIND_IS_NATIVE_ONLY"); - - if (parent->build_mode == BuildModeDebug) { - c_file->args.append("-D_DEBUG"); - } - if (parent->is_single_threaded) { - c_file->args.append("-D_LIBUNWIND_HAS_NO_THREADS"); - } - c_file->args.append("-Wno-bitwise-conditional-parentheses"); - c_source_files.append(c_file); - } - child_gen->c_source_files = c_source_files; - codegen_build_and_link(child_gen); - return buf_ptr(&child_gen->bin_file_output_path); -} - -static void mingw_add_cc_args(CodeGen *parent, CFile *c_file) { - c_file->args.append("-DHAVE_CONFIG_H"); - - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "libc" OS_SEP "mingw" OS_SEP "include", - buf_ptr(parent->zig_lib_dir)))); - - c_file->args.append("-isystem"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "libc" OS_SEP "include" OS_SEP "any-windows-any", - buf_ptr(parent->zig_lib_dir)))); - - if (target_is_arm(parent->zig_target) && - target_arch_pointer_bit_width(parent->zig_target->arch) == 32) - { - c_file->args.append("-mfpu=vfp"); - } - - c_file->args.append("-std=gnu11"); - c_file->args.append("-D_CRTBLD"); - c_file->args.append("-D_WIN32_WINNT=0x0f00"); - c_file->args.append("-D__MSVCRT_VERSION__=0x700"); -} - -static void glibc_add_include_dirs_arch(CFile *c_file, ZigLLVM_ArchType arch, const char *nptl, const char *dir) { - bool is_x86 = arch == ZigLLVM_x86 || arch == ZigLLVM_x86_64; - bool is_aarch64 = arch == ZigLLVM_aarch64 || arch == ZigLLVM_aarch64_be; - bool is_mips = arch == ZigLLVM_mips || arch == ZigLLVM_mipsel || - arch == ZigLLVM_mips64el || arch == ZigLLVM_mips64; - bool is_arm = arch == ZigLLVM_arm || arch == ZigLLVM_armeb; - bool is_ppc = arch == ZigLLVM_ppc || arch == ZigLLVM_ppc64 || arch == ZigLLVM_ppc64le; - bool is_riscv = arch == ZigLLVM_riscv32 || arch == ZigLLVM_riscv64; - bool is_sparc = arch == ZigLLVM_sparc || arch == ZigLLVM_sparcel || arch == ZigLLVM_sparcv9; - bool is_64 = target_arch_pointer_bit_width(arch) == 64; - - if (is_x86) { - if (arch == ZigLLVM_x86_64) { - if (nptl != nullptr) { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "x86_64" OS_SEP "%s", dir, nptl))); - } else { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "x86_64", dir))); - } - } else if (arch == ZigLLVM_x86) { - if (nptl != nullptr) { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "i386" OS_SEP "%s", dir, nptl))); - } else { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "i386", dir))); - } - } - if (nptl != nullptr) { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "x86" OS_SEP "%s", dir, nptl))); - } else { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "x86", dir))); - } - } else if (is_arm) { - if (nptl != nullptr) { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "arm" OS_SEP "%s", dir, nptl))); - } else { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "arm", dir))); - } - } else if (is_mips) { - if (nptl != nullptr) { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "mips" OS_SEP "%s", dir, nptl))); - } else { - if (is_64) { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "mips" OS_SEP "mips64", dir))); - } else { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "mips" OS_SEP "mips32", dir))); - } - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "mips", dir))); - } - } else if (is_sparc) { - if (nptl != nullptr) { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "sparc" OS_SEP "%s", dir, nptl))); - } else { - if (is_64) { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "sparc" OS_SEP "sparc64", dir))); - } else { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "sparc" OS_SEP "sparc32", dir))); - } - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "sparc", dir))); - } - } else if (is_aarch64) { - if (nptl != nullptr) { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "aarch64" OS_SEP "%s", dir, nptl))); - } else { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "aarch64", dir))); - } - } else if (is_ppc) { - if (nptl != nullptr) { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "powerpc" OS_SEP "%s", dir, nptl))); - } else { - if (is_64) { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "powerpc" OS_SEP "powerpc64", dir))); - } else { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "powerpc" OS_SEP "powerpc32", dir))); - } - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "powerpc", dir))); - } - } else if (is_riscv) { - if (nptl != nullptr) { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "riscv" OS_SEP "%s", dir, nptl))); - } else { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "riscv", dir))); - } - } -} - -static void glibc_add_include_dirs(CodeGen *parent, CFile *c_file) { - ZigLLVM_ArchType arch = parent->zig_target->arch; - const char *nptl = (parent->zig_target->os == OsLinux) ? "nptl" : "htl"; - const char *glibc = path_from_libc(parent, "glibc"); - - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "include", glibc))); - - if (parent->zig_target->os == OsLinux) { - glibc_add_include_dirs_arch(c_file, arch, nullptr, - path_from_libc(parent, "glibc" OS_SEP "sysdeps" OS_SEP "unix" OS_SEP "sysv" OS_SEP "linux")); - } - - if (nptl != nullptr) { - glibc_add_include_dirs_arch(c_file, arch, nptl, path_from_libc(parent, "glibc" OS_SEP "sysdeps")); - } - - if (parent->zig_target->os == OsLinux) { - c_file->args.append("-I"); - c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "sysdeps" OS_SEP - "unix" OS_SEP "sysv" OS_SEP "linux" OS_SEP "generic")); - - c_file->args.append("-I"); - c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "sysdeps" OS_SEP - "unix" OS_SEP "sysv" OS_SEP "linux" OS_SEP "include")); - c_file->args.append("-I"); - c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "sysdeps" OS_SEP - "unix" OS_SEP "sysv" OS_SEP "linux")); - } - if (nptl != nullptr) { - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "sysdeps" OS_SEP "%s", glibc, nptl))); - } - - c_file->args.append("-I"); - c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "sysdeps" OS_SEP "pthread")); - - c_file->args.append("-I"); - c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "sysdeps" OS_SEP "unix" OS_SEP "sysv")); - - glibc_add_include_dirs_arch(c_file, arch, nullptr, - path_from_libc(parent, "glibc" OS_SEP "sysdeps" OS_SEP "unix")); - - c_file->args.append("-I"); - c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "sysdeps" OS_SEP "unix")); - - glibc_add_include_dirs_arch(c_file, arch, nullptr, path_from_libc(parent, "glibc" OS_SEP "sysdeps")); - - c_file->args.append("-I"); - c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "sysdeps" OS_SEP "generic")); - - c_file->args.append("-I"); - c_file->args.append(path_from_libc(parent, "glibc")); - - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "libc" OS_SEP "include" OS_SEP "%s-%s-%s", - buf_ptr(parent->zig_lib_dir), target_arch_name(parent->zig_target->arch), - target_os_name(parent->zig_target->os), target_abi_name(parent->zig_target->abi)))); - - c_file->args.append("-I"); - c_file->args.append(path_from_libc(parent, "include" OS_SEP "generic-glibc")); - - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "libc" OS_SEP "include" OS_SEP "%s-linux-any", - buf_ptr(parent->zig_lib_dir), target_arch_name(parent->zig_target->arch)))); - - c_file->args.append("-I"); - c_file->args.append(path_from_libc(parent, "include" OS_SEP "any-linux-any")); -} - -static const char *glibc_start_asm_path(CodeGen *parent, const char *file) { - ZigLLVM_ArchType arch = parent->zig_target->arch; - bool is_aarch64 = arch == ZigLLVM_aarch64 || arch == ZigLLVM_aarch64_be; - bool is_mips = arch == ZigLLVM_mips || arch == ZigLLVM_mipsel || - arch == ZigLLVM_mips64el || arch == ZigLLVM_mips64; - bool is_arm = arch == ZigLLVM_arm || arch == ZigLLVM_armeb; - bool is_ppc = arch == ZigLLVM_ppc || arch == ZigLLVM_ppc64 || arch == ZigLLVM_ppc64le; - bool is_riscv = arch == ZigLLVM_riscv32 || arch == ZigLLVM_riscv64; - bool is_sparc = arch == ZigLLVM_sparc || arch == ZigLLVM_sparcel || arch == ZigLLVM_sparcv9; - bool is_64 = target_arch_pointer_bit_width(arch) == 64; - - Buf result = BUF_INIT; - buf_resize(&result, 0); - buf_append_buf(&result, parent->zig_lib_dir); - buf_append_str(&result, OS_SEP "libc" OS_SEP "glibc" OS_SEP "sysdeps" OS_SEP); - if (is_sparc) { - if (is_64) { - buf_append_str(&result, "sparc" OS_SEP "sparc64"); - } else { - buf_append_str(&result, "sparc" OS_SEP "sparc32"); - } - } else if (is_arm) { - buf_append_str(&result, "arm"); - } else if (is_mips) { - buf_append_str(&result, "mips"); - } else if (arch == ZigLLVM_x86_64) { - buf_append_str(&result, "x86_64"); - } else if (arch == ZigLLVM_x86) { - buf_append_str(&result, "i386"); - } else if (is_aarch64) { - buf_append_str(&result, "aarch64"); - } else if (is_riscv) { - buf_append_str(&result, "riscv"); - } else if (is_ppc) { - if (is_64) { - buf_append_str(&result, "powerpc" OS_SEP "powerpc64"); - } else { - buf_append_str(&result, "powerpc" OS_SEP "powerpc32"); - } - } - - buf_append_str(&result, OS_SEP); - buf_append_str(&result, file); - return buf_ptr(&result); -} - -static const char *musl_start_asm_path(CodeGen *parent, const char *file) { - Buf *result = buf_sprintf("%s" OS_SEP "libc" OS_SEP "musl" OS_SEP "crt" OS_SEP "%s" OS_SEP "%s", - buf_ptr(parent->zig_lib_dir), target_arch_musl_name(parent->zig_target->arch), file); - return buf_ptr(result); -} - -static void musl_add_cc_args(CodeGen *parent, CFile *c_file, bool want_O3) { - c_file->args.append("-std=c99"); - c_file->args.append("-ffreestanding"); - // Musl adds these args to builds with gcc but clang does not support them. - //c_file->args.append("-fexcess-precision=standard"); - //c_file->args.append("-frounding-math"); - c_file->args.append("-Wa,--noexecstack"); - c_file->args.append("-D_XOPEN_SOURCE=700"); - - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "libc" OS_SEP "musl" OS_SEP "arch" OS_SEP "%s", - buf_ptr(parent->zig_lib_dir), target_arch_musl_name(parent->zig_target->arch)))); - - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "libc" OS_SEP "musl" OS_SEP "arch" OS_SEP "generic", - buf_ptr(parent->zig_lib_dir)))); - - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "libc" OS_SEP "musl" OS_SEP "src" OS_SEP "include", - buf_ptr(parent->zig_lib_dir)))); - - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "libc" OS_SEP "musl" OS_SEP "src" OS_SEP "internal", - buf_ptr(parent->zig_lib_dir)))); - - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "libc" OS_SEP "musl" OS_SEP "include", - buf_ptr(parent->zig_lib_dir)))); - - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf( - "%s" OS_SEP "libc" OS_SEP "include" OS_SEP "%s-%s-musl", - buf_ptr(parent->zig_lib_dir), - target_arch_musl_name(parent->zig_target->arch), - target_os_name(parent->zig_target->os)))); - - c_file->args.append("-I"); - c_file->args.append(buf_ptr(buf_sprintf("%s" OS_SEP "libc" OS_SEP "include" OS_SEP "generic-musl", - buf_ptr(parent->zig_lib_dir)))); - - if (want_O3) - c_file->args.append("-O3"); - else - c_file->args.append("-Os"); - - c_file->args.append("-fomit-frame-pointer"); - c_file->args.append("-fno-unwind-tables"); - c_file->args.append("-fno-asynchronous-unwind-tables"); - c_file->args.append("-ffunction-sections"); - c_file->args.append("-fdata-sections"); -} - -static const char *musl_arch_names[] = { - "aarch64", - "arm", - "generic", - "i386", - "m68k", - "microblaze", - "mips", - "mips64", - "mipsn32", - "or1k", - "powerpc", - "powerpc64", - "riscv64", - "s390x", - "sh", - "x32", - "x86_64", -}; - -static bool is_musl_arch_name(const char *name) { - for (size_t i = 0; i < array_length(musl_arch_names); i += 1) { - if (strcmp(name, musl_arch_names[i]) == 0) - return true; - } - return false; -} - -enum MuslSrc { - MuslSrcAsm, - MuslSrcNormal, - MuslSrcO3, -}; - -static void add_musl_src_file(HashMap &source_table, - const char *file_path) -{ - Buf *src_file = buf_create_from_str(file_path); - - MuslSrc src_kind; - if (buf_ends_with_str(src_file, ".c")) { - bool want_O3 = buf_starts_with_str(src_file, "musl/src/malloc/") || - buf_starts_with_str(src_file, "musl/src/string/") || - buf_starts_with_str(src_file, "musl/src/internal/"); - src_kind = want_O3 ? MuslSrcO3 : MuslSrcNormal; - } else if (buf_ends_with_str(src_file, ".s") || buf_ends_with_str(src_file, ".S")) { - src_kind = MuslSrcAsm; - } else { - zig_unreachable(); - } - if (ZIG_OS_SEP_CHAR != '/') { - buf_replace(src_file, '/', ZIG_OS_SEP_CHAR); - } - source_table.put_unique(src_file, src_kind); -} - -static const char *build_musl(CodeGen *parent, Stage2ProgressNode *progress_node) { - CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr, "c", progress_node); - - // When there is a src//foo.* then it should substitute for src/foo.* - // Even a .s file can substitute for a .c file. - - const char *target_musl_arch_name = target_arch_musl_name(parent->zig_target->arch); - - HashMap source_table = {}; - source_table.init(2000); - - for (size_t i = 0; i < array_length(ZIG_MUSL_SRC_FILES); i += 1) { - add_musl_src_file(source_table, ZIG_MUSL_SRC_FILES[i]); - } - - static const char *time32_compat_arch_list[] = {"arm", "i386", "mips", "powerpc"}; - for (size_t arch_i = 0; arch_i < array_length(time32_compat_arch_list); arch_i += 1) { - if (strcmp(target_musl_arch_name, time32_compat_arch_list[arch_i]) == 0) { - for (size_t i = 0; i < array_length(ZIG_MUSL_COMPAT_TIME32_FILES); i += 1) { - add_musl_src_file(source_table, ZIG_MUSL_COMPAT_TIME32_FILES[i]); - } - } - } - - - ZigList c_source_files = {0}; - - Buf dirname = BUF_INIT; - Buf basename = BUF_INIT; - Buf noextbasename = BUF_INIT; - Buf dirbasename = BUF_INIT; - Buf before_arch_dir = BUF_INIT; - - auto source_it = source_table.entry_iterator(); - for (;;) { - auto *entry = source_it.next(); - if (!entry) break; - - Buf *src_file = entry->key; - MuslSrc src_kind = entry->value; - - os_path_split(src_file, &dirname, &basename); - os_path_extname(&basename, &noextbasename, nullptr); - os_path_split(&dirname, &before_arch_dir, &dirbasename); - - bool is_arch_specific = false; - // Architecture-specific implementations are under a / folder. - if (is_musl_arch_name(buf_ptr(&dirbasename))) { - // Not the architecture we're compiling for. - if (strcmp(buf_ptr(&dirbasename), target_musl_arch_name) != 0) - continue; - is_arch_specific = true; - } - - if (!is_arch_specific) { - Buf override_path = BUF_INIT; - - // Look for an arch specific override. - buf_resize(&override_path, 0); - buf_appendf(&override_path, "%s" OS_SEP "%s" OS_SEP "%s.s", - buf_ptr(&dirname), target_musl_arch_name, buf_ptr(&noextbasename)); - if (source_table.maybe_get(&override_path) != nullptr) - continue; - - buf_resize(&override_path, 0); - buf_appendf(&override_path, "%s" OS_SEP "%s" OS_SEP "%s.S", - buf_ptr(&dirname), target_musl_arch_name, buf_ptr(&noextbasename)); - if (source_table.maybe_get(&override_path) != nullptr) - continue; - - buf_resize(&override_path, 0); - buf_appendf(&override_path, "%s" OS_SEP "%s" OS_SEP "%s.c", - buf_ptr(&dirname), target_musl_arch_name, buf_ptr(&noextbasename)); - if (source_table.maybe_get(&override_path) != nullptr) - continue; - } - - Buf *full_path = buf_sprintf("%s" OS_SEP "libc" OS_SEP "%s", - buf_ptr(parent->zig_lib_dir), buf_ptr(src_file)); - - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = buf_ptr(full_path); - - musl_add_cc_args(parent, c_file, src_kind == MuslSrcO3); - c_file->args.append("-Qunused-arguments"); - c_file->args.append("-w"); // disable all warnings - - c_source_files.append(c_file); - } - - child_gen->c_source_files = c_source_files; - codegen_build_and_link(child_gen); - return buf_ptr(&child_gen->bin_file_output_path); -} - -static const char *build_libcxxabi(CodeGen *parent, Stage2ProgressNode *progress_node) { - CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr, "c++abi", progress_node); - codegen_add_link_lib(child_gen, buf_create_from_str("c")); - - ZigList c_source_files = {0}; - - const char *cxxabi_include_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxxabi" OS_SEP "include", - buf_ptr(parent->zig_lib_dir))); - const char *cxx_include_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxx" OS_SEP "include", - buf_ptr(parent->zig_lib_dir))); - - for (size_t i = 0; i < array_length(ZIG_LIBCXXABI_FILES); i += 1) { - const char *rel_src_path = ZIG_LIBCXXABI_FILES[i]; - - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxxabi" OS_SEP "%s", - buf_ptr(parent->zig_lib_dir), rel_src_path)); - - c_file->args.append("-DHAVE___CXA_THREAD_ATEXIT_IMPL"); - c_file->args.append("-D_LIBCPP_DISABLE_EXTERN_TEMPLATE"); - c_file->args.append("-D_LIBCPP_ENABLE_CXX17_REMOVED_UNEXPECTED_FUNCTIONS"); - c_file->args.append("-D_LIBCXXABI_BUILDING_LIBRARY"); - c_file->args.append("-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS"); - c_file->args.append("-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS"); - - if (target_abi_is_musl(parent->zig_target->abi)) { - c_file->args.append("-D_LIBCPP_HAS_MUSL_LIBC"); - } - - c_file->args.append("-I"); - c_file->args.append(cxxabi_include_path); - - c_file->args.append("-I"); - c_file->args.append(cxx_include_path); - - c_file->args.append("-O3"); - c_file->args.append("-DNDEBUG"); - if (target_supports_fpic(parent->zig_target)) { - c_file->args.append("-fPIC"); - } - c_file->args.append("-nostdinc++"); - c_file->args.append("-fstrict-aliasing"); - c_file->args.append("-funwind-tables"); - c_file->args.append("-D_DEBUG"); - c_file->args.append("-UNDEBUG"); - c_file->args.append("-std=c++11"); - - c_source_files.append(c_file); - } - - - child_gen->c_source_files = c_source_files; - codegen_build_and_link(child_gen); - return buf_ptr(&child_gen->bin_file_output_path); -} - -static const char *build_libcxx(CodeGen *parent, Stage2ProgressNode *progress_node) { - CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr, "c++", progress_node); - codegen_add_link_lib(child_gen, buf_create_from_str("c")); - - ZigList c_source_files = {0}; - - const char *cxxabi_include_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxxabi" OS_SEP "include", - buf_ptr(parent->zig_lib_dir))); - const char *cxx_include_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxx" OS_SEP "include", - buf_ptr(parent->zig_lib_dir))); - - for (size_t i = 0; i < array_length(ZIG_LIBCXX_FILES); i += 1) { - const char *rel_src_path = ZIG_LIBCXX_FILES[i]; - - Buf *src_path_buf = buf_create_from_str(rel_src_path); - if (parent->zig_target->os == OsWindows) { - // filesystem stuff isn't supported on Windows - if (buf_starts_with_str(src_path_buf, "src/filesystem/")) { - continue; - } - } else { - if (buf_starts_with_str(src_path_buf, "src/support/win32/")) { - continue; - } - } - - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = buf_ptr(buf_sprintf("%s" OS_SEP "libcxx" OS_SEP "%s", - buf_ptr(parent->zig_lib_dir), rel_src_path)); - - c_file->args.append("-DNDEBUG"); - c_file->args.append("-D_LIBCPP_BUILDING_LIBRARY"); - c_file->args.append("-D_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER"); - c_file->args.append("-DLIBCXX_BUILDING_LIBCXXABI"); - c_file->args.append("-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS"); - c_file->args.append("-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS"); - - if (target_abi_is_musl(parent->zig_target->abi)) { - c_file->args.append("-D_LIBCPP_HAS_MUSL_LIBC"); - } - - c_file->args.append("-I"); - c_file->args.append(cxx_include_path); - - c_file->args.append("-I"); - c_file->args.append(cxxabi_include_path); - - c_file->args.append("-O3"); - c_file->args.append("-DNDEBUG"); - if (target_supports_fpic(parent->zig_target)) { - c_file->args.append("-fPIC"); - } - c_file->args.append("-nostdinc++"); - c_file->args.append("-fvisibility-inlines-hidden"); - c_file->args.append("-std=c++14"); - c_file->args.append("-Wno-user-defined-literals"); - - c_source_files.append(c_file); - } - - - child_gen->c_source_files = c_source_files; - codegen_build_and_link(child_gen); - return buf_ptr(&child_gen->bin_file_output_path); -} - -static void add_msvcrt_os_dep(CodeGen *parent, CodeGen *child_gen, const char *src_path) { - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = buf_ptr(buf_sprintf("%s" OS_SEP "libc" OS_SEP "mingw" OS_SEP "%s", - buf_ptr(parent->zig_lib_dir), src_path)); - c_file->args.append("-DHAVE_CONFIG_H"); - c_file->args.append("-D__LIBMSVCRT__"); - - c_file->args.append("-I"); - c_file->args.append(path_from_libc(parent, "mingw" OS_SEP "include")); - - c_file->args.append("-std=gnu99"); - c_file->args.append("-D_CRTBLD"); - c_file->args.append("-D_WIN32_WINNT=0x0f00"); - c_file->args.append("-D__MSVCRT_VERSION__=0x700"); - - c_file->args.append("-isystem"); - c_file->args.append(path_from_libc(parent, "include" OS_SEP "any-windows-any")); - - c_file->args.append("-g"); - c_file->args.append("-O2"); - - child_gen->c_source_files.append(c_file); -} - -static void add_mingwex_dep(CodeGen *parent, CodeGen *child_gen, const char *src_path) { - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = buf_ptr(buf_sprintf("%s" OS_SEP "libc" OS_SEP "mingw" OS_SEP "%s", - buf_ptr(parent->zig_lib_dir), src_path)); - c_file->args.append("-DHAVE_CONFIG_H"); - - c_file->args.append("-I"); - c_file->args.append(path_from_libc(parent, "mingw")); - - c_file->args.append("-I"); - c_file->args.append(path_from_libc(parent, "mingw" OS_SEP "include")); - - c_file->args.append("-std=gnu99"); - c_file->args.append("-D_CRTBLD"); - c_file->args.append("-D_WIN32_WINNT=0x0f00"); - c_file->args.append("-D__MSVCRT_VERSION__=0x700"); - c_file->args.append("-g"); - c_file->args.append("-O2"); - - c_file->args.append("-isystem"); - c_file->args.append(path_from_libc(parent, "include" OS_SEP "any-windows-any")); - - child_gen->c_source_files.append(c_file); -} - -static void add_mingw_uuid_dep(CodeGen *parent, CodeGen *child_gen, const char *src_path) { - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = buf_ptr(buf_sprintf("%s" OS_SEP "libc" OS_SEP "mingw" OS_SEP "%s", - buf_ptr(parent->zig_lib_dir), src_path)); - c_file->args.append("-DHAVE_CONFIG_H"); - - c_file->args.append("-I"); - c_file->args.append(path_from_libc(parent, "mingw")); - - c_file->args.append("-I"); - c_file->args.append(path_from_libc(parent, "mingw" OS_SEP "include")); - - c_file->args.append("-std=gnu99"); - c_file->args.append("-D_CRTBLD"); - c_file->args.append("-D_WIN32_WINNT=0x0f00"); - c_file->args.append("-D__MSVCRT_VERSION__=0x700"); - c_file->args.append("-g"); - c_file->args.append("-O2"); - - c_file->args.append("-isystem"); - c_file->args.append(path_from_libc(parent, "include" OS_SEP "any-windows-any")); - - child_gen->c_source_files.append(c_file); -} - -static const char *get_libc_crt_file(CodeGen *parent, const char *file, Stage2ProgressNode *progress_node) { - if (parent->libc == nullptr && parent->zig_target->os == OsWindows) { - if (strcmp(file, "crt2.o") == 0) { - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = buf_ptr(buf_sprintf( - "%s" OS_SEP "libc" OS_SEP "mingw" OS_SEP "crt" OS_SEP "crtexe.c", buf_ptr(parent->zig_lib_dir))); - mingw_add_cc_args(parent, c_file); - c_file->args.append("-U__CRTDLL__"); - c_file->args.append("-D__MSVCRT__"); - // Uncomment these 3 things for crtu - //c_file->args.append("-DUNICODE"); - //c_file->args.append("-D_UNICODE"); - //c_file->args.append("-DWPRFLAG=1"); - return build_libc_object(parent, "crt2", c_file, progress_node); - } else if (strcmp(file, "dllcrt2.o") == 0) { - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = buf_ptr(buf_sprintf( - "%s" OS_SEP "libc" OS_SEP "mingw" OS_SEP "crt" OS_SEP "crtdll.c", buf_ptr(parent->zig_lib_dir))); - mingw_add_cc_args(parent, c_file); - c_file->args.append("-U__CRTDLL__"); - c_file->args.append("-D__MSVCRT__"); - return build_libc_object(parent, "dllcrt2", c_file, progress_node); - } else if (strcmp(file, "mingw32.lib") == 0) { - CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr, "mingw32", progress_node); - - static const char *deps[] = { - "mingw" OS_SEP "crt" OS_SEP "crt0_c.c", - "mingw" OS_SEP "crt" OS_SEP "dll_argv.c", - "mingw" OS_SEP "crt" OS_SEP "gccmain.c", - "mingw" OS_SEP "crt" OS_SEP "natstart.c", - "mingw" OS_SEP "crt" OS_SEP "pseudo-reloc-list.c", - "mingw" OS_SEP "crt" OS_SEP "wildcard.c", - "mingw" OS_SEP "crt" OS_SEP "charmax.c", - "mingw" OS_SEP "crt" OS_SEP "crt0_w.c", - "mingw" OS_SEP "crt" OS_SEP "dllargv.c", - "mingw" OS_SEP "crt" OS_SEP "gs_support.c", - "mingw" OS_SEP "crt" OS_SEP "_newmode.c", - "mingw" OS_SEP "crt" OS_SEP "tlssup.c", - "mingw" OS_SEP "crt" OS_SEP "xncommod.c", - "mingw" OS_SEP "crt" OS_SEP "cinitexe.c", - "mingw" OS_SEP "crt" OS_SEP "merr.c", - "mingw" OS_SEP "crt" OS_SEP "usermatherr.c", - "mingw" OS_SEP "crt" OS_SEP "pesect.c", - "mingw" OS_SEP "crt" OS_SEP "udllargc.c", - "mingw" OS_SEP "crt" OS_SEP "xthdloc.c", - "mingw" OS_SEP "crt" OS_SEP "CRT_fp10.c", - "mingw" OS_SEP "crt" OS_SEP "mingw_helpers.c", - "mingw" OS_SEP "crt" OS_SEP "pseudo-reloc.c", - "mingw" OS_SEP "crt" OS_SEP "udll_argv.c", - "mingw" OS_SEP "crt" OS_SEP "xtxtmode.c", - "mingw" OS_SEP "crt" OS_SEP "crt_handler.c", - "mingw" OS_SEP "crt" OS_SEP "tlsthrd.c", - "mingw" OS_SEP "crt" OS_SEP "tlsmthread.c", - "mingw" OS_SEP "crt" OS_SEP "tlsmcrt.c", - "mingw" OS_SEP "crt" OS_SEP "cxa_atexit.c", - }; - for (size_t i = 0; i < array_length(deps); i += 1) { - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = path_from_libc(parent, deps[i]); - c_file->args.append("-DHAVE_CONFIG_H"); - c_file->args.append("-D_SYSCRT=1"); - c_file->args.append("-DCRTDLL=1"); - - c_file->args.append("-isystem"); - c_file->args.append(path_from_libc(parent, "include" OS_SEP "any-windows-any")); - - c_file->args.append("-isystem"); - c_file->args.append(path_from_libc(parent, "mingw" OS_SEP "include")); - - c_file->args.append("-std=gnu99"); - c_file->args.append("-D_CRTBLD"); - c_file->args.append("-D_WIN32_WINNT=0x0f00"); - c_file->args.append("-D__MSVCRT_VERSION__=0x700"); - c_file->args.append("-g"); - c_file->args.append("-O2"); - - child_gen->c_source_files.append(c_file); - } - codegen_build_and_link(child_gen); - return buf_ptr(&child_gen->bin_file_output_path); - } else if (strcmp(file, "msvcrt-os.lib") == 0) { - CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr, "msvcrt-os", progress_node); - - for (size_t i = 0; i < array_length(msvcrt_common_src); i += 1) { - add_msvcrt_os_dep(parent, child_gen, msvcrt_common_src[i]); - } - if (parent->zig_target->arch == ZigLLVM_x86) { - for (size_t i = 0; i < array_length(msvcrt_i386_src); i += 1) { - add_msvcrt_os_dep(parent, child_gen, msvcrt_i386_src[i]); - } - } else { - for (size_t i = 0; i < array_length(msvcrt_other_src); i += 1) { - add_msvcrt_os_dep(parent, child_gen, msvcrt_other_src[i]); - } - } - codegen_build_and_link(child_gen); - return buf_ptr(&child_gen->bin_file_output_path); - } else if (strcmp(file, "mingwex.lib") == 0) { - CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr, "mingwex", progress_node); - - for (size_t i = 0; i < array_length(mingwex_generic_src); i += 1) { - add_mingwex_dep(parent, child_gen, mingwex_generic_src[i]); - } - if (parent->zig_target->arch == ZigLLVM_x86 || parent->zig_target->arch == ZigLLVM_x86_64) { - for (size_t i = 0; i < array_length(mingwex_x86_src); i += 1) { - add_mingwex_dep(parent, child_gen, mingwex_x86_src[i]); - } - } else if (target_is_arm(parent->zig_target)) { - if (target_arch_pointer_bit_width(parent->zig_target->arch) == 32) { - for (size_t i = 0; i < array_length(mingwex_arm32_src); i += 1) { - add_mingwex_dep(parent, child_gen, mingwex_arm32_src[i]); - } - } else { - for (size_t i = 0; i < array_length(mingwex_arm64_src); i += 1) { - add_mingwex_dep(parent, child_gen, mingwex_arm64_src[i]); - } - } - } else { - zig_unreachable(); - } - codegen_build_and_link(child_gen); - return buf_ptr(&child_gen->bin_file_output_path); - } else if (strcmp(file, "uuid.lib") == 0) { - CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr, "uuid", progress_node); - for (size_t i = 0; i < array_length(mingw_uuid_src); i += 1) { - add_mingw_uuid_dep(parent, child_gen, mingw_uuid_src[i]); - } - codegen_build_and_link(child_gen); - return buf_ptr(&child_gen->bin_file_output_path); - } else { - zig_unreachable(); - } - } else if (parent->libc == nullptr && target_is_glibc(parent->zig_target)) { - if (strcmp(file, "crti.o") == 0) { - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = glibc_start_asm_path(parent, "crti.S"); - glibc_add_include_dirs(parent, c_file); - c_file->args.append("-D_LIBC_REENTRANT"); - c_file->args.append("-include"); - c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "include" OS_SEP "libc-modules.h")); - c_file->args.append("-DMODULE_NAME=libc"); - c_file->args.append("-Wno-nonportable-include-path"); - c_file->args.append("-include"); - c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "include" OS_SEP "libc-symbols.h")); - c_file->args.append("-DTOP_NAMESPACE=glibc"); - c_file->args.append("-DASSEMBLER"); - c_file->args.append("-g"); - c_file->args.append("-Wa,--noexecstack"); - return build_libc_object(parent, "crti", c_file, progress_node); - } else if (strcmp(file, "crtn.o") == 0) { - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = glibc_start_asm_path(parent, "crtn.S"); - glibc_add_include_dirs(parent, c_file); - c_file->args.append("-D_LIBC_REENTRANT"); - c_file->args.append("-DMODULE_NAME=libc"); - c_file->args.append("-DTOP_NAMESPACE=glibc"); - c_file->args.append("-DASSEMBLER"); - c_file->args.append("-g"); - c_file->args.append("-Wa,--noexecstack"); - return build_libc_object(parent, "crtn", c_file, progress_node); - } else if (strcmp(file, "start.os") == 0) { - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = glibc_start_asm_path(parent, "start.S"); - glibc_add_include_dirs(parent, c_file); - c_file->args.append("-D_LIBC_REENTRANT"); - c_file->args.append("-include"); - c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "include" OS_SEP "libc-modules.h")); - c_file->args.append("-DMODULE_NAME=libc"); - c_file->args.append("-Wno-nonportable-include-path"); - c_file->args.append("-include"); - c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "include" OS_SEP "libc-symbols.h")); - c_file->args.append("-DPIC"); - c_file->args.append("-DSHARED"); - c_file->args.append("-DTOP_NAMESPACE=glibc"); - c_file->args.append("-DASSEMBLER"); - c_file->args.append("-g"); - c_file->args.append("-Wa,--noexecstack"); - return build_libc_object(parent, "start", c_file, progress_node); - } else if (strcmp(file, "abi-note.o") == 0) { - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = path_from_libc(parent, "glibc" OS_SEP "csu" OS_SEP "abi-note.S"); - c_file->args.append("-I"); - c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "csu")); - glibc_add_include_dirs(parent, c_file); - c_file->args.append("-D_LIBC_REENTRANT"); - c_file->args.append("-DMODULE_NAME=libc"); - c_file->args.append("-DTOP_NAMESPACE=glibc"); - c_file->args.append("-DASSEMBLER"); - c_file->args.append("-g"); - c_file->args.append("-Wa,--noexecstack"); - return build_libc_object(parent, "abi-note", c_file, progress_node); - } else if (strcmp(file, "Scrt1.o") == 0) { - const char *start_os = get_libc_crt_file(parent, "start.os", progress_node); - const char *abi_note_o = get_libc_crt_file(parent, "abi-note.o", progress_node); - CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeObj, nullptr, "Scrt1", progress_node); - codegen_add_object(child_gen, buf_create_from_str(start_os)); - codegen_add_object(child_gen, buf_create_from_str(abi_note_o)); - codegen_build_and_link(child_gen); - return buf_ptr(&child_gen->bin_file_output_path); - } else if (strcmp(file, "libc_nonshared.a") == 0) { - CodeGen *child_gen = create_child_codegen(parent, nullptr, OutTypeLib, nullptr, "c_nonshared", progress_node); - { - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = path_from_libc(parent, "glibc" OS_SEP "csu" OS_SEP "elf-init.c"); - c_file->args.append("-std=gnu11"); - c_file->args.append("-fgnu89-inline"); - c_file->args.append("-g"); - c_file->args.append("-O2"); - c_file->args.append("-fmerge-all-constants"); - c_file->args.append("-fno-stack-protector"); - c_file->args.append("-fmath-errno"); - c_file->args.append("-fno-stack-protector"); - c_file->args.append("-I"); - c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "csu")); - glibc_add_include_dirs(parent, c_file); - c_file->args.append("-DSTACK_PROTECTOR_LEVEL=0"); - c_file->args.append("-fPIC"); - c_file->args.append("-fno-stack-protector"); - c_file->args.append("-ftls-model=initial-exec"); - c_file->args.append("-D_LIBC_REENTRANT"); - c_file->args.append("-include"); - c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "include" OS_SEP "libc-modules.h")); - c_file->args.append("-DMODULE_NAME=libc"); - c_file->args.append("-Wno-nonportable-include-path"); - c_file->args.append("-include"); - c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "include" OS_SEP "libc-symbols.h")); - c_file->args.append("-DPIC"); - c_file->args.append("-DLIBC_NONSHARED=1"); - c_file->args.append("-DTOP_NAMESPACE=glibc"); - codegen_add_object(child_gen, buf_create_from_str( - build_libc_object(parent, "elf-init", c_file, progress_node))); - } - static const struct { - const char *name; - const char *path; - } deps[] = { - {"atexit", "glibc" OS_SEP "stdlib" OS_SEP "atexit.c"}, - {"at_quick_exit", "glibc" OS_SEP "stdlib" OS_SEP "at_quick_exit.c"}, - {"stat", "glibc" OS_SEP "io" OS_SEP "stat.c"}, - {"fstat", "glibc" OS_SEP "io" OS_SEP "fstat.c"}, - {"lstat", "glibc" OS_SEP "io" OS_SEP "lstat.c"}, - {"stat64", "glibc" OS_SEP "io" OS_SEP "stat64.c"}, - {"fstat64", "glibc" OS_SEP "io" OS_SEP "fstat64.c"}, - {"lstat64", "glibc" OS_SEP "io" OS_SEP "lstat64.c"}, - {"fstatat", "glibc" OS_SEP "io" OS_SEP "fstatat.c"}, - {"fstatat64", "glibc" OS_SEP "io" OS_SEP "fstatat64.c"}, - {"mknod", "glibc" OS_SEP "io" OS_SEP "mknod.c"}, - {"mknodat", "glibc" OS_SEP "io" OS_SEP "mknodat.c"}, - {"pthread_atfork", "glibc" OS_SEP "nptl" OS_SEP "pthread_atfork.c"}, - {"stack_chk_fail_local", "glibc" OS_SEP "debug" OS_SEP "stack_chk_fail_local.c"}, - }; - for (size_t i = 0; i < array_length(deps); i += 1) { - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = path_from_libc(parent, deps[i].path); - c_file->args.append("-std=gnu11"); - c_file->args.append("-fgnu89-inline"); - c_file->args.append("-g"); - c_file->args.append("-O2"); - c_file->args.append("-fmerge-all-constants"); - c_file->args.append("-fno-stack-protector"); - c_file->args.append("-fmath-errno"); - c_file->args.append("-ftls-model=initial-exec"); - c_file->args.append("-Wno-ignored-attributes"); - glibc_add_include_dirs(parent, c_file); - c_file->args.append("-D_LIBC_REENTRANT"); - c_file->args.append("-include"); - c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "include" OS_SEP "libc-modules.h")); - c_file->args.append("-DMODULE_NAME=libc"); - c_file->args.append("-Wno-nonportable-include-path"); - c_file->args.append("-include"); - c_file->args.append(path_from_libc(parent, "glibc" OS_SEP "include" OS_SEP "libc-symbols.h")); - c_file->args.append("-DPIC"); - c_file->args.append("-DLIBC_NONSHARED=1"); - c_file->args.append("-DTOP_NAMESPACE=glibc"); - codegen_add_object(child_gen, buf_create_from_str( - build_libc_object(parent, deps[i].name, c_file, progress_node))); - } - codegen_build_and_link(child_gen); - return buf_ptr(&child_gen->bin_file_output_path); - } else { - zig_unreachable(); - } - } else if (parent->libc == nullptr && target_is_musl(parent->zig_target)) { - if (strcmp(file, "crti.o") == 0) { - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = musl_start_asm_path(parent, "crti.s"); - musl_add_cc_args(parent, c_file, false); - c_file->args.append("-Qunused-arguments"); - return build_libc_object(parent, "crti", c_file, progress_node); - } else if (strcmp(file, "crtn.o") == 0) { - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = musl_start_asm_path(parent, "crtn.s"); - c_file->args.append("-Qunused-arguments"); - musl_add_cc_args(parent, c_file, false); - return build_libc_object(parent, "crtn", c_file, progress_node); - } else if (strcmp(file, "crt1.o") == 0) { - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = path_from_libc(parent, "musl" OS_SEP "crt" OS_SEP "crt1.c"); - musl_add_cc_args(parent, c_file, false); - c_file->args.append("-fno-stack-protector"); - c_file->args.append("-DCRT"); - return build_libc_object(parent, "crt1", c_file, progress_node); - } else if (strcmp(file, "Scrt1.o") == 0) { - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = path_from_libc(parent, "musl" OS_SEP "crt" OS_SEP "Scrt1.c"); - musl_add_cc_args(parent, c_file, false); - c_file->args.append("-fPIC"); - c_file->args.append("-fno-stack-protector"); - c_file->args.append("-DCRT"); - return build_libc_object(parent, "Scrt1", c_file, progress_node); - } else { - zig_unreachable(); - } - } else { - assert(parent->libc != nullptr); - Buf *out_buf = buf_alloc(); - os_path_join(buf_create_from_mem(parent->libc->crt_dir, parent->libc->crt_dir_len), - buf_create_from_str(file), out_buf); - return buf_ptr(out_buf); - } -} - -static Buf *build_a_raw(CodeGen *parent_gen, const char *aname, Buf *full_path, OutType child_out_type, - Stage2ProgressNode *progress_node) -{ - CodeGen *child_gen = create_child_codegen(parent_gen, full_path, child_out_type, parent_gen->libc, aname, - progress_node); - - // This is so that compiler_rt and libc.zig libraries know whether they - // will eventually be linked with libc. They make different decisions - // about what to export depending on whether libc is linked. - if (parent_gen->libc_link_lib != nullptr) { - LinkLib *new_link_lib = codegen_add_link_lib(child_gen, parent_gen->libc_link_lib->name); - new_link_lib->provided_explicitly = parent_gen->libc_link_lib->provided_explicitly; - } - - // Override the inherited build mode parameter - if (!parent_gen->is_test_build) { - switch (parent_gen->build_mode) { - case BuildModeDebug: - case BuildModeFastRelease: - case BuildModeSafeRelease: - child_gen->build_mode = BuildModeFastRelease; - break; - case BuildModeSmallRelease: - break; - } - } - - child_gen->function_sections = true; - child_gen->want_stack_check = WantStackCheckDisabled; - - codegen_build_and_link(child_gen); - return &child_gen->bin_file_output_path; -} - -static Buf *build_compiler_rt(CodeGen *parent_gen, OutType child_out_type, Stage2ProgressNode *progress_node) { - Buf *full_path = buf_alloc(); - os_path_join(parent_gen->zig_std_special_dir, buf_create_from_str("compiler_rt.zig"), full_path); - - return build_a_raw(parent_gen, "compiler_rt", full_path, child_out_type, progress_node); -} - -static Buf *build_c(CodeGen *parent_gen, OutType child_out_type, Stage2ProgressNode *progress_node) { - Buf *full_path = buf_alloc(); - os_path_join(parent_gen->zig_std_special_dir, buf_create_from_str("c.zig"), full_path); - - return build_a_raw(parent_gen, "c", full_path, child_out_type, progress_node); -} - -static const char *get_darwin_arch_string(const ZigTarget *t) { - switch (t->arch) { - case ZigLLVM_aarch64: - return "arm64"; - case ZigLLVM_thumb: - case ZigLLVM_arm: - return "arm"; - case ZigLLVM_ppc: - return "ppc"; - case ZigLLVM_ppc64: - return "ppc64"; - case ZigLLVM_ppc64le: - return "ppc64le"; - default: - return ZigLLVMGetArchTypeName(t->arch); - } -} - - -static const char *getLDMOption(const ZigTarget *t) { - switch (t->arch) { - case ZigLLVM_x86: - return "elf_i386"; - case ZigLLVM_aarch64: - return "aarch64linux"; - case ZigLLVM_aarch64_be: - return "aarch64_be_linux"; - case ZigLLVM_arm: - case ZigLLVM_thumb: - return "armelf_linux_eabi"; - case ZigLLVM_armeb: - case ZigLLVM_thumbeb: - return "armebelf_linux_eabi"; - case ZigLLVM_ppc: - return "elf32ppclinux"; - case ZigLLVM_ppc64: - return "elf64ppc"; - case ZigLLVM_ppc64le: - return "elf64lppc"; - case ZigLLVM_sparc: - case ZigLLVM_sparcel: - return "elf32_sparc"; - case ZigLLVM_sparcv9: - return "elf64_sparc"; - case ZigLLVM_mips: - return "elf32btsmip"; - case ZigLLVM_mipsel: - return "elf32ltsmip"; - return "elf64btsmip"; - case ZigLLVM_mips64el: - return "elf64ltsmip"; - case ZigLLVM_systemz: - return "elf64_s390"; - case ZigLLVM_x86_64: - if (t->abi == ZigLLVM_GNUX32) { - return "elf32_x86_64"; - } - // Any target elf will use the freebsd osabi if suffixed with "_fbsd". - if (t->os == OsFreeBSD) { - return "elf_x86_64_fbsd"; - } - return "elf_x86_64"; - case ZigLLVM_riscv32: - return "elf32lriscv"; - case ZigLLVM_riscv64: - return "elf64lriscv"; - default: - zig_unreachable(); - } -} - -static void add_rpath(LinkJob *lj, Buf *rpath) { - if (lj->rpath_table.maybe_get(rpath) != nullptr) - return; - - lj->args.append("-rpath"); - lj->args.append(buf_ptr(rpath)); - - lj->rpath_table.put(rpath, true); -} - -static void add_glibc_libs(LinkJob *lj) { - Error err; - ZigGLibCAbi *glibc_abi; - if ((err = glibc_load_metadata(&glibc_abi, lj->codegen->zig_lib_dir, true))) { - fprintf(stderr, "%s\n", err_str(err)); - exit(1); - } - - Buf *artifact_dir; - if ((err = glibc_build_dummies_and_maps(lj->codegen, glibc_abi, lj->codegen->zig_target, - &artifact_dir, true, lj->build_dep_prog_node))) - { - fprintf(stderr, "%s\n", err_str(err)); - exit(1); - } - - size_t lib_count = glibc_lib_count(); - for (size_t i = 0; i < lib_count; i += 1) { - const ZigGLibCLib *lib = glibc_lib_enum(i); - Buf *so_path = buf_sprintf("%s" OS_SEP "lib%s.so.%d.0.0", buf_ptr(artifact_dir), lib->name, lib->sover); - lj->args.append(buf_ptr(so_path)); - } -} - -static void construct_linker_job_elf(LinkJob *lj) { - CodeGen *g = lj->codegen; - - lj->args.append("-error-limit=0"); - - if (g->out_type == OutTypeExe) { - lj->args.append("-z"); - size_t stack_size = (g->stack_size_override == 0) ? 16777216 : g->stack_size_override; - lj->args.append(buf_ptr(buf_sprintf("stack-size=%" ZIG_PRI_usize, stack_size))); - } - - if (g->linker_script) { - lj->args.append("-T"); - lj->args.append(g->linker_script); - } - - switch (g->linker_gc_sections) { - case OptionalBoolNull: - if (g->out_type != OutTypeObj) { - lj->args.append("--gc-sections"); - } - break; - case OptionalBoolTrue: - lj->args.append("--gc-sections"); - break; - case OptionalBoolFalse: - break; - } - - if (g->link_eh_frame_hdr) { - lj->args.append("--eh-frame-hdr"); - } - - if (g->linker_rdynamic) { - lj->args.append("--export-dynamic"); - } - - if (g->linker_optimization != nullptr) { - lj->args.append(buf_ptr(g->linker_optimization)); - } - - if (g->linker_z_nodelete) { - lj->args.append("-z"); - lj->args.append("nodelete"); - } - if (g->linker_z_defs) { - lj->args.append("-z"); - lj->args.append("defs"); - } - - lj->args.append("-m"); - lj->args.append(getLDMOption(g->zig_target)); - - bool is_lib = g->out_type == OutTypeLib; - bool is_dyn_lib = g->is_dynamic && is_lib; - if (!g->have_dynamic_link) { - if (g->zig_target->arch == ZigLLVM_arm || g->zig_target->arch == ZigLLVM_armeb || - g->zig_target->arch == ZigLLVM_thumb || g->zig_target->arch == ZigLLVM_thumbeb) - { - lj->args.append("-Bstatic"); - } else { - lj->args.append("-static"); - } - } else if (is_dyn_lib) { - lj->args.append("-shared"); - } - - if (target_requires_pie(g->zig_target) && g->out_type == OutTypeExe) { - lj->args.append("-pie"); - } - - assert(buf_len(&g->bin_file_output_path) != 0); - lj->args.append("-o"); - lj->args.append(buf_ptr(&g->bin_file_output_path)); - - if (lj->link_in_crt) { - const char *crt1o; - if (g->zig_target->os == OsNetBSD) { - crt1o = "crt0.o"; - } else if (target_is_android(g->zig_target)) { - if (g->have_dynamic_link) { - crt1o = "crtbegin_dynamic.o"; - } else { - crt1o = "crtbegin_static.o"; - } - } else if (!g->have_dynamic_link) { - crt1o = "crt1.o"; - } else { - crt1o = "Scrt1.o"; - } - lj->args.append(get_libc_crt_file(g, crt1o, lj->build_dep_prog_node)); - if (target_libc_needs_crti_crtn(g->zig_target)) { - lj->args.append(get_libc_crt_file(g, "crti.o", lj->build_dep_prog_node)); - } - } - - for (size_t i = 0; i < g->rpath_list.length; i += 1) { - Buf *rpath = g->rpath_list.at(i); - add_rpath(lj, rpath); - } - if (g->each_lib_rpath) { - for (size_t i = 0; i < g->lib_dirs.length; i += 1) { - const char *lib_dir = g->lib_dirs.at(i); - for (size_t i = 0; i < g->link_libs_list.length; i += 1) { - LinkLib *link_lib = g->link_libs_list.at(i); - if (buf_eql_str(link_lib->name, "c")) { - continue; - } - bool does_exist; - Buf *test_path = buf_sprintf("%s/lib%s.so", lib_dir, buf_ptr(link_lib->name)); - if (os_file_exists(test_path, &does_exist) != ErrorNone) { - zig_panic("link: unable to check if file exists: %s", buf_ptr(test_path)); - } - if (does_exist) { - add_rpath(lj, buf_create_from_str(lib_dir)); - break; - } - } - } - } - - for (size_t i = 0; i < g->lib_dirs.length; i += 1) { - const char *lib_dir = g->lib_dirs.at(i); - lj->args.append("-L"); - lj->args.append(lib_dir); - } - - if (g->libc_link_lib != nullptr) { - if (g->libc != nullptr) { - lj->args.append("-L"); - lj->args.append(buf_ptr(buf_create_from_mem(g->libc->crt_dir, g->libc->crt_dir_len))); - } - - if (g->have_dynamic_link && (is_dyn_lib || g->out_type == OutTypeExe)) { - assert(g->zig_target->dynamic_linker != nullptr); - lj->args.append("-dynamic-linker"); - lj->args.append(g->zig_target->dynamic_linker); - } - } - - if (is_dyn_lib) { - Buf *soname = (g->override_soname == nullptr) ? - buf_sprintf("lib%s.so.%" ZIG_PRI_usize, buf_ptr(g->root_out_name), g->version_major) : - g->override_soname; - lj->args.append("-soname"); - lj->args.append(buf_ptr(soname)); - - if (g->version_script_path != nullptr) { - lj->args.append("-version-script"); - lj->args.append(buf_ptr(g->version_script_path)); - } - } - - // .o files - for (size_t i = 0; i < g->link_objects.length; i += 1) { - lj->args.append((const char *)buf_ptr(g->link_objects.at(i))); - } - - if (!g->is_dummy_so && (g->out_type == OutTypeExe || is_dyn_lib)) { - if (g->libc_link_lib == nullptr) { - Buf *libc_a_path = build_c(g, OutTypeLib, lj->build_dep_prog_node); - lj->args.append(buf_ptr(libc_a_path)); - } - - Buf *compiler_rt_o_path = build_compiler_rt(g, OutTypeLib, lj->build_dep_prog_node); - lj->args.append(buf_ptr(compiler_rt_o_path)); - } - - // libraries - for (size_t i = 0; i < g->link_libs_list.length; i += 1) { - LinkLib *link_lib = g->link_libs_list.at(i); - if (buf_eql_str(link_lib->name, "c")) { - // libc is linked specially - continue; - } - if (target_is_libcpp_lib_name(g->zig_target, buf_ptr(link_lib->name))) { - // libc++ is linked specially - continue; - } - if (g->libc == nullptr && target_is_libc_lib_name(g->zig_target, buf_ptr(link_lib->name))) { - // these libraries are always linked below when targeting glibc - continue; - } - Buf *arg; - if (buf_starts_with_str(link_lib->name, "/") || buf_ends_with_str(link_lib->name, ".a") || - buf_ends_with_str(link_lib->name, ".so")) - { - arg = link_lib->name; - } else { - arg = buf_sprintf("-l%s", buf_ptr(link_lib->name)); - } - lj->args.append(buf_ptr(arg)); - } - - // libc++ dep - if (g->libcpp_link_lib != nullptr && g->out_type != OutTypeObj) { - lj->args.append(build_libcxxabi(g, lj->build_dep_prog_node)); - lj->args.append(build_libcxx(g, lj->build_dep_prog_node)); - } - - // libc dep - if (g->libc_link_lib != nullptr && g->out_type != OutTypeObj) { - if (g->libc != nullptr) { - if (!g->have_dynamic_link) { - lj->args.append("--start-group"); - lj->args.append("-lc"); - lj->args.append("-lm"); - lj->args.append("--end-group"); - } else { - lj->args.append("-lc"); - lj->args.append("-lm"); - } - - if (g->zig_target->os == OsFreeBSD || - g->zig_target->os == OsNetBSD) - { - lj->args.append("-lpthread"); - } - } else if (target_is_glibc(g->zig_target)) { - lj->args.append(build_libunwind(g, lj->build_dep_prog_node)); - add_glibc_libs(lj); - lj->args.append(get_libc_crt_file(g, "libc_nonshared.a", lj->build_dep_prog_node)); - } else if (target_is_musl(g->zig_target)) { - lj->args.append(build_libunwind(g, lj->build_dep_prog_node)); - lj->args.append(build_musl(g, lj->build_dep_prog_node)); - } else if (g->libcpp_link_lib != nullptr) { - lj->args.append(build_libunwind(g, lj->build_dep_prog_node)); - } else { - zig_unreachable(); - } - } - - // crt end - if (lj->link_in_crt) { - if (target_is_android(g->zig_target)) { - lj->args.append(get_libc_crt_file(g, "crtend_android.o", lj->build_dep_prog_node)); - } else if (target_libc_needs_crti_crtn(g->zig_target)) { - lj->args.append(get_libc_crt_file(g, "crtn.o", lj->build_dep_prog_node)); - } - } - - switch (g->linker_allow_shlib_undefined) { - case OptionalBoolNull: - if (!g->zig_target->is_native_os) { - lj->args.append("--allow-shlib-undefined"); - } - break; - case OptionalBoolFalse: - break; - case OptionalBoolTrue: - lj->args.append("--allow-shlib-undefined"); - break; - } - switch (g->linker_bind_global_refs_locally) { - case OptionalBoolNull: - case OptionalBoolFalse: - break; - case OptionalBoolTrue: - lj->args.append("-Bsymbolic"); - break; - } -} - -static void construct_linker_job_wasm(LinkJob *lj) { - CodeGen *g = lj->codegen; - - lj->args.append("-error-limit=0"); - // Increase the default stack size to a more reasonable value of 1MB instead of - // the default of 1 Wasm page being 64KB, unless overriden by the user. - size_t stack_size = (g->stack_size_override == 0) ? 1048576 : g->stack_size_override; - lj->args.append("-z"); - lj->args.append(buf_ptr(buf_sprintf("stack-size=%" ZIG_PRI_usize, stack_size))); - - // put stack before globals so that stack overflow results in segfault immediately before corrupting globals - // see https://github.com/ziglang/zig/issues/4496 - lj->args.append("--stack-first"); - - if (g->out_type != OutTypeExe) { - lj->args.append("--no-entry"); // So lld doesn't look for _start. - - // If there are any C source files we cannot rely on individual exports. - if (g->c_source_files.length != 0) { - lj->args.append("--export-all"); - } else { - auto export_it = g->exported_symbol_names.entry_iterator(); - decltype(g->exported_symbol_names)::Entry *curr_entry = nullptr; - while ((curr_entry = export_it.next()) != nullptr) { - Buf *arg = buf_sprintf("--export=%s", buf_ptr(curr_entry->key)); - lj->args.append(buf_ptr(arg)); - } - } - } - lj->args.append("--allow-undefined"); - lj->args.append("-o"); - lj->args.append(buf_ptr(&g->bin_file_output_path)); - - // .o files - for (size_t i = 0; i < g->link_objects.length; i += 1) { - lj->args.append((const char *)buf_ptr(g->link_objects.at(i))); - } - - if (g->out_type != OutTypeObj) { - Buf *libc_o_path = build_c(g, OutTypeObj, lj->build_dep_prog_node); - lj->args.append(buf_ptr(libc_o_path)); - - Buf *compiler_rt_o_path = build_compiler_rt(g, OutTypeObj, lj->build_dep_prog_node); - lj->args.append(buf_ptr(compiler_rt_o_path)); - } -} - -static void coff_append_machine_arg(CodeGen *g, ZigList *list) { - if (g->zig_target->arch == ZigLLVM_x86) { - list->append("-MACHINE:X86"); - } else if (g->zig_target->arch == ZigLLVM_x86_64) { - list->append("-MACHINE:X64"); - } else if (target_is_arm(g->zig_target)) { - if (target_arch_pointer_bit_width(g->zig_target->arch) == 32) { - list->append("-MACHINE:ARM"); - } else { - list->append("-MACHINE:ARM64"); - } - } -} - -static void link_diag_callback(void *context, const char *ptr, size_t len) { - Buf *diag = reinterpret_cast(context); - buf_append_mem(diag, ptr, len); -} - -static bool zig_lld_link(ZigLLVM_ObjectFormatType oformat, const char **args, size_t arg_count, - Buf *diag) -{ - Buf *stdout_diag = buf_alloc(); - buf_resize(diag, 0); - bool result = ZigLLDLink(oformat, args, arg_count, link_diag_callback, stdout_diag, diag); - buf_destroy(stdout_diag); - return result; -} - -static void add_uefi_link_args(LinkJob *lj) { - lj->args.append("-BASE:0"); - lj->args.append("-ENTRY:EfiMain"); - lj->args.append("-OPT:REF"); - lj->args.append("-SAFESEH:NO"); - lj->args.append("-MERGE:.rdata=.data"); - lj->args.append("-ALIGN:32"); - lj->args.append("-NODEFAULTLIB"); - lj->args.append("-SECTION:.xdata,D"); -} - -static void add_msvc_link_args(LinkJob *lj, bool is_library) { - CodeGen *g = lj->codegen; - - bool is_dynamic = g->is_dynamic; - const char *lib_str = is_dynamic ? "" : "lib"; - const char *d_str = (g->build_mode == BuildModeDebug) ? "d" : ""; - - if (!is_dynamic) { - Buf *cmt_lib_name = buf_sprintf("libcmt%s.lib", d_str); - lj->args.append(buf_ptr(cmt_lib_name)); - } else { - Buf *msvcrt_lib_name = buf_sprintf("msvcrt%s.lib", d_str); - lj->args.append(buf_ptr(msvcrt_lib_name)); - } - - Buf *vcruntime_lib_name = buf_sprintf("%svcruntime%s.lib", lib_str, d_str); - lj->args.append(buf_ptr(vcruntime_lib_name)); - - Buf *crt_lib_name = buf_sprintf("%sucrt%s.lib", lib_str, d_str); - lj->args.append(buf_ptr(crt_lib_name)); - - //Visual C++ 2015 Conformance Changes - //https://msdn.microsoft.com/en-us/library/bb531344.aspx - lj->args.append("legacy_stdio_definitions.lib"); - - // msvcrt depends on kernel32 and ntdll - lj->args.append("kernel32.lib"); - lj->args.append("ntdll.lib"); -} - -static void print_zig_cc_cmd(ZigList *args) { - for (size_t arg_i = 0; arg_i < args->length; arg_i += 1) { - const char *space_str = (arg_i == 0) ? "" : " "; - fprintf(stderr, "%s%s", space_str, args->at(arg_i)); - } - fprintf(stderr, "\n"); -} - -static const char *get_def_lib(CodeGen *parent, const char *name, Buf *def_in_file) { - Error err; - - Buf *self_exe_path = buf_alloc(); - if ((err = os_self_exe_path(self_exe_path))) { - fprintf(stderr, "Unable to get self exe path: %s\n", err_str(err)); - exit(1); - } - Buf *compiler_id; - if ((err = get_compiler_id(&compiler_id))) { - fprintf(stderr, "Unable to get compiler id: %s\n", err_str(err)); - exit(1); - } - - Buf *cache_dir = get_global_cache_dir(); - Buf *o_dir = buf_sprintf("%s" OS_SEP CACHE_OUT_SUBDIR, buf_ptr(cache_dir)); - Buf *manifest_dir = buf_sprintf("%s" OS_SEP CACHE_HASH_SUBDIR, buf_ptr(cache_dir)); - - Buf *def_include_dir = buf_sprintf("%s" OS_SEP "libc" OS_SEP "mingw" OS_SEP "def-include", - buf_ptr(parent->zig_lib_dir)); - - CacheHash *cache_hash = heap::c_allocator.create(); - cache_init(cache_hash, manifest_dir); - - cache_buf(cache_hash, compiler_id); - cache_file(cache_hash, def_in_file); - cache_buf(cache_hash, def_include_dir); - cache_int(cache_hash, parent->zig_target->arch); - - Buf digest = BUF_INIT; - buf_resize(&digest, 0); - if ((err = cache_hit(cache_hash, &digest))) { - if (err != ErrorInvalidFormat) { - if (err == ErrorCacheUnavailable) { - // already printed error - } else { - fprintf(stderr, "unable to check cache when processing .def.in file: %s\n", err_str(err)); - } - exit(1); - } - } - - Buf *artifact_dir; - Buf *lib_final_path; - Buf *final_lib_basename = buf_sprintf("%s.lib", name); - - bool is_cache_miss = (buf_len(&digest) == 0); - if (is_cache_miss) { - if ((err = cache_final(cache_hash, &digest))) { - fprintf(stderr, "Unable to finalize cache hash: %s\n", err_str(err)); - exit(1); - } - artifact_dir = buf_alloc(); - os_path_join(o_dir, &digest, artifact_dir); - if ((err = os_make_path(artifact_dir))) { - fprintf(stderr, "Unable to create output directory '%s': %s", - buf_ptr(artifact_dir), err_str(err)); - exit(1); - } - Buf *final_def_basename = buf_sprintf("%s.def", name); - Buf *def_final_path = buf_alloc(); - os_path_join(artifact_dir, final_def_basename, def_final_path); - - ZigList args = {}; - args.append(buf_ptr(self_exe_path)); - args.append("clang"); - args.append("-x"); - args.append("c"); - args.append(buf_ptr(def_in_file)); - args.append("-Wp,-w"); - args.append("-undef"); - args.append("-P"); - args.append("-I"); - args.append(buf_ptr(def_include_dir)); - if (target_is_arm(parent->zig_target)) { - if (target_arch_pointer_bit_width(parent->zig_target->arch) == 32) { - args.append("-DDEF_ARM32"); - } else { - args.append("-DDEF_ARM64"); - } - } else if (parent->zig_target->arch == ZigLLVM_x86) { - args.append("-DDEF_I386"); - } else if (parent->zig_target->arch == ZigLLVM_x86_64) { - args.append("-DDEF_X64"); - } else { - zig_unreachable(); - } - args.append("-E"); - args.append("-o"); - args.append(buf_ptr(def_final_path)); - - if (parent->verbose_cc) { - print_zig_cc_cmd(&args); - } - Termination term; - os_spawn_process(args, &term); - if (term.how != TerminationIdClean || term.code != 0) { - fprintf(stderr, "\nThe following command failed:\n"); - print_zig_cc_cmd(&args); - exit(1); - } - - lib_final_path = buf_alloc(); - os_path_join(artifact_dir, final_lib_basename, lib_final_path); - - if (ZigLLVMWriteImportLibrary(buf_ptr(def_final_path), - parent->zig_target->arch, - buf_ptr(lib_final_path), - /* kill_at */ true)) - { - zig_panic("link: could not emit %s", buf_ptr(lib_final_path)); - } - } else { - // cache hit - artifact_dir = buf_alloc(); - os_path_join(o_dir, &digest, artifact_dir); - lib_final_path = buf_alloc(); - os_path_join(artifact_dir, final_lib_basename, lib_final_path); - } - parent->caches_to_release.append(cache_hash); - - return buf_ptr(lib_final_path); -} - -static bool is_linking_system_lib(CodeGen *g, const char *name) { - for (size_t lib_i = 0; lib_i < g->link_libs_list.length; lib_i += 1) { - LinkLib *link_lib = g->link_libs_list.at(lib_i); - if (buf_eql_str(link_lib->name, name)) { - return true; - } - } - return false; -} - -static Error find_mingw_lib_def(LinkJob *lj, const char *name, Buf *out_path) { - CodeGen *g = lj->codegen; - Buf override_path = BUF_INIT; - Error err; - - char const *lib_path = nullptr; - if (g->zig_target->arch == ZigLLVM_x86) { - lib_path = "lib32"; - } else if (g->zig_target->arch == ZigLLVM_x86_64) { - lib_path = "lib64"; - } else if (target_is_arm(g->zig_target)) { - const bool is_32 = target_arch_pointer_bit_width(g->zig_target->arch) == 32; - lib_path = is_32 ? "libarm32" : "libarm64"; - } else { - zig_unreachable(); - } - - // Try the archtecture-specific path first - buf_resize(&override_path, 0); - buf_appendf(&override_path, "%s" OS_SEP "libc" OS_SEP "mingw" OS_SEP "%s" OS_SEP "%s.def", buf_ptr(g->zig_lib_dir), lib_path, name); - - bool does_exist; - if ((err = os_file_exists(&override_path, &does_exist)) != ErrorNone) { - return err; - } - - if (!does_exist) { - // Try the generic version - buf_resize(&override_path, 0); - buf_appendf(&override_path, "%s" OS_SEP "libc" OS_SEP "mingw" OS_SEP "lib-common" OS_SEP "%s.def", buf_ptr(g->zig_lib_dir), name); - - if ((err = os_file_exists(&override_path, &does_exist)) != ErrorNone) { - return err; - } - } - - if (!does_exist) { - // Try the generic version and preprocess it - buf_resize(&override_path, 0); - buf_appendf(&override_path, "%s" OS_SEP "libc" OS_SEP "mingw" OS_SEP "lib-common" OS_SEP "%s.def.in", buf_ptr(g->zig_lib_dir), name); - - if ((err = os_file_exists(&override_path, &does_exist)) != ErrorNone) { - return err; - } - } - - if (!does_exist) { - return ErrorFileNotFound; - } - - buf_init_from_buf(out_path, &override_path); - return ErrorNone; -} - -static void add_mingw_link_args(LinkJob *lj, bool is_library) { - CodeGen *g = lj->codegen; - - lj->args.append("-lldmingw"); - - bool is_dll = g->out_type == OutTypeLib && g->is_dynamic; - - if (g->zig_target->arch == ZigLLVM_x86) { - lj->args.append("-ALTERNATENAME:__image_base__=___ImageBase"); - } else { - lj->args.append("-ALTERNATENAME:__image_base__=__ImageBase"); - } - - if (is_dll) { - lj->args.append(get_libc_crt_file(g, "dllcrt2.o", lj->build_dep_prog_node)); - } else { - lj->args.append(get_libc_crt_file(g, "crt2.o", lj->build_dep_prog_node)); - } - - lj->args.append(get_libc_crt_file(g, "mingw32.lib", lj->build_dep_prog_node)); - lj->args.append(get_libc_crt_file(g, "mingwex.lib", lj->build_dep_prog_node)); - lj->args.append(get_libc_crt_file(g, "msvcrt-os.lib", lj->build_dep_prog_node)); - - for (size_t def_i = 0; def_i < array_length(mingw_def_list); def_i += 1) { - const char *name = mingw_def_list[def_i].name; - const bool always_link = mingw_def_list[def_i].always_link; - - if (always_link || is_linking_system_lib(g, name)) { - Buf lib_path = BUF_INIT; - Error err = find_mingw_lib_def(lj, name, &lib_path); - - if (err == ErrorFileNotFound) { - zig_panic("link: could not find .def file to build %s\n", name); - } else if (err != ErrorNone) { - zig_panic("link: unable to check if .def file for %s exists: %s", - name, err_str(err)); - } - - lj->args.append(get_def_lib(g, name, &lib_path)); - } - } -} - -static void add_win_link_args(LinkJob *lj, bool is_library, bool *have_windows_dll_import_libs) { - if (lj->link_in_crt) { - if (target_abi_is_gnu(lj->codegen->zig_target->abi)) { - *have_windows_dll_import_libs = true; - add_mingw_link_args(lj, is_library); - } else { - add_msvc_link_args(lj, is_library); - } - } else { - lj->args.append("-NODEFAULTLIB"); - if (!is_library) { - if (lj->codegen->have_winmain) { - lj->args.append("-ENTRY:WinMain"); - } else if (lj->codegen->have_wwinmain) { - lj->args.append("-ENTRY:wWinMain"); - } else if (lj->codegen->have_wwinmain_crt_startup) { - lj->args.append("-ENTRY:wWinMainCRTStartup"); - } else { - lj->args.append("-ENTRY:WinMainCRTStartup"); - } - } - } -} - -static bool is_mingw_link_lib(Buf *name) { - for (size_t def_i = 0; def_i < array_length(mingw_def_list); def_i += 1) { - if (buf_eql_str_ignore_case(name, mingw_def_list[def_i].name)) { - return true; - } - } - return false; -} -static void construct_linker_job_coff(LinkJob *lj) { - Error err; - CodeGen *g = lj->codegen; - - lj->args.append("-ERRORLIMIT:0"); - - lj->args.append("-NOLOGO"); - - if (!g->strip_debug_symbols) { - lj->args.append("-DEBUG"); - } - - if (g->out_type == OutTypeExe) { - // TODO compile time stack upper bound detection - size_t stack_size = (g->stack_size_override == 0) ? 16777216 : g->stack_size_override; - lj->args.append(buf_ptr(buf_sprintf("-STACK:%" ZIG_PRI_usize, stack_size))); - } - - coff_append_machine_arg(g, &lj->args); - - bool is_library = g->out_type == OutTypeLib; - if (is_library && g->is_dynamic) { - lj->args.append("-DLL"); - } - - lj->args.append(buf_ptr(buf_sprintf("-OUT:%s", buf_ptr(&g->bin_file_output_path)))); - - if (g->libc_link_lib != nullptr && g->libc != nullptr) { - Buf *buff0 = buf_create_from_str("-LIBPATH:"); - buf_append_mem(buff0, g->libc->crt_dir, g->libc->crt_dir_len); - lj->args.append(buf_ptr(buff0)); - - if (target_abi_is_gnu(g->zig_target->abi)) { - Buf *buff1 = buf_create_from_str("-LIBPATH:"); - buf_append_mem(buff1, g->libc->sys_include_dir, g->libc->sys_include_dir_len); - lj->args.append(buf_ptr(buff1)); - - Buf *buff2 = buf_create_from_str("-LIBPATH:"); - buf_append_mem(buff2, g->libc->include_dir, g->libc->include_dir_len); - lj->args.append(buf_ptr(buff2)); - } else { - Buf *buff1 = buf_create_from_str("-LIBPATH:"); - buf_append_mem(buff1, g->libc->msvc_lib_dir, g->libc->msvc_lib_dir_len); - lj->args.append(buf_ptr(buff1)); - - Buf *buff2 = buf_create_from_str("-LIBPATH:"); - buf_append_mem(buff2, g->libc->kernel32_lib_dir, g->libc->kernel32_lib_dir_len); - lj->args.append(buf_ptr(buff2)); - } - } - - for (size_t i = 0; i < g->lib_dirs.length; i += 1) { - const char *lib_dir = g->lib_dirs.at(i); - lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", lib_dir))); - } - - for (size_t i = 0; i < g->link_objects.length; i += 1) { - lj->args.append((const char *)buf_ptr(g->link_objects.at(i))); - } - - bool have_windows_dll_import_libs = false; - switch (detect_subsystem(g)) { - case TargetSubsystemAuto: - if (g->zig_target->os == OsUefi) { - add_uefi_link_args(lj); - } else { - add_win_link_args(lj, is_library, &have_windows_dll_import_libs); - } - break; - case TargetSubsystemConsole: - lj->args.append("-SUBSYSTEM:console"); - add_win_link_args(lj, is_library, &have_windows_dll_import_libs); - break; - case TargetSubsystemEfiApplication: - lj->args.append("-SUBSYSTEM:efi_application"); - add_uefi_link_args(lj); - break; - case TargetSubsystemEfiBootServiceDriver: - lj->args.append("-SUBSYSTEM:efi_boot_service_driver"); - add_uefi_link_args(lj); - break; - case TargetSubsystemEfiRom: - lj->args.append("-SUBSYSTEM:efi_rom"); - add_uefi_link_args(lj); - break; - case TargetSubsystemEfiRuntimeDriver: - lj->args.append("-SUBSYSTEM:efi_runtime_driver"); - add_uefi_link_args(lj); - break; - case TargetSubsystemNative: - lj->args.append("-SUBSYSTEM:native"); - add_win_link_args(lj, is_library, &have_windows_dll_import_libs); - break; - case TargetSubsystemPosix: - lj->args.append("-SUBSYSTEM:posix"); - add_win_link_args(lj, is_library, &have_windows_dll_import_libs); - break; - case TargetSubsystemWindows: - lj->args.append("-SUBSYSTEM:windows"); - add_win_link_args(lj, is_library, &have_windows_dll_import_libs); - break; - } - - // libc++ dep - if (g->libcpp_link_lib != nullptr && g->out_type != OutTypeObj) { - lj->args.append(build_libcxxabi(g, lj->build_dep_prog_node)); - lj->args.append(build_libcxx(g, lj->build_dep_prog_node)); - lj->args.append(build_libunwind(g, lj->build_dep_prog_node)); - } - - if (g->out_type == OutTypeExe || (g->out_type == OutTypeLib && g->is_dynamic)) { - if (g->libc_link_lib == nullptr && !g->is_dummy_so) { - Buf *libc_a_path = build_c(g, OutTypeLib, lj->build_dep_prog_node); - lj->args.append(buf_ptr(libc_a_path)); - } - - // msvc compiler_rt is missing some stuff, so we still build it and rely on weak linkage - Buf *compiler_rt_o_path = build_compiler_rt(g, OutTypeLib, lj->build_dep_prog_node); - lj->args.append(buf_ptr(compiler_rt_o_path)); - } - - for (size_t lib_i = 0; lib_i < g->link_libs_list.length; lib_i += 1) { - LinkLib *link_lib = g->link_libs_list.at(lib_i); - if (buf_eql_str(link_lib->name, "c")) { - continue; - } - if (target_is_libcpp_lib_name(g->zig_target, buf_ptr(link_lib->name))) { - // libc++ is linked specially - continue; - } - if (g->libc == nullptr && target_is_libc_lib_name(g->zig_target, buf_ptr(link_lib->name))) { - // these libraries are always linked below when targeting glibc - continue; - } - bool is_sys_lib = is_mingw_link_lib(link_lib->name); - if (have_windows_dll_import_libs && is_sys_lib) { - continue; - } - // If we're linking in the CRT or the libs are provided explictly we don't want to generate def/libs - if ((lj->link_in_crt && is_sys_lib) || link_lib->provided_explicitly) { - if (target_abi_is_gnu(lj->codegen->zig_target->abi)) { - if (buf_eql_str(link_lib->name, "uuid")) { - // mingw-w64 provides this lib - lj->args.append(get_libc_crt_file(g, "uuid.lib", lj->build_dep_prog_node)); - } else { - Buf* lib_name = buf_sprintf("lib%s.a", buf_ptr(link_lib->name)); - lj->args.append(buf_ptr(lib_name)); - } - } else { - Buf* lib_name = buf_sprintf("%s.lib", buf_ptr(link_lib->name)); - lj->args.append(buf_ptr(lib_name)); - } - continue; - } - - // This library may be a system one and we may have a suitable .lib file - - // Normalize the library name to lower case, the FS may be - // case-sensitive - char *name = strdup(buf_ptr(link_lib->name)); - assert(name != nullptr); - for (char *ch = name; *ch; ++ch) *ch = tolower(*ch); - - Buf lib_path = BUF_INIT; - err = find_mingw_lib_def(lj, name, &lib_path); - - if (err == ErrorFileNotFound) { - zig_panic("link: could not find .def file to build %s\n", name); - } else if (err != ErrorNone) { - zig_panic("link: unable to check if .def file for %s exists: %s", - name, err_str(err)); - } - - lj->args.append(get_def_lib(g, name, &lib_path)); - - mem::os::free(name); - } -} - -static void construct_linker_job_macho(LinkJob *lj) { - CodeGen *g = lj->codegen; - - lj->args.append("-error-limit"); - lj->args.append("0"); - lj->args.append("-demangle"); - - switch (g->linker_gc_sections) { - case OptionalBoolNull: - // TODO why do we not follow the same logic of elf here? - break; - case OptionalBoolTrue: - lj->args.append("--gc-sections"); - break; - case OptionalBoolFalse: - break; - } - - if (g->linker_rdynamic) { - lj->args.append("-export_dynamic"); - } - - if (g->linker_optimization != nullptr) { - lj->args.append(buf_ptr(g->linker_optimization)); - } - - if (g->linker_z_nodelete) { - lj->args.append("-z"); - lj->args.append("nodelete"); - } - if (g->linker_z_defs) { - lj->args.append("-z"); - lj->args.append("defs"); - } - - bool is_lib = g->out_type == OutTypeLib; - bool is_dyn_lib = g->is_dynamic && is_lib; - if (is_lib && !g->is_dynamic) { - lj->args.append("-static"); - } else { - lj->args.append("-dynamic"); - } - - if (is_dyn_lib) { - lj->args.append("-dylib"); - - Buf *compat_vers = buf_sprintf("%" ZIG_PRI_usize ".0.0", g->version_major); - lj->args.append("-compatibility_version"); - lj->args.append(buf_ptr(compat_vers)); - - Buf *cur_vers = buf_sprintf("%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize, - g->version_major, g->version_minor, g->version_patch); - lj->args.append("-current_version"); - lj->args.append(buf_ptr(cur_vers)); - - // TODO getting an error when running an executable when doing this rpath thing - //Buf *dylib_install_name = buf_sprintf("@rpath/lib%s.%" ZIG_PRI_usize ".dylib", - // buf_ptr(g->root_out_name), g->version_major); - //lj->args.append("-install_name"); - //lj->args.append(buf_ptr(dylib_install_name)); - - assert(buf_len(&g->bin_file_output_path) != 0); - } - - lj->args.append("-arch"); - lj->args.append(get_darwin_arch_string(g->zig_target)); - - if (g->zig_target->glibc_or_darwin_version != nullptr) { - if (g->zig_target->os == OsMacOSX) { - lj->args.append("-macosx_version_min"); - } else if (g->zig_target->os == OsIOS) { - if (g->zig_target->arch == ZigLLVM_x86 || g->zig_target->arch == ZigLLVM_x86_64) { - lj->args.append("-ios_simulator_version_min"); - } else { - lj->args.append("-iphoneos_version_min"); - } - } - - Buf *version_string = buf_sprintf("%d.%d.%d", - g->zig_target->glibc_or_darwin_version->major, - g->zig_target->glibc_or_darwin_version->minor, - g->zig_target->glibc_or_darwin_version->patch); - lj->args.append(buf_ptr(version_string)); - - lj->args.append("-sdk_version"); - lj->args.append(buf_ptr(version_string)); - } else if (stage2_is_zig0 && g->zig_target->os == OsMacOSX) { - // running `zig0`; `-pie` requires versions >= 10.5; select 10.13 - lj->args.append("-macosx_version_min"); - lj->args.append("10.13"); - lj->args.append("-sdk_version"); - lj->args.append("10.13"); - } - - if (g->out_type == OutTypeExe) { - lj->args.append("-pie"); - } - - lj->args.append("-o"); - lj->args.append(buf_ptr(&g->bin_file_output_path)); - - for (size_t i = 0; i < g->rpath_list.length; i += 1) { - Buf *rpath = g->rpath_list.at(i); - add_rpath(lj, rpath); - } - if (is_dyn_lib) { - add_rpath(lj, &g->bin_file_output_path); - } - - if (is_dyn_lib) { - if (g->system_linker_hack) { - lj->args.append("-headerpad_max_install_names"); - } - } - - for (size_t i = 0; i < g->lib_dirs.length; i += 1) { - const char *lib_dir = g->lib_dirs.at(i); - lj->args.append("-L"); - lj->args.append(lib_dir); - } - - // .o files - for (size_t i = 0; i < g->link_objects.length; i += 1) { - lj->args.append((const char *)buf_ptr(g->link_objects.at(i))); - } - - // compiler_rt on darwin is missing some stuff, so we still build it and rely on LinkOnce - if (g->out_type == OutTypeExe || is_dyn_lib) { - Buf *compiler_rt_o_path = build_compiler_rt(g, OutTypeLib, lj->build_dep_prog_node); - lj->args.append(buf_ptr(compiler_rt_o_path)); - } - - // libraries - for (size_t lib_i = 0; lib_i < g->link_libs_list.length; lib_i += 1) { - LinkLib *link_lib = g->link_libs_list.at(lib_i); - if (buf_eql_str(link_lib->name, "c")) { - // libc is linked specially - continue; - } - if (target_is_libcpp_lib_name(g->zig_target, buf_ptr(link_lib->name))) { - // libc++ is linked specially - continue; - } - if (g->zig_target->is_native_os && target_is_libc_lib_name(g->zig_target, buf_ptr(link_lib->name))) { - // libSystem is linked specially - continue; - } - - Buf *arg; - if (buf_starts_with_str(link_lib->name, "/") || buf_ends_with_str(link_lib->name, ".a") || - buf_ends_with_str(link_lib->name, ".dylib")) - { - arg = link_lib->name; - } else { - arg = buf_sprintf("-l%s", buf_ptr(link_lib->name)); - } - lj->args.append(buf_ptr(arg)); - } - - // libc++ dep - if (g->libcpp_link_lib != nullptr && g->out_type != OutTypeObj) { - lj->args.append(build_libcxxabi(g, lj->build_dep_prog_node)); - lj->args.append(build_libcxx(g, lj->build_dep_prog_node)); - } - - // libc dep - if (g->zig_target->is_native_os || stage2_is_zig0) { - // on Darwin, libSystem has libc in it, but also you have to use it - // to make syscalls because the syscall numbers are not documented - // and change between versions. - // so we always link against libSystem - lj->args.append("-lSystem"); - } - - for (size_t i = 0; i < g->framework_dirs.length; i += 1) { - const char *framework_dir = g->framework_dirs.at(i); - lj->args.append("-F"); - lj->args.append(framework_dir); - } - - for (size_t i = 0; i < g->darwin_frameworks.length; i += 1) { - lj->args.append("-framework"); - lj->args.append(buf_ptr(g->darwin_frameworks.at(i))); - } - - switch (g->linker_allow_shlib_undefined) { - case OptionalBoolNull: - if (!g->zig_target->is_native_os && !stage2_is_zig0) { - // TODO https://github.com/ziglang/zig/issues/5059 - lj->args.append("-undefined"); - lj->args.append("dynamic_lookup"); - } - break; - case OptionalBoolFalse: - break; - case OptionalBoolTrue: - lj->args.append("-undefined"); - lj->args.append("dynamic_lookup"); - break; - } - switch (g->linker_bind_global_refs_locally) { - case OptionalBoolNull: - case OptionalBoolFalse: - break; - case OptionalBoolTrue: - lj->args.append("-Bsymbolic"); - break; - } -} - -static void construct_linker_job(LinkJob *lj) { - switch (target_object_format(lj->codegen->zig_target)) { - case ZigLLVM_UnknownObjectFormat: - case ZigLLVM_XCOFF: - zig_unreachable(); - - case ZigLLVM_COFF: - return construct_linker_job_coff(lj); - case ZigLLVM_ELF: - return construct_linker_job_elf(lj); - case ZigLLVM_MachO: - return construct_linker_job_macho(lj); - case ZigLLVM_Wasm: - return construct_linker_job_wasm(lj); - } -} - -void zig_link_add_compiler_rt(CodeGen *g, Stage2ProgressNode *progress_node) { - Buf *compiler_rt_o_path = build_compiler_rt(g, OutTypeObj, progress_node); - g->link_objects.append(compiler_rt_o_path); -} - -void codegen_link(CodeGen *g) { - codegen_add_time_event(g, "Build Dependencies"); - LinkJob lj = {0}; - - { - const char *progress_name = "Build Dependencies"; - codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node, - progress_name, strlen(progress_name), 0)); - lj.build_dep_prog_node = g->sub_progress_node; - } - - - // even though we're calling LLD as a library it thinks the first - // argument is its own exe name - lj.args.append("lld"); - - lj.rpath_table.init(4); - lj.codegen = g; - - if (g->out_type == OutTypeObj) { - lj.args.append("-r"); - } - - if (g->out_type == OutTypeLib && !g->is_dynamic && !target_is_wasm(g->zig_target)) { - ZigList file_names = {}; - for (size_t i = 0; i < g->link_objects.length; i += 1) { - file_names.append(buf_ptr(g->link_objects.at(i))); - } - ZigLLVM_OSType os_type = get_llvm_os_type(g->zig_target->os); - codegen_add_time_event(g, "LLVM Link"); - { - const char *progress_name = "Link"; - codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node, - progress_name, strlen(progress_name), 0)); - } - if (g->verbose_link) { - fprintf(stderr, "ar rcs %s", buf_ptr(&g->bin_file_output_path)); - for (size_t i = 0; i < file_names.length; i += 1) { - fprintf(stderr, " %s", file_names.at(i)); - } - fprintf(stderr, "\n"); - } - if (ZigLLVMWriteArchive(buf_ptr(&g->bin_file_output_path), file_names.items, file_names.length, os_type)) { - fprintf(stderr, "Unable to write archive '%s'\n", buf_ptr(&g->bin_file_output_path)); - exit(1); - } - return; - } - - lj.link_in_crt = (g->libc_link_lib != nullptr && g->out_type == OutTypeExe); - - construct_linker_job(&lj); - - if (g->verbose_link) { - for (size_t i = 0; i < lj.args.length; i += 1) { - const char *space = (i != 0) ? " " : ""; - fprintf(stderr, "%s%s", space, lj.args.at(i)); - } - fprintf(stderr, "\n"); - } - - Buf diag = BUF_INIT; - - codegen_add_time_event(g, "LLVM Link"); - { - const char *progress_name = "Link"; - codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node, - progress_name, strlen(progress_name), 0)); - } - if (g->system_linker_hack && g->zig_target->os == OsMacOSX) { - Termination term; - ZigList args = {}; - args.append("ld"); - for (size_t i = 1; i < lj.args.length; i += 1) { - args.append(lj.args.at(i)); - } - os_spawn_process(args, &term); - if (term.how != TerminationIdClean || term.code != 0) { - exit(1); - } - } else if (!zig_lld_link(target_object_format(g->zig_target), lj.args.items, lj.args.length, &diag)) { - fprintf(stderr, "%s\n", buf_ptr(&diag)); - exit(1); - } -} - diff --git a/src/link.zig b/src/link.zig new file mode 100644 index 0000000000000000000000000000000000000000..4d28c8a1a797c746588e9be879b90543e6f36b1a --- /dev/null +++ b/src/link.zig @@ -0,0 +1,549 @@ +const std = @import("std"); +const mem = std.mem; +const Allocator = std.mem.Allocator; +const fs = std.fs; +const log = std.log.scoped(.link); +const assert = std.debug.assert; + +const Compilation = @import("Compilation.zig"); +const Module = @import("Module.zig"); +const trace = @import("tracy.zig").trace; +const Package = @import("Package.zig"); +const Type = @import("type.zig").Type; +const Cache = @import("Cache.zig"); +const build_options = @import("build_options"); +const LibCInstallation = @import("libc_installation.zig").LibCInstallation; + +pub const producer_string = if (std.builtin.is_test) "zig test" else "zig " ++ build_options.version; + +pub const Emit = struct { + /// Where the output will go. + directory: Compilation.Directory, + /// Path to the output file, relative to `directory`. + sub_path: []const u8, +}; + +pub const Options = struct { + /// This is `null` when -fno-emit-bin is used. When `openPath` or `flush` is called, + /// it will have already been null-checked. + emit: ?Emit, + target: std.Target, + output_mode: std.builtin.OutputMode, + link_mode: std.builtin.LinkMode, + object_format: std.builtin.ObjectFormat, + optimize_mode: std.builtin.Mode, + machine_code_model: std.builtin.CodeModel, + root_name: []const u8, + /// Not every Compilation compiles .zig code! For example you could do `zig build-exe foo.o`. + module: ?*Module, + dynamic_linker: ?[]const u8, + /// Used for calculating how much space to reserve for symbols in case the binary file + /// does not already have a symbol table. + symbol_count_hint: u64 = 32, + /// Used for calculating how much space to reserve for executable program code in case + /// the binary file does not already have such a section. + program_code_size_hint: u64 = 256 * 1024, + entry_addr: ?u64 = null, + stack_size_override: ?u64, + /// Set to `true` to omit debug info. + strip: bool, + /// If this is true then this link code is responsible for outputting an object + /// file and then using LLD to link it together with the link options and other objects. + /// Otherwise (depending on `use_llvm`) this link code directly outputs and updates the final binary. + use_lld: bool, + /// If this is true then this link code is responsible for making an LLVM IR Module, + /// outputting it to an object file, and then linking that together with link options and + /// other objects. + /// Otherwise (depending on `use_lld`) this link code directly outputs and updates the final binary. + use_llvm: bool, + link_libc: bool, + link_libcpp: bool, + function_sections: bool, + eh_frame_hdr: bool, + rdynamic: bool, + z_nodelete: bool, + z_defs: bool, + bind_global_refs_locally: bool, + is_native_os: bool, + pic: bool, + valgrind: bool, + stack_check: bool, + single_threaded: bool, + verbose_link: bool, + dll_export_fns: bool, + error_return_tracing: bool, + is_compiler_rt_or_libc: bool, + parent_compilation_link_libc: bool, + each_lib_rpath: bool, + disable_lld_caching: bool, + is_test: bool, + gc_sections: ?bool = null, + allow_shlib_undefined: ?bool, + subsystem: ?std.Target.SubSystem, + linker_script: ?[]const u8, + version_script: ?[]const u8, + override_soname: ?[]const u8, + llvm_cpu_features: ?[*:0]const u8, + /// Extra args passed directly to LLD. Ignored when not linking with LLD. + extra_lld_args: []const []const u8, + + objects: []const []const u8, + framework_dirs: []const []const u8, + frameworks: []const []const u8, + system_libs: std.StringArrayHashMapUnmanaged(void), + lib_dirs: []const []const u8, + rpath_list: []const []const u8, + + version: ?std.builtin.Version, + libc_installation: ?*const LibCInstallation, + + pub fn effectiveOutputMode(options: Options) std.builtin.OutputMode { + return if (options.use_lld) .Obj else options.output_mode; + } +}; + +pub const File = struct { + tag: Tag, + options: Options, + file: ?fs.File, + allocator: *Allocator, + /// When linking with LLD, this linker code will output an object file only at + /// this location, and then this path can be placed on the LLD linker line. + intermediary_basename: ?[]const u8 = null, + + /// Prevents other processes from clobbering files in the output directory + /// of this linking operation. + lock: ?Cache.Lock = null, + + pub const LinkBlock = union { + elf: Elf.TextBlock, + coff: Coff.TextBlock, + macho: MachO.TextBlock, + c: void, + wasm: void, + }; + + pub const LinkFn = union { + elf: Elf.SrcFn, + coff: Coff.SrcFn, + macho: MachO.SrcFn, + c: void, + wasm: ?Wasm.FnData, + }; + + /// For DWARF .debug_info. + pub const DbgInfoTypeRelocsTable = std.HashMapUnmanaged(Type, DbgInfoTypeReloc, Type.hash, Type.eql, std.hash_map.DefaultMaxLoadPercentage); + + /// For DWARF .debug_info. + pub const DbgInfoTypeReloc = struct { + /// Offset from `TextBlock.dbg_info_off` (the buffer that is local to a Decl). + /// This is where the .debug_info tag for the type is. + off: u32, + /// Offset from `TextBlock.dbg_info_off` (the buffer that is local to a Decl). + /// List of DW.AT_type / DW.FORM_ref4 that points to the type. + relocs: std.ArrayListUnmanaged(u32), + }; + + /// Attempts incremental linking, if the file already exists. If + /// incremental linking fails, falls back to truncating the file and + /// rewriting it. A malicious file is detected as incremental link failure + /// and does not cause Illegal Behavior. This operation is not atomic. + pub fn openPath(allocator: *Allocator, options: Options) !*File { + const use_stage1 = build_options.is_stage1 and options.use_llvm; + if (use_stage1 or options.emit == null) { + return switch (options.object_format) { + .coff, .pe => &(try Coff.createEmpty(allocator, options)).base, + .elf => &(try Elf.createEmpty(allocator, options)).base, + .macho => &(try MachO.createEmpty(allocator, options)).base, + .wasm => &(try Wasm.createEmpty(allocator, options)).base, + .c => unreachable, // Reported error earlier. + .hex => return error.HexObjectFormatUnimplemented, + .raw => return error.RawObjectFormatUnimplemented, + }; + } + const emit = options.emit.?; + const use_lld = build_options.have_llvm and options.use_lld; // comptime known false when !have_llvm + const sub_path = if (use_lld) blk: { + if (options.module == null) { + // No point in opening a file, we would not write anything to it. Initialize with empty. + return switch (options.object_format) { + .coff, .pe => &(try Coff.createEmpty(allocator, options)).base, + .elf => &(try Elf.createEmpty(allocator, options)).base, + .macho => &(try MachO.createEmpty(allocator, options)).base, + .wasm => &(try Wasm.createEmpty(allocator, options)).base, + .c => unreachable, // Reported error earlier. + .hex => return error.HexObjectFormatUnimplemented, + .raw => return error.RawObjectFormatUnimplemented, + }; + } + // Open a temporary object file, not the final output file because we want to link with LLD. + break :blk try std.fmt.allocPrint(allocator, "{s}{s}", .{ emit.sub_path, options.target.oFileExt() }); + } else emit.sub_path; + errdefer if (use_lld) allocator.free(sub_path); + + const file: *File = switch (options.object_format) { + .coff, .pe => &(try Coff.openPath(allocator, sub_path, options)).base, + .elf => &(try Elf.openPath(allocator, sub_path, options)).base, + .macho => &(try MachO.openPath(allocator, sub_path, options)).base, + .wasm => &(try Wasm.openPath(allocator, sub_path, options)).base, + .c => &(try C.openPath(allocator, sub_path, options)).base, + .hex => return error.HexObjectFormatUnimplemented, + .raw => return error.RawObjectFormatUnimplemented, + }; + + if (use_lld) { + file.intermediary_basename = sub_path; + } + + return file; + } + + pub fn cast(base: *File, comptime T: type) ?*T { + if (base.tag != T.base_tag) + return null; + + return @fieldParentPtr(T, "base", base); + } + + pub fn makeWritable(base: *File) !void { + switch (base.tag) { + .coff, .elf, .macho => { + if (base.file != null) return; + const emit = base.options.emit orelse return; + base.file = try emit.directory.handle.createFile(emit.sub_path, .{ + .truncate = false, + .read = true, + .mode = determineMode(base.options), + }); + }, + .c, .wasm => {}, + } + } + + pub fn makeExecutable(base: *File) !void { + switch (base.tag) { + .coff, .elf, .macho => if (base.file) |f| { + if (base.intermediary_basename != null) { + // The file we have open is not the final file that we want to + // make executable, so we don't have to close it. + return; + } + f.close(); + base.file = null; + }, + .c, .wasm => {}, + } + } + + /// May be called before or after updateDeclExports but must be called + /// after allocateDeclIndexes for any given Decl. + pub fn updateDecl(base: *File, module: *Module, decl: *Module.Decl) !void { + switch (base.tag) { + .coff => return @fieldParentPtr(Coff, "base", base).updateDecl(module, decl), + .elf => return @fieldParentPtr(Elf, "base", base).updateDecl(module, decl), + .macho => return @fieldParentPtr(MachO, "base", base).updateDecl(module, decl), + .c => return @fieldParentPtr(C, "base", base).updateDecl(module, decl), + .wasm => return @fieldParentPtr(Wasm, "base", base).updateDecl(module, decl), + } + } + + pub fn updateDeclLineNumber(base: *File, module: *Module, decl: *Module.Decl) !void { + switch (base.tag) { + .coff => return @fieldParentPtr(Coff, "base", base).updateDeclLineNumber(module, decl), + .elf => return @fieldParentPtr(Elf, "base", base).updateDeclLineNumber(module, decl), + .macho => return @fieldParentPtr(MachO, "base", base).updateDeclLineNumber(module, decl), + .c, .wasm => {}, + } + } + + /// Must be called before any call to updateDecl or updateDeclExports for + /// any given Decl. + pub fn allocateDeclIndexes(base: *File, decl: *Module.Decl) !void { + switch (base.tag) { + .coff => return @fieldParentPtr(Coff, "base", base).allocateDeclIndexes(decl), + .elf => return @fieldParentPtr(Elf, "base", base).allocateDeclIndexes(decl), + .macho => return @fieldParentPtr(MachO, "base", base).allocateDeclIndexes(decl), + .c, .wasm => {}, + } + } + + pub fn releaseLock(self: *File) void { + if (self.lock) |*lock| { + lock.release(); + self.lock = null; + } + } + + pub fn toOwnedLock(self: *File) Cache.Lock { + const lock = self.lock.?; + self.lock = null; + return lock; + } + + pub fn destroy(base: *File) void { + base.releaseLock(); + if (base.file) |f| f.close(); + if (base.intermediary_basename) |sub_path| base.allocator.free(sub_path); + switch (base.tag) { + .coff => { + const parent = @fieldParentPtr(Coff, "base", base); + parent.deinit(); + base.allocator.destroy(parent); + }, + .elf => { + const parent = @fieldParentPtr(Elf, "base", base); + parent.deinit(); + base.allocator.destroy(parent); + }, + .macho => { + const parent = @fieldParentPtr(MachO, "base", base); + parent.deinit(); + base.allocator.destroy(parent); + }, + .c => { + const parent = @fieldParentPtr(C, "base", base); + parent.deinit(); + base.allocator.destroy(parent); + }, + .wasm => { + const parent = @fieldParentPtr(Wasm, "base", base); + parent.deinit(); + base.allocator.destroy(parent); + }, + } + } + + /// Commit pending changes and write headers. Takes into account final output mode + /// and `use_lld`, not only `effectiveOutputMode`. + pub fn flush(base: *File, comp: *Compilation) !void { + const emit = base.options.emit orelse return; // -fno-emit-bin + + if (comp.clang_preprocessor_mode == .yes) { + // TODO: avoid extra link step when it's just 1 object file (the `zig cc -c` case) + // Until then, we do `lld -r -o output.o input.o` even though the output is the same + // as the input. For the preprocessing case (`zig cc -E -o foo`) we copy the file + // to the final location. See also the corresponding TODO in Coff linking. + const full_out_path = try emit.directory.join(comp.gpa, &[_][]const u8{emit.sub_path}); + defer comp.gpa.free(full_out_path); + assert(comp.c_object_table.count() == 1); + const the_entry = comp.c_object_table.items()[0]; + const cached_pp_file_path = the_entry.key.status.success.object_path; + try fs.cwd().copyFile(cached_pp_file_path, fs.cwd(), full_out_path, .{}); + return; + } + const use_lld = build_options.have_llvm and base.options.use_lld; + if (use_lld and base.options.output_mode == .Lib and base.options.link_mode == .Static and + !base.options.target.isWasm()) + { + return base.linkAsArchive(comp); + } + switch (base.tag) { + .coff => return @fieldParentPtr(Coff, "base", base).flush(comp), + .elf => return @fieldParentPtr(Elf, "base", base).flush(comp), + .macho => return @fieldParentPtr(MachO, "base", base).flush(comp), + .c => return @fieldParentPtr(C, "base", base).flush(comp), + .wasm => return @fieldParentPtr(Wasm, "base", base).flush(comp), + } + } + + /// Commit pending changes and write headers. Works based on `effectiveOutputMode` + /// rather than final output mode. + pub fn flushModule(base: *File, comp: *Compilation) !void { + switch (base.tag) { + .coff => return @fieldParentPtr(Coff, "base", base).flushModule(comp), + .elf => return @fieldParentPtr(Elf, "base", base).flushModule(comp), + .macho => return @fieldParentPtr(MachO, "base", base).flushModule(comp), + .c => return @fieldParentPtr(C, "base", base).flushModule(comp), + .wasm => return @fieldParentPtr(Wasm, "base", base).flushModule(comp), + } + } + + pub fn freeDecl(base: *File, decl: *Module.Decl) void { + switch (base.tag) { + .coff => @fieldParentPtr(Coff, "base", base).freeDecl(decl), + .elf => @fieldParentPtr(Elf, "base", base).freeDecl(decl), + .macho => @fieldParentPtr(MachO, "base", base).freeDecl(decl), + .c => unreachable, + .wasm => @fieldParentPtr(Wasm, "base", base).freeDecl(decl), + } + } + + pub fn errorFlags(base: *File) ErrorFlags { + switch (base.tag) { + .coff => return @fieldParentPtr(Coff, "base", base).error_flags, + .elf => return @fieldParentPtr(Elf, "base", base).error_flags, + .macho => return @fieldParentPtr(MachO, "base", base).error_flags, + .c => return .{ .no_entry_point_found = false }, + .wasm => return ErrorFlags{}, + } + } + + /// May be called before or after updateDecl, but must be called after + /// allocateDeclIndexes for any given Decl. + pub fn updateDeclExports( + base: *File, + module: *Module, + decl: *const Module.Decl, + exports: []const *Module.Export, + ) !void { + switch (base.tag) { + .coff => return @fieldParentPtr(Coff, "base", base).updateDeclExports(module, decl, exports), + .elf => return @fieldParentPtr(Elf, "base", base).updateDeclExports(module, decl, exports), + .macho => return @fieldParentPtr(MachO, "base", base).updateDeclExports(module, decl, exports), + .c => return {}, + .wasm => return @fieldParentPtr(Wasm, "base", base).updateDeclExports(module, decl, exports), + } + } + + pub fn getDeclVAddr(base: *File, decl: *const Module.Decl) u64 { + switch (base.tag) { + .coff => return @fieldParentPtr(Coff, "base", base).getDeclVAddr(decl), + .elf => return @fieldParentPtr(Elf, "base", base).getDeclVAddr(decl), + .macho => return @fieldParentPtr(MachO, "base", base).getDeclVAddr(decl), + .c => unreachable, + .wasm => unreachable, + } + } + + fn linkAsArchive(base: *File, comp: *Compilation) !void { + const tracy = trace(@src()); + defer tracy.end(); + + var arena_allocator = std.heap.ArenaAllocator.init(base.allocator); + defer arena_allocator.deinit(); + const arena = &arena_allocator.allocator; + + const directory = base.options.emit.?.directory; // Just an alias to make it shorter to type. + + // If there is no Zig code to compile, then we should skip flushing the output file because it + // will not be part of the linker line anyway. + const module_obj_path: ?[]const u8 = if (base.options.module) |module| blk: { + const use_stage1 = build_options.is_stage1 and base.options.use_llvm; + if (use_stage1) { + const obj_basename = try std.zig.binNameAlloc(arena, .{ + .root_name = base.options.root_name, + .target = base.options.target, + .output_mode = .Obj, + }); + const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename}); + break :blk full_obj_path; + } + try base.flushModule(comp); + const obj_basename = base.intermediary_basename.?; + const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename}); + break :blk full_obj_path; + } else null; + + // This function follows the same pattern as link.Elf.linkWithLLD so if you want some + // insight as to what's going on here you can read that function body which is more + // well-commented. + + const id_symlink_basename = "llvm-ar.id"; + + base.releaseLock(); + + var ch = comp.cache_parent.obtain(); + defer ch.deinit(); + + try ch.addListOfFiles(base.options.objects); + for (comp.c_object_table.items()) |entry| { + _ = try ch.addFile(entry.key.status.success.object_path, null); + } + try ch.addOptionalFile(module_obj_path); + + // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. + _ = try ch.hit(); + const digest = ch.final(); + + var prev_digest_buf: [digest.len]u8 = undefined; + const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch |err| b: { + log.debug("archive new_digest={} readlink error: {}", .{ digest, @errorName(err) }); + break :b prev_digest_buf[0..0]; + }; + if (mem.eql(u8, prev_digest, &digest)) { + log.debug("archive digest={} match - skipping invocation", .{digest}); + base.lock = ch.toOwnedLock(); + return; + } + + // We are about to change the output file to be different, so we invalidate the build hash now. + directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { + error.FileNotFound => {}, + else => |e| return e, + }; + + var object_files = std.ArrayList([*:0]const u8).init(base.allocator); + defer object_files.deinit(); + + try object_files.ensureCapacity(base.options.objects.len + comp.c_object_table.items().len + 1); + for (base.options.objects) |obj_path| { + object_files.appendAssumeCapacity(try arena.dupeZ(u8, obj_path)); + } + for (comp.c_object_table.items()) |entry| { + object_files.appendAssumeCapacity(try arena.dupeZ(u8, entry.key.status.success.object_path)); + } + if (module_obj_path) |p| { + object_files.appendAssumeCapacity(try arena.dupeZ(u8, p)); + } + + const full_out_path = try directory.join(arena, &[_][]const u8{base.options.emit.?.sub_path}); + const full_out_path_z = try arena.dupeZ(u8, full_out_path); + + if (base.options.verbose_link) { + std.debug.print("ar rcs {}", .{full_out_path_z}); + for (object_files.items) |arg| { + std.debug.print(" {}", .{arg}); + } + std.debug.print("\n", .{}); + } + + const llvm = @import("llvm.zig"); + const os_type = @import("target.zig").osToLLVM(base.options.target.os.tag); + const bad = llvm.WriteArchive(full_out_path_z, object_files.items.ptr, object_files.items.len, os_type); + if (bad) return error.UnableToWriteArchive; + + directory.handle.symLink(&digest, id_symlink_basename, .{}) catch |err| { + std.log.warn("failed to save archive hash digest symlink: {}", .{@errorName(err)}); + }; + + ch.writeManifest() catch |err| { + std.log.warn("failed to write cache manifest when archiving: {}", .{@errorName(err)}); + }; + + base.lock = ch.toOwnedLock(); + } + + pub const Tag = enum { + coff, + elf, + macho, + c, + wasm, + }; + + pub const ErrorFlags = struct { + no_entry_point_found: bool = false, + }; + + pub const C = @import("link/C.zig"); + pub const Coff = @import("link/Coff.zig"); + pub const Elf = @import("link/Elf.zig"); + pub const MachO = @import("link/MachO.zig"); + pub const Wasm = @import("link/Wasm.zig"); +}; + +pub fn determineMode(options: Options) fs.File.Mode { + // On common systems with a 0o022 umask, 0o777 will still result in a file created + // with 0o755 permissions, but it works appropriately if the system is configured + // more leniently. As another data point, C's fopen seems to open files with the + // 666 mode. + const executable_mode = if (std.Target.current.os.tag == .windows) 0 else 0o777; + switch (options.effectiveOutputMode()) { + .Lib => return switch (options.link_mode) { + .Dynamic => executable_mode, + .Static => fs.File.default_mode, + }, + .Exe => return executable_mode, + .Obj => return fs.File.default_mode, + } +} diff --git a/src/link/C.zig b/src/link/C.zig new file mode 100644 index 0000000000000000000000000000000000000000..467e10998ed0fa83a1a68501b71dbb3eb82f5715 --- /dev/null +++ b/src/link/C.zig @@ -0,0 +1,113 @@ +const std = @import("std"); +const mem = std.mem; +const assert = std.debug.assert; +const Allocator = std.mem.Allocator; +const Module = @import("../Module.zig"); +const Compilation = @import("../Compilation.zig"); +const fs = std.fs; +const codegen = @import("../codegen/c.zig"); +const link = @import("../link.zig"); +const trace = @import("../tracy.zig").trace; +const File = link.File; +const C = @This(); + +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: *Compilation.ErrorMsg = undefined, + +pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*C { + assert(options.object_format == .c); + + if (options.use_llvm) return error.LLVMHasNoCBackend; + if (options.use_lld) return error.LLDHasNoCBackend; + + const file = try options.emit.?.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true, .mode = link.determineMode(options) }); + errdefer file.close(); + + var c_file = try allocator.create(C); + 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; +} + +pub fn fail(self: *C, src: usize, comptime format: []const u8, args: anytype) error{ AnalysisFail, OutOfMemory } { + self.error_msg = try Compilation.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, comp: *Compilation) !void { + return self.flushModule(comp); +} + +pub fn flushModule(self: *C, comp: *Compilation) !void { + const tracy = trace(@src()); + defer tracy.end(); + + const writer = self.base.file.?.writer(); + try writer.writeAll(@embedFile("cbe.h")); + var includes = false; + 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/link/Coff.zig b/src/link/Coff.zig new file mode 100644 index 0000000000000000000000000000000000000000..3f462582e7e9b691155cd5f33a10ac8429cba2da --- /dev/null +++ b/src/link/Coff.zig @@ -0,0 +1,1220 @@ +const Coff = @This(); + +const std = @import("std"); +const log = std.log.scoped(.link); +const Allocator = std.mem.Allocator; +const assert = std.debug.assert; +const fs = std.fs; +const allocPrint = std.fmt.allocPrint; +const mem = std.mem; + +const trace = @import("../tracy.zig").trace; +const Module = @import("../Module.zig"); +const Compilation = @import("../Compilation.zig"); +const codegen = @import("../codegen.zig"); +const link = @import("../link.zig"); +const build_options = @import("build_options"); +const Cache = @import("../Cache.zig"); +const mingw = @import("../mingw.zig"); + +const allocation_padding = 4 / 3; +const minimum_text_block_size = 64 * allocation_padding; + +const section_alignment = 4096; +const file_alignment = 512; +const image_base = 0x400_000; +const section_table_size = 2 * 40; +comptime { + assert(mem.isAligned(image_base, section_alignment)); +} + +pub const base_tag: link.File.Tag = .coff; + +const msdos_stub = @embedFile("msdos-stub.bin"); + +base: link.File, +ptr_width: PtrWidth, +error_flags: link.File.ErrorFlags = .{}, + +text_block_free_list: std.ArrayListUnmanaged(*TextBlock) = .{}, +last_text_block: ?*TextBlock = null, + +/// Section table file pointer. +section_table_offset: u32 = 0, +/// Section data file pointer. +section_data_offset: u32 = 0, +/// Optiona header file pointer. +optional_header_offset: u32 = 0, + +/// Absolute virtual address of the offset table when the executable is loaded in memory. +offset_table_virtual_address: u32 = 0, +/// Current size of the offset table on disk, must be a multiple of `file_alignment` +offset_table_size: u32 = 0, +/// Contains absolute virtual addresses +offset_table: std.ArrayListUnmanaged(u64) = .{}, +/// Free list of offset table indices +offset_table_free_list: std.ArrayListUnmanaged(u32) = .{}, + +/// Virtual address of the entry point procedure relative to `image_base` +entry_addr: ?u32 = null, + +/// Absolute virtual address of the text section when the executable is loaded in memory. +text_section_virtual_address: u32 = 0, +/// Current size of the `.text` section on disk, must be a multiple of `file_alignment` +text_section_size: u32 = 0, + +offset_table_size_dirty: bool = false, +text_section_size_dirty: bool = false, +/// This flag is set when the virtual size of the whole image file when loaded in memory has changed +/// and needs to be updated in the optional header. +size_of_image_dirty: bool = false, + +pub const PtrWidth = enum { p32, p64 }; + +pub const TextBlock = struct { + /// Offset of the code relative to the start of the text section + text_offset: u32, + /// Used size of the text block + size: u32, + /// This field is undefined for symbols with size = 0. + offset_table_index: u32, + /// Points to the previous and next neighbors, based on the `text_offset`. + /// This can be used to find, for example, the capacity of this `TextBlock`. + prev: ?*TextBlock, + next: ?*TextBlock, + + pub const empty = TextBlock{ + .text_offset = 0, + .size = 0, + .offset_table_index = undefined, + .prev = null, + .next = null, + }; + + /// Returns how much room there is to grow in virtual address space. + fn capacity(self: TextBlock) u64 { + if (self.next) |next| { + return next.text_offset - self.text_offset; + } + // This is the last block, the capacity is only limited by the address space. + return std.math.maxInt(u32) - self.text_offset; + } + + fn freeListEligible(self: TextBlock) bool { + // No need to keep a free list node for the last block. + const next = self.next orelse return false; + const cap = next.text_offset - self.text_offset; + const ideal_cap = self.size * allocation_padding; + if (cap <= ideal_cap) return false; + const surplus = cap - ideal_cap; + return surplus >= minimum_text_block_size; + } + + /// Absolute virtual address of the text block when the file is loaded in memory. + fn getVAddr(self: TextBlock, coff: Coff) u32 { + return coff.text_section_virtual_address + self.text_offset; + } +}; + +pub const SrcFn = void; + +pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Coff { + assert(options.object_format == .coff); + + if (options.use_llvm) return error.LLVM_BackendIsTODO_ForCoff; // TODO + if (options.use_lld) return error.LLD_LinkingIsTODO_ForCoff; // TODO + + const file = try options.emit.?.directory.handle.createFile(sub_path, .{ + .truncate = false, + .read = true, + .mode = link.determineMode(options), + }); + errdefer file.close(); + + const self = try createEmpty(allocator, options); + errdefer self.base.destroy(); + + self.base.file = file; + + // TODO Write object specific relocations, COFF symbol table, then enable object file output. + switch (options.output_mode) { + .Exe => {}, + .Obj => return error.TODOImplementWritingObjFiles, + .Lib => return error.TODOImplementWritingLibFiles, + } + + var coff_file_header_offset: u32 = 0; + if (options.output_mode == .Exe) { + // Write the MS-DOS stub and the PE signature + try self.base.file.?.pwriteAll(msdos_stub ++ "PE\x00\x00", 0); + coff_file_header_offset = msdos_stub.len + 4; + } + + // COFF file header + const data_directory_count = 0; + var hdr_data: [112 + data_directory_count * 8 + section_table_size]u8 = undefined; + var index: usize = 0; + + const machine = self.base.options.target.cpu.arch.toCoffMachine(); + if (machine == .Unknown) { + return error.UnsupportedCOFFArchitecture; + } + mem.writeIntLittle(u16, hdr_data[0..2], @enumToInt(machine)); + index += 2; + + // Number of sections (we only use .got, .text) + mem.writeIntLittle(u16, hdr_data[index..][0..2], 2); + index += 2; + // TimeDateStamp (u32), PointerToSymbolTable (u32), NumberOfSymbols (u32) + mem.set(u8, hdr_data[index..][0..12], 0); + index += 12; + + const optional_header_size = switch (options.output_mode) { + .Exe => data_directory_count * 8 + switch (self.ptr_width) { + .p32 => @as(u16, 96), + .p64 => 112, + }, + else => 0, + }; + + const section_table_offset = coff_file_header_offset + 20 + optional_header_size; + const default_offset_table_size = file_alignment; + const default_size_of_code = 0; + + self.section_data_offset = mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, file_alignment); + const section_data_relative_virtual_address = mem.alignForwardGeneric(u32, self.section_table_offset + section_table_size, section_alignment); + self.offset_table_virtual_address = image_base + section_data_relative_virtual_address; + self.offset_table_size = default_offset_table_size; + self.section_table_offset = section_table_offset; + self.text_section_virtual_address = image_base + section_data_relative_virtual_address + section_alignment; + self.text_section_size = default_size_of_code; + + // Size of file when loaded in memory + const size_of_image = mem.alignForwardGeneric(u32, self.text_section_virtual_address - image_base + default_size_of_code, section_alignment); + + mem.writeIntLittle(u16, hdr_data[index..][0..2], optional_header_size); + index += 2; + + // Characteristics + var characteristics: u16 = std.coff.IMAGE_FILE_DEBUG_STRIPPED | std.coff.IMAGE_FILE_RELOCS_STRIPPED; // TODO Remove debug info stripped flag when necessary + if (options.output_mode == .Exe) { + characteristics |= std.coff.IMAGE_FILE_EXECUTABLE_IMAGE; + } + switch (self.ptr_width) { + .p32 => characteristics |= std.coff.IMAGE_FILE_32BIT_MACHINE, + .p64 => characteristics |= std.coff.IMAGE_FILE_LARGE_ADDRESS_AWARE, + } + mem.writeIntLittle(u16, hdr_data[index..][0..2], characteristics); + index += 2; + + assert(index == 20); + try self.base.file.?.pwriteAll(hdr_data[0..index], coff_file_header_offset); + + if (options.output_mode == .Exe) { + self.optional_header_offset = coff_file_header_offset + 20; + // Optional header + index = 0; + mem.writeIntLittle(u16, hdr_data[0..2], switch (self.ptr_width) { + .p32 => @as(u16, 0x10b), + .p64 => 0x20b, + }); + index += 2; + + // Linker version (u8 + u8) + mem.set(u8, hdr_data[index..][0..2], 0); + index += 2; + + // SizeOfCode (UNUSED, u32), SizeOfInitializedData (u32), SizeOfUninitializedData (u32), AddressOfEntryPoint (u32), BaseOfCode (UNUSED, u32) + mem.set(u8, hdr_data[index..][0..20], 0); + index += 20; + + if (self.ptr_width == .p32) { + // Base of data relative to the image base (UNUSED) + mem.set(u8, hdr_data[index..][0..4], 0); + index += 4; + + // Image base address + mem.writeIntLittle(u32, hdr_data[index..][0..4], image_base); + index += 4; + } else { + // Image base address + mem.writeIntLittle(u64, hdr_data[index..][0..8], image_base); + index += 8; + } + + // Section alignment + mem.writeIntLittle(u32, hdr_data[index..][0..4], section_alignment); + index += 4; + // File alignment + mem.writeIntLittle(u32, hdr_data[index..][0..4], file_alignment); + index += 4; + // Required OS version, 6.0 is vista + mem.writeIntLittle(u16, hdr_data[index..][0..2], 6); + index += 2; + mem.writeIntLittle(u16, hdr_data[index..][0..2], 0); + index += 2; + // Image version + mem.set(u8, hdr_data[index..][0..4], 0); + index += 4; + // Required subsystem version, same as OS version + mem.writeIntLittle(u16, hdr_data[index..][0..2], 6); + index += 2; + mem.writeIntLittle(u16, hdr_data[index..][0..2], 0); + index += 2; + // Reserved zeroes (u32) + mem.set(u8, hdr_data[index..][0..4], 0); + index += 4; + mem.writeIntLittle(u32, hdr_data[index..][0..4], size_of_image); + index += 4; + mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset); + index += 4; + // CheckSum (u32) + mem.set(u8, hdr_data[index..][0..4], 0); + index += 4; + // Subsystem, TODO: Let users specify the subsystem, always CUI for now + mem.writeIntLittle(u16, hdr_data[index..][0..2], 3); + index += 2; + // DLL characteristics + mem.writeIntLittle(u16, hdr_data[index..][0..2], 0x0); + index += 2; + + switch (self.ptr_width) { + .p32 => { + // Size of stack reserve + commit + mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000_000); + index += 4; + mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000); + index += 4; + // Size of heap reserve + commit + mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x100_000); + index += 4; + mem.writeIntLittle(u32, hdr_data[index..][0..4], 0x1_000); + index += 4; + }, + .p64 => { + // Size of stack reserve + commit + mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000_000); + index += 8; + mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000); + index += 8; + // Size of heap reserve + commit + mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x100_000); + index += 8; + mem.writeIntLittle(u64, hdr_data[index..][0..8], 0x1_000); + index += 8; + }, + } + + // Reserved zeroes + mem.set(u8, hdr_data[index..][0..4], 0); + index += 4; + + // Number of data directories + mem.writeIntLittle(u32, hdr_data[index..][0..4], data_directory_count); + index += 4; + // Initialize data directories to zero + mem.set(u8, hdr_data[index..][0 .. data_directory_count * 8], 0); + index += data_directory_count * 8; + + assert(index == optional_header_size); + } + + // Write section table. + // First, the .got section + hdr_data[index..][0..8].* = ".got\x00\x00\x00\x00".*; + index += 8; + if (options.output_mode == .Exe) { + // Virtual size (u32) + mem.writeIntLittle(u32, hdr_data[index..][0..4], default_offset_table_size); + index += 4; + // Virtual address (u32) + mem.writeIntLittle(u32, hdr_data[index..][0..4], self.offset_table_virtual_address - image_base); + index += 4; + } else { + mem.set(u8, hdr_data[index..][0..8], 0); + index += 8; + } + // Size of raw data (u32) + mem.writeIntLittle(u32, hdr_data[index..][0..4], default_offset_table_size); + index += 4; + // File pointer to the start of the section + mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset); + index += 4; + // Pointer to relocations (u32), PointerToLinenumbers (u32), NumberOfRelocations (u16), NumberOfLinenumbers (u16) + mem.set(u8, hdr_data[index..][0..12], 0); + index += 12; + // Section flags + mem.writeIntLittle(u32, hdr_data[index..][0..4], std.coff.IMAGE_SCN_CNT_INITIALIZED_DATA | std.coff.IMAGE_SCN_MEM_READ); + index += 4; + // Then, the .text section + hdr_data[index..][0..8].* = ".text\x00\x00\x00".*; + index += 8; + if (options.output_mode == .Exe) { + // Virtual size (u32) + mem.writeIntLittle(u32, hdr_data[index..][0..4], default_size_of_code); + index += 4; + // Virtual address (u32) + mem.writeIntLittle(u32, hdr_data[index..][0..4], self.text_section_virtual_address - image_base); + index += 4; + } else { + mem.set(u8, hdr_data[index..][0..8], 0); + index += 8; + } + // Size of raw data (u32) + mem.writeIntLittle(u32, hdr_data[index..][0..4], default_size_of_code); + index += 4; + // File pointer to the start of the section + mem.writeIntLittle(u32, hdr_data[index..][0..4], self.section_data_offset + default_offset_table_size); + index += 4; + // Pointer to relocations (u32), PointerToLinenumbers (u32), NumberOfRelocations (u16), NumberOfLinenumbers (u16) + mem.set(u8, hdr_data[index..][0..12], 0); + index += 12; + // Section flags + mem.writeIntLittle( + u32, + hdr_data[index..][0..4], + std.coff.IMAGE_SCN_CNT_CODE | std.coff.IMAGE_SCN_MEM_EXECUTE | std.coff.IMAGE_SCN_MEM_READ | std.coff.IMAGE_SCN_MEM_WRITE, + ); + index += 4; + + assert(index == optional_header_size + section_table_size); + try self.base.file.?.pwriteAll(hdr_data[0..index], self.optional_header_offset); + try self.base.file.?.setEndPos(self.section_data_offset + default_offset_table_size + default_size_of_code); + + return self; +} + +pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Coff { + const ptr_width: PtrWidth = switch (options.target.cpu.arch.ptrBitWidth()) { + 0...32 => .p32, + 33...64 => .p64, + else => return error.UnsupportedCOFFArchitecture, + }; + const self = try gpa.create(Coff); + self.* = .{ + .base = .{ + .tag = .coff, + .options = options, + .allocator = gpa, + .file = null, + }, + .ptr_width = ptr_width, + }; + return self; +} + +pub fn allocateDeclIndexes(self: *Coff, decl: *Module.Decl) !void { + try self.offset_table.ensureCapacity(self.base.allocator, self.offset_table.items.len + 1); + + if (self.offset_table_free_list.popOrNull()) |i| { + decl.link.coff.offset_table_index = i; + } else { + decl.link.coff.offset_table_index = @intCast(u32, self.offset_table.items.len); + _ = self.offset_table.addOneAssumeCapacity(); + + const entry_size = self.base.options.target.cpu.arch.ptrBitWidth() / 8; + if (self.offset_table.items.len > self.offset_table_size / entry_size) { + self.offset_table_size_dirty = true; + } + } + + self.offset_table.items[decl.link.coff.offset_table_index] = 0; +} + +fn allocateTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 { + const new_block_min_capacity = new_block_size * allocation_padding; + + // We use these to indicate our intention to update metadata, placing the new block, + // and possibly removing a free list node. + // It would be simpler to do it inside the for loop below, but that would cause a + // problem if an error was returned later in the function. So this action + // is actually carried out at the end of the function, when errors are no longer possible. + var block_placement: ?*TextBlock = null; + var free_list_removal: ?usize = null; + + const vaddr = blk: { + var i: usize = 0; + while (i < self.text_block_free_list.items.len) { + const free_block = self.text_block_free_list.items[i]; + + const next_block_text_offset = free_block.text_offset + free_block.capacity(); + const new_block_text_offset = mem.alignForwardGeneric(u64, free_block.getVAddr(self.*) + free_block.size, alignment) - self.text_section_virtual_address; + if (new_block_text_offset < next_block_text_offset and next_block_text_offset - new_block_text_offset >= new_block_min_capacity) { + block_placement = free_block; + + const remaining_capacity = next_block_text_offset - new_block_text_offset - new_block_min_capacity; + if (remaining_capacity < minimum_text_block_size) { + free_list_removal = i; + } + + break :blk new_block_text_offset + self.text_section_virtual_address; + } else { + if (!free_block.freeListEligible()) { + _ = self.text_block_free_list.swapRemove(i); + } else { + i += 1; + } + continue; + } + } else if (self.last_text_block) |last| { + const new_block_vaddr = mem.alignForwardGeneric(u64, last.getVAddr(self.*) + last.size, alignment); + block_placement = last; + break :blk new_block_vaddr; + } else { + break :blk self.text_section_virtual_address; + } + }; + + const expand_text_section = block_placement == null or block_placement.?.next == null; + if (expand_text_section) { + const needed_size = @intCast(u32, mem.alignForwardGeneric(u64, vaddr + new_block_size - self.text_section_virtual_address, file_alignment)); + if (needed_size > self.text_section_size) { + const current_text_section_virtual_size = mem.alignForwardGeneric(u32, self.text_section_size, section_alignment); + const new_text_section_virtual_size = mem.alignForwardGeneric(u32, needed_size, section_alignment); + if (current_text_section_virtual_size != new_text_section_virtual_size) { + self.size_of_image_dirty = true; + // Write new virtual size + var buf: [4]u8 = undefined; + mem.writeIntLittle(u32, &buf, new_text_section_virtual_size); + try self.base.file.?.pwriteAll(&buf, self.section_table_offset + 40 + 8); + } + + self.text_section_size = needed_size; + self.text_section_size_dirty = true; + } + self.last_text_block = text_block; + } + text_block.text_offset = @intCast(u32, vaddr - self.text_section_virtual_address); + text_block.size = @intCast(u32, new_block_size); + + // This function can also reallocate a text block. + // In this case we need to "unplug" it from its previous location before + // plugging it in to its new location. + if (text_block.prev) |prev| { + prev.next = text_block.next; + } + if (text_block.next) |next| { + next.prev = text_block.prev; + } + + if (block_placement) |big_block| { + text_block.prev = big_block; + text_block.next = big_block.next; + big_block.next = text_block; + } else { + text_block.prev = null; + text_block.next = null; + } + if (free_list_removal) |i| { + _ = self.text_block_free_list.swapRemove(i); + } + return vaddr; +} + +fn growTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64, alignment: u64) !u64 { + const block_vaddr = text_block.getVAddr(self.*); + const align_ok = mem.alignBackwardGeneric(u64, block_vaddr, alignment) == block_vaddr; + const need_realloc = !align_ok or new_block_size > text_block.capacity(); + if (!need_realloc) return @as(u64, block_vaddr); + return self.allocateTextBlock(text_block, new_block_size, alignment); +} + +fn shrinkTextBlock(self: *Coff, text_block: *TextBlock, new_block_size: u64) void { + text_block.size = @intCast(u32, new_block_size); + if (text_block.capacity() - text_block.size >= minimum_text_block_size) { + self.text_block_free_list.append(self.base.allocator, text_block) catch {}; + } +} + +fn freeTextBlock(self: *Coff, text_block: *TextBlock) void { + var already_have_free_list_node = false; + { + var i: usize = 0; + // TODO turn text_block_free_list into a hash map + while (i < self.text_block_free_list.items.len) { + if (self.text_block_free_list.items[i] == text_block) { + _ = self.text_block_free_list.swapRemove(i); + continue; + } + if (self.text_block_free_list.items[i] == text_block.prev) { + already_have_free_list_node = true; + } + i += 1; + } + } + if (self.last_text_block == text_block) { + self.last_text_block = text_block.prev; + } + if (text_block.prev) |prev| { + prev.next = text_block.next; + + if (!already_have_free_list_node and prev.freeListEligible()) { + // The free list is heuristics, it doesn't have to be perfect, so we can + // ignore the OOM here. + self.text_block_free_list.append(self.base.allocator, prev) catch {}; + } + } + + if (text_block.next) |next| { + next.prev = text_block.prev; + } +} + +fn writeOffsetTableEntry(self: *Coff, index: usize) !void { + const entry_size = self.base.options.target.cpu.arch.ptrBitWidth() / 8; + const endian = self.base.options.target.cpu.arch.endian(); + + const offset_table_start = self.section_data_offset; + if (self.offset_table_size_dirty) { + const current_raw_size = self.offset_table_size; + const new_raw_size = self.offset_table_size * 2; + log.debug("growing offset table from raw size {} to {}\n", .{ current_raw_size, new_raw_size }); + + // Move the text section to a new place in the executable + const current_text_section_start = self.section_data_offset + current_raw_size; + const new_text_section_start = self.section_data_offset + new_raw_size; + + const amt = try self.base.file.?.copyRangeAll(current_text_section_start, self.base.file.?, new_text_section_start, self.text_section_size); + if (amt != self.text_section_size) return error.InputOutput; + + // Write the new raw size in the .got header + var buf: [8]u8 = undefined; + mem.writeIntLittle(u32, buf[0..4], new_raw_size); + try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 16); + // Write the new .text section file offset in the .text section header + mem.writeIntLittle(u32, buf[0..4], new_text_section_start); + try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 40 + 20); + + const current_virtual_size = mem.alignForwardGeneric(u32, self.offset_table_size, section_alignment); + const new_virtual_size = mem.alignForwardGeneric(u32, new_raw_size, section_alignment); + // If we had to move in the virtual address space, we need to fix the VAs in the offset table, as well as the virtual address of the `.text` section + // and the virutal size of the `.got` section + + if (new_virtual_size != current_virtual_size) { + log.debug("growing offset table from virtual size {} to {}\n", .{ current_virtual_size, new_virtual_size }); + self.size_of_image_dirty = true; + const va_offset = new_virtual_size - current_virtual_size; + + // Write .got virtual size + mem.writeIntLittle(u32, buf[0..4], new_virtual_size); + try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 8); + + // Write .text new virtual address + self.text_section_virtual_address = self.text_section_virtual_address + va_offset; + mem.writeIntLittle(u32, buf[0..4], self.text_section_virtual_address - image_base); + try self.base.file.?.pwriteAll(buf[0..4], self.section_table_offset + 40 + 12); + + // Fix the VAs in the offset table + for (self.offset_table.items) |*va, idx| { + if (va.* != 0) { + va.* += va_offset; + + switch (entry_size) { + 4 => { + mem.writeInt(u32, buf[0..4], @intCast(u32, va.*), endian); + try self.base.file.?.pwriteAll(buf[0..4], offset_table_start + idx * entry_size); + }, + 8 => { + mem.writeInt(u64, &buf, va.*, endian); + try self.base.file.?.pwriteAll(&buf, offset_table_start + idx * entry_size); + }, + else => unreachable, + } + } + } + } + self.offset_table_size = new_raw_size; + self.offset_table_size_dirty = false; + } + // Write the new entry + switch (entry_size) { + 4 => { + var buf: [4]u8 = undefined; + mem.writeInt(u32, &buf, @intCast(u32, self.offset_table.items[index]), endian); + try self.base.file.?.pwriteAll(&buf, offset_table_start + index * entry_size); + }, + 8 => { + var buf: [8]u8 = undefined; + mem.writeInt(u64, &buf, self.offset_table.items[index], endian); + try self.base.file.?.pwriteAll(&buf, offset_table_start + index * entry_size); + }, + else => unreachable, + } +} + +pub fn updateDecl(self: *Coff, module: *Module, decl: *Module.Decl) !void { + // TODO COFF/PE debug information + // TODO Implement exports + const tracy = trace(@src()); + defer tracy.end(); + + var code_buffer = std.ArrayList(u8).init(self.base.allocator); + defer code_buffer.deinit(); + + const typed_value = decl.typed_value.most_recent.typed_value; + const res = try codegen.generateSymbol(&self.base, decl.src(), typed_value, &code_buffer, .none); + const code = switch (res) { + .externally_managed => |x| x, + .appended => code_buffer.items, + .fail => |em| { + decl.analysis = .codegen_failure; + try module.failed_decls.put(module.gpa, decl, em); + return; + }, + }; + + const required_alignment = typed_value.ty.abiAlignment(self.base.options.target); + const curr_size = decl.link.coff.size; + if (curr_size != 0) { + const capacity = decl.link.coff.capacity(); + const need_realloc = code.len > capacity or + !mem.isAlignedGeneric(u32, decl.link.coff.text_offset, required_alignment); + if (need_realloc) { + const curr_vaddr = self.getDeclVAddr(decl); + const vaddr = try self.growTextBlock(&decl.link.coff, code.len, required_alignment); + log.debug("growing {} from 0x{x} to 0x{x}\n", .{ decl.name, curr_vaddr, vaddr }); + if (vaddr != curr_vaddr) { + log.debug(" (writing new offset table entry)\n", .{}); + self.offset_table.items[decl.link.coff.offset_table_index] = vaddr; + try self.writeOffsetTableEntry(decl.link.coff.offset_table_index); + } + } else if (code.len < curr_size) { + self.shrinkTextBlock(&decl.link.coff, code.len); + } + } else { + const vaddr = try self.allocateTextBlock(&decl.link.coff, code.len, required_alignment); + log.debug("allocated text block for {} at 0x{x} (size: {Bi})\n", .{ mem.spanZ(decl.name), vaddr, code.len }); + errdefer self.freeTextBlock(&decl.link.coff); + self.offset_table.items[decl.link.coff.offset_table_index] = vaddr; + try self.writeOffsetTableEntry(decl.link.coff.offset_table_index); + } + + // Write the code into the file + try self.base.file.?.pwriteAll(code, self.section_data_offset + self.offset_table_size + decl.link.coff.text_offset); + + // Since we updated the vaddr and the size, each corresponding export symbol also needs to be updated. + const decl_exports = module.decl_exports.get(decl) orelse &[0]*Module.Export{}; + return self.updateDeclExports(module, decl, decl_exports); +} + +pub fn freeDecl(self: *Coff, decl: *Module.Decl) void { + // Appending to free lists is allowed to fail because the free lists are heuristics based anyway. + self.freeTextBlock(&decl.link.coff); + self.offset_table_free_list.append(self.base.allocator, decl.link.coff.offset_table_index) catch {}; +} + +pub fn updateDeclExports(self: *Coff, module: *Module, decl: *const Module.Decl, exports: []const *Module.Export) !void { + for (exports) |exp| { + if (exp.options.section) |section_name| { + if (!mem.eql(u8, section_name, ".text")) { + try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1); + module.failed_exports.putAssumeCapacityNoClobber( + exp, + try Compilation.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: ExportOptions.section", .{}), + ); + continue; + } + } + if (mem.eql(u8, exp.options.name, "_start")) { + self.entry_addr = decl.link.coff.getVAddr(self.*) - image_base; + } else { + try module.failed_exports.ensureCapacity(module.gpa, module.failed_exports.items().len + 1); + module.failed_exports.putAssumeCapacityNoClobber( + exp, + try Compilation.ErrorMsg.create(self.base.allocator, 0, "Unimplemented: Exports other than '_start'", .{}), + ); + continue; + } + } +} + +pub fn flush(self: *Coff, comp: *Compilation) !void { + if (build_options.have_llvm and self.base.options.use_lld) { + return self.linkWithLLD(comp); + } else { + switch (self.base.options.effectiveOutputMode()) { + .Exe, .Obj => {}, + .Lib => return error.TODOImplementWritingLibFiles, + } + return self.flushModule(comp); + } +} + +pub fn flushModule(self: *Coff, comp: *Compilation) !void { + const tracy = trace(@src()); + defer tracy.end(); + + if (self.text_section_size_dirty) { + // Write the new raw size in the .text header + var buf: [4]u8 = undefined; + mem.writeIntLittle(u32, &buf, self.text_section_size); + try self.base.file.?.pwriteAll(&buf, self.section_table_offset + 40 + 16); + try self.base.file.?.setEndPos(self.section_data_offset + self.offset_table_size + self.text_section_size); + self.text_section_size_dirty = false; + } + + if (self.base.options.output_mode == .Exe and self.size_of_image_dirty) { + const new_size_of_image = mem.alignForwardGeneric(u32, self.text_section_virtual_address - image_base + self.text_section_size, section_alignment); + var buf: [4]u8 = undefined; + mem.writeIntLittle(u32, &buf, new_size_of_image); + try self.base.file.?.pwriteAll(&buf, self.optional_header_offset + 56); + self.size_of_image_dirty = false; + } + + if (self.entry_addr == null and self.base.options.output_mode == .Exe) { + log.debug("flushing. no_entry_point_found = true\n", .{}); + self.error_flags.no_entry_point_found = true; + } else { + log.debug("flushing. no_entry_point_found = false\n", .{}); + self.error_flags.no_entry_point_found = false; + + if (self.base.options.output_mode == .Exe) { + // Write AddressOfEntryPoint + var buf: [4]u8 = undefined; + mem.writeIntLittle(u32, &buf, self.entry_addr.?); + try self.base.file.?.pwriteAll(&buf, self.optional_header_offset + 16); + } + } +} + +fn linkWithLLD(self: *Coff, comp: *Compilation) !void { + const tracy = trace(@src()); + defer tracy.end(); + + var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator); + defer arena_allocator.deinit(); + const arena = &arena_allocator.allocator; + + const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type. + + // If there is no Zig code to compile, then we should skip flushing the output file because it + // will not be part of the linker line anyway. + const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: { + const use_stage1 = build_options.is_stage1 and self.base.options.use_llvm; + if (use_stage1) { + const obj_basename = try std.zig.binNameAlloc(arena, .{ + .root_name = self.base.options.root_name, + .target = self.base.options.target, + .output_mode = .Obj, + }); + const o_directory = self.base.options.module.?.zig_cache_artifact_directory; + const full_obj_path = try o_directory.join(arena, &[_][]const u8{obj_basename}); + break :blk full_obj_path; + } + + try self.flushModule(comp); + const obj_basename = self.base.intermediary_basename.?; + const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename}); + break :blk full_obj_path; + } else null; + + const is_lib = self.base.options.output_mode == .Lib; + const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib; + const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe; + const link_in_crt = self.base.options.link_libc and self.base.options.output_mode == .Exe; + const target = self.base.options.target; + + // See link/Elf.zig for comments on how this mechanism works. + const id_symlink_basename = "lld.id"; + + var man: Cache.Manifest = undefined; + defer if (!self.base.options.disable_lld_caching) man.deinit(); + + var digest: [Cache.hex_digest_len]u8 = undefined; + + if (!self.base.options.disable_lld_caching) { + man = comp.cache_parent.obtain(); + self.base.releaseLock(); + + try man.addListOfFiles(self.base.options.objects); + for (comp.c_object_table.items()) |entry| { + _ = try man.addFile(entry.key.status.success.object_path, null); + } + try man.addOptionalFile(module_obj_path); + man.hash.addOptional(self.base.options.stack_size_override); + man.hash.addListOfBytes(self.base.options.extra_lld_args); + man.hash.addListOfBytes(self.base.options.lib_dirs); + man.hash.add(self.base.options.is_compiler_rt_or_libc); + if (self.base.options.link_libc) { + man.hash.add(self.base.options.libc_installation != null); + if (self.base.options.libc_installation) |libc_installation| { + man.hash.addBytes(libc_installation.crt_dir.?); + if (target.abi == .msvc) { + man.hash.addBytes(libc_installation.msvc_lib_dir.?); + man.hash.addBytes(libc_installation.kernel32_lib_dir.?); + } + } + } + man.hash.addStringSet(self.base.options.system_libs); + man.hash.addOptional(self.base.options.subsystem); + man.hash.add(self.base.options.is_test); + + // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. + _ = try man.hit(); + digest = man.final(); + var prev_digest_buf: [digest.len]u8 = undefined; + const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch |err| blk: { + log.debug("COFF LLD new_digest={} readlink error: {}", .{ digest, @errorName(err) }); + // Handle this as a cache miss. + break :blk prev_digest_buf[0..0]; + }; + if (mem.eql(u8, prev_digest, &digest)) { + log.debug("COFF LLD digest={} match - skipping invocation", .{digest}); + // Hot diggity dog! The output binary is already there. + self.base.lock = man.toOwnedLock(); + return; + } + log.debug("COFF LLD prev_digest={} new_digest={}", .{ prev_digest, digest }); + + // We are about to change the output file to be different, so we invalidate the build hash now. + directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { + error.FileNotFound => {}, + else => |e| return e, + }; + } + + const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path}); + + if (self.base.options.output_mode == .Obj) { + // LLD's COFF driver does not support the equvialent of `-r` so we do a simple file copy + // here. TODO: think carefully about how we can avoid this redundant operation when doing + // build-obj. See also the corresponding TODO in linkAsArchive. + const the_object_path = blk: { + if (self.base.options.objects.len != 0) + break :blk self.base.options.objects[0]; + + if (comp.c_object_table.count() != 0) + break :blk comp.c_object_table.items()[0].key.status.success.object_path; + + if (module_obj_path) |p| + break :blk p; + + // TODO I think this is unreachable. Audit this situation when solving the above TODO + // regarding eliding redundant object -> object transformations. + return error.NoObjectsToLink; + }; + // This can happen when using --enable-cache and using the stage1 backend. In this case + // we can skip the file copy. + if (!mem.eql(u8, the_object_path, full_out_path)) { + try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{}); + } + } else { + // Create an LLD command line and invoke it. + var argv = std.ArrayList([]const u8).init(self.base.allocator); + defer argv.deinit(); + // Even though we're calling LLD as a library it thinks the first argument is its own exe name. + try argv.append("lld"); + + try argv.append("-ERRORLIMIT:0"); + try argv.append("-NOLOGO"); + if (!self.base.options.strip) { + try argv.append("-DEBUG"); + } + if (self.base.options.output_mode == .Exe) { + const stack_size = self.base.options.stack_size_override orelse 16777216; + try argv.append(try allocPrint(arena, "-STACK:{d}", .{stack_size})); + } + + if (target.cpu.arch == .i386) { + try argv.append("-MACHINE:X86"); + } else if (target.cpu.arch == .x86_64) { + try argv.append("-MACHINE:X64"); + } else if (target.cpu.arch.isARM()) { + if (target.cpu.arch.ptrBitWidth() == 32) { + try argv.append("-MACHINE:ARM"); + } else { + try argv.append("-MACHINE:ARM64"); + } + } + + if (is_dyn_lib) { + try argv.append("-DLL"); + } + + try argv.append(try allocPrint(arena, "-OUT:{s}", .{full_out_path})); + + if (self.base.options.link_libc) { + if (self.base.options.libc_installation) |libc_installation| { + try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.crt_dir.?})); + + if (target.abi == .msvc) { + try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.msvc_lib_dir.?})); + try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{libc_installation.kernel32_lib_dir.?})); + } + } + } + + for (self.base.options.lib_dirs) |lib_dir| { + try argv.append(try allocPrint(arena, "-LIBPATH:{s}", .{lib_dir})); + } + + try argv.appendSlice(self.base.options.objects); + + for (comp.c_object_table.items()) |entry| { + try argv.append(entry.key.status.success.object_path); + } + + if (module_obj_path) |p| { + try argv.append(p); + } + + const resolved_subsystem: ?std.Target.SubSystem = blk: { + if (self.base.options.subsystem) |explicit| break :blk explicit; + switch (target.os.tag) { + .windows => { + if (self.base.options.module) |module| { + if (module.stage1_flags.have_dllmain_crt_startup or is_dyn_lib) + break :blk null; + if (module.stage1_flags.have_c_main or self.base.options.is_test or + module.stage1_flags.have_winmain_crt_startup or + module.stage1_flags.have_wwinmain_crt_startup) + { + break :blk .Console; + } + if (module.stage1_flags.have_winmain or module.stage1_flags.have_wwinmain) + break :blk .Windows; + } + }, + .uefi => break :blk .EfiApplication, + else => {}, + } + break :blk null; + }; + const Mode = enum { uefi, win32 }; + const mode: Mode = mode: { + if (resolved_subsystem) |subsystem| switch (subsystem) { + .Console => { + try argv.append("-SUBSYSTEM:console"); + break :mode .win32; + }, + .EfiApplication => { + try argv.append("-SUBSYSTEM:efi_application"); + break :mode .uefi; + }, + .EfiBootServiceDriver => { + try argv.append("-SUBSYSTEM:efi_boot_service_driver"); + break :mode .uefi; + }, + .EfiRom => { + try argv.append("-SUBSYSTEM:efi_rom"); + break :mode .uefi; + }, + .EfiRuntimeDriver => { + try argv.append("-SUBSYSTEM:efi_runtime_driver"); + break :mode .uefi; + }, + .Native => { + try argv.append("-SUBSYSTEM:native"); + break :mode .win32; + }, + .Posix => { + try argv.append("-SUBSYSTEM:posix"); + break :mode .win32; + }, + .Windows => { + try argv.append("-SUBSYSTEM:windows"); + break :mode .win32; + }, + } else if (target.os.tag == .uefi) { + break :mode .uefi; + } else { + break :mode .win32; + } + }; + + switch (mode) { + .uefi => try argv.appendSlice(&[_][]const u8{ + "-BASE:0", + "-ENTRY:EfiMain", + "-OPT:REF", + "-SAFESEH:NO", + "-MERGE:.rdata=.data", + "-ALIGN:32", + "-NODEFAULTLIB", + "-SECTION:.xdata,D", + }), + .win32 => { + if (link_in_crt) { + if (target.abi.isGnu()) { + try argv.append("-lldmingw"); + + if (target.cpu.arch == .i386) { + try argv.append("-ALTERNATENAME:__image_base__=___ImageBase"); + } else { + try argv.append("-ALTERNATENAME:__image_base__=__ImageBase"); + } + + if (is_dyn_lib) { + try argv.append(try comp.get_libc_crt_file(arena, "dllcrt2.o")); + } else { + try argv.append(try comp.get_libc_crt_file(arena, "crt2.o")); + } + + try argv.append(try comp.get_libc_crt_file(arena, "mingw32.lib")); + try argv.append(try comp.get_libc_crt_file(arena, "mingwex.lib")); + try argv.append(try comp.get_libc_crt_file(arena, "msvcrt-os.lib")); + + for (mingw.always_link_libs) |name| { + if (!self.base.options.system_libs.contains(name)) { + const lib_basename = try allocPrint(arena, "{s}.lib", .{name}); + try argv.append(try comp.get_libc_crt_file(arena, lib_basename)); + } + } + } else { + const lib_str = switch (self.base.options.link_mode) { + .Dynamic => "", + .Static => "lib", + }; + const d_str = switch (self.base.options.optimize_mode) { + .Debug => "d", + else => "", + }; + switch (self.base.options.link_mode) { + .Static => try argv.append(try allocPrint(arena, "libcmt{s}.lib", .{d_str})), + .Dynamic => try argv.append(try allocPrint(arena, "msvcrt{s}.lib", .{d_str})), + } + + try argv.append(try allocPrint(arena, "{s}vcruntime{s}.lib", .{ lib_str, d_str })); + try argv.append(try allocPrint(arena, "{s}ucrt{s}.lib", .{ lib_str, d_str })); + + //Visual C++ 2015 Conformance Changes + //https://msdn.microsoft.com/en-us/library/bb531344.aspx + try argv.append("legacy_stdio_definitions.lib"); + + // msvcrt depends on kernel32 and ntdll + try argv.append("kernel32.lib"); + try argv.append("ntdll.lib"); + } + } else { + try argv.append("-NODEFAULTLIB"); + if (!is_lib) { + if (self.base.options.module) |module| { + if (module.stage1_flags.have_winmain) { + try argv.append("-ENTRY:WinMain"); + } else if (module.stage1_flags.have_wwinmain) { + try argv.append("-ENTRY:wWinMain"); + } else if (module.stage1_flags.have_wwinmain_crt_startup) { + try argv.append("-ENTRY:wWinMainCRTStartup"); + } else { + try argv.append("-ENTRY:WinMainCRTStartup"); + } + } else { + try argv.append("-ENTRY:WinMainCRTStartup"); + } + } + } + }, + } + + // libc++ dep + if (self.base.options.link_libcpp) { + try argv.append(comp.libcxxabi_static_lib.?.full_object_path); + try argv.append(comp.libcxx_static_lib.?.full_object_path); + try argv.append(comp.libunwind_static_lib.?.full_object_path); + } + + // compiler-rt and libc + if (is_exe_or_dyn_lib and !self.base.options.is_compiler_rt_or_libc) { + if (!self.base.options.link_libc) { + try argv.append(comp.libc_static_lib.?.full_object_path); + } + // MSVC compiler_rt is missing some stuff, so we build it unconditionally but + // and rely on weak linkage to allow MSVC compiler_rt functions to override ours. + try argv.append(comp.compiler_rt_static_lib.?.full_object_path); + } + + for (self.base.options.system_libs.items()) |entry| { + const lib_basename = try allocPrint(arena, "{s}.lib", .{entry.key}); + if (comp.crt_files.get(lib_basename)) |crt_file| { + try argv.append(crt_file.full_object_path); + } else { + try argv.append(lib_basename); + } + } + + if (self.base.options.verbose_link) { + Compilation.dump_argv(argv.items); + } + + const new_argv = try arena.allocSentinel(?[*:0]const u8, argv.items.len, null); + for (argv.items) |arg, i| { + new_argv[i] = try arena.dupeZ(u8, arg); + } + + var stderr_context: LLDContext = .{ + .coff = self, + .data = std.ArrayList(u8).init(self.base.allocator), + }; + defer stderr_context.data.deinit(); + var stdout_context: LLDContext = .{ + .coff = self, + .data = std.ArrayList(u8).init(self.base.allocator), + }; + defer stdout_context.data.deinit(); + const llvm = @import("../llvm.zig"); + const ok = llvm.Link( + .COFF, + new_argv.ptr, + new_argv.len, + append_diagnostic, + @ptrToInt(&stdout_context), + @ptrToInt(&stderr_context), + ); + if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory; + if (stdout_context.data.items.len != 0) { + std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items}); + } + if (!ok) { + // TODO parse this output and surface with the Compilation API rather than + // directly outputting to stderr here. + std.debug.print("{}", .{stderr_context.data.items}); + return error.LLDReportedFailure; + } + if (stderr_context.data.items.len != 0) { + std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items}); + } + } + + if (!self.base.options.disable_lld_caching) { + // Update the dangling symlink with the digest. If it fails we can continue; it only + // means that the next invocation will have an unnecessary cache miss. + directory.handle.symLink(&digest, id_symlink_basename, .{}) catch |err| { + std.log.warn("failed to save linking hash digest symlink: {}", .{@errorName(err)}); + }; + // Again failure here only means an unnecessary cache miss. + man.writeManifest() catch |err| { + std.log.warn("failed to write cache manifest when linking: {}", .{@errorName(err)}); + }; + // We hang on to this lock so that the output file path can be used without + // other processes clobbering it. + self.base.lock = man.toOwnedLock(); + } +} + +const LLDContext = struct { + data: std.ArrayList(u8), + coff: *Coff, + oom: bool = false, +}; + +fn append_diagnostic(context: usize, ptr: [*]const u8, len: usize) callconv(.C) void { + const lld_context = @intToPtr(*LLDContext, context); + const msg = ptr[0..len]; + lld_context.data.appendSlice(msg) catch |err| switch (err) { + error.OutOfMemory => lld_context.oom = true, + }; +} + +pub fn getDeclVAddr(self: *Coff, decl: *const Module.Decl) u64 { + return self.text_section_virtual_address + decl.link.coff.text_offset; +} + +pub fn updateDeclLineNumber(self: *Coff, module: *Module, decl: *Module.Decl) !void { + // TODO Implement this +} + +pub fn deinit(self: *Coff) void { + self.text_block_free_list.deinit(self.base.allocator); + self.offset_table.deinit(self.base.allocator); + self.offset_table_free_list.deinit(self.base.allocator); +} diff --git a/src/link/Elf.zig b/src/link/Elf.zig new file mode 100644 index 0000000000000000000000000000000000000000..38b9b1acca1f2616e0e6e6541fbcc5abeb3a33e5 --- /dev/null +++ b/src/link/Elf.zig @@ -0,0 +1,3100 @@ +const Elf = @This(); + +const std = @import("std"); +const mem = std.mem; +const assert = std.debug.assert; +const Allocator = std.mem.Allocator; +const fs = std.fs; +const elf = std.elf; +const log = std.log.scoped(.link); +const DW = std.dwarf; +const leb128 = std.debug.leb; + +const ir = @import("../ir.zig"); +const Module = @import("../Module.zig"); +const Compilation = @import("../Compilation.zig"); +const codegen = @import("../codegen.zig"); +const trace = @import("../tracy.zig").trace; +const Package = @import("../Package.zig"); +const Value = @import("../value.zig").Value; +const Type = @import("../type.zig").Type; +const link = @import("../link.zig"); +const File = link.File; +const build_options = @import("build_options"); +const target_util = @import("../target.zig"); +const glibc = @import("../glibc.zig"); +const Cache = @import("../Cache.zig"); + +const default_entry_addr = 0x8000000; + +// 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: PtrWidth, + +/// 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 PtrWidth = enum { p32, p64 }; + +pub const TextBlock = struct { + /// Each decl always gets a local symbol with the fully qualified name. + /// The vaddr and size are found here directly. + /// 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, sub_path: []const u8, options: link.Options) !*Elf { + assert(options.object_format == .elf); + + if (options.use_llvm) return error.LLVMBackendUnimplementedForELF; // TODO + + const file = try options.emit.?.directory.handle.createFile(sub_path, .{ + .truncate = false, + .read = true, + .mode = link.determineMode(options), + }); + errdefer file.close(); + + const self = try createEmpty(allocator, options); + errdefer self.base.destroy(); + + self.base.file = file; + self.shdr_table_dirty = true; + + // 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 createEmpty(gpa: *Allocator, options: link.Options) !*Elf { + const ptr_width: PtrWidth = switch (options.target.cpu.arch.ptrBitWidth()) { + 0 ... 32 => .p32, + 33 ... 64 => .p64, + else => return error.UnsupportedELFArchitecture, + }; + const self = try gpa.create(Elf); + self.* = .{ + .base = .{ + .tag = .elf, + .options = options, + .allocator = gpa, + .file = null, + }, + .ptr_width = ptr_width, + }; + return self; +} + +pub fn deinit(self: *Elf) void { + self.sections.deinit(self.base.allocator); + self.program_headers.deinit(self.base.allocator); + 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; + +pub fn flush(self: *Elf, comp: *Compilation) !void { + if (build_options.have_llvm and self.base.options.use_lld) { + return self.linkWithLLD(comp); + } else { + switch (self.base.options.effectiveOutputMode()) { + .Exe, .Obj => {}, + .Lib => return error.TODOImplementWritingLibFiles, + } + return self.flushModule(comp); + } +} + +pub fn flushModule(self: *Elf, comp: *Compilation) !void { + const tracy = trace(@src()); + defer tracy.end(); + + // TODO This linker code currently assumes there is only 1 compilation unit and it corresponds to the + // Zig source code. + const module = self.base.options.module orelse return error.LinkingWithoutZigSourceUnimplemented; + + const target_endian = self.base.options.target.cpu.arch.endian(); + const foreign_endian = target_endian != std.Target.current.cpu.arch.endian(); + const ptr_width_bytes: u8 = self.ptrWidthBytes(); + 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(module.root_pkg.root_src_path); + const comp_dir_strp = try self.makeDebugString(module.root_pkg.root_src_directory.path orelse "."); + const producer_strp = try self.makeDebugString(link.producer_string); + // Currently only one compilation unit is supported, so the address range is simply + // identical to the main program header virtual address and memory size. + 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(module.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.effectiveOutputMode() == .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 linkWithLLD(self: *Elf, comp: *Compilation) !void { + const tracy = trace(@src()); + defer tracy.end(); + + var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator); + defer arena_allocator.deinit(); + const arena = &arena_allocator.allocator; + + const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type. + + // If there is no Zig code to compile, then we should skip flushing the output file because it + // will not be part of the linker line anyway. + const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: { + const use_stage1 = build_options.is_stage1 and self.base.options.use_llvm; + if (use_stage1) { + const obj_basename = try std.zig.binNameAlloc(arena, .{ + .root_name = self.base.options.root_name, + .target = self.base.options.target, + .output_mode = .Obj, + }); + const o_directory = self.base.options.module.?.zig_cache_artifact_directory; + const full_obj_path = try o_directory.join(arena, &[_][]const u8{obj_basename}); + break :blk full_obj_path; + } + + try self.flushModule(comp); + const obj_basename = self.base.intermediary_basename.?; + const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename}); + break :blk full_obj_path; + } else null; + + const is_obj = self.base.options.output_mode == .Obj; + const is_lib = self.base.options.output_mode == .Lib; + const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib; + const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe; + const have_dynamic_linker = self.base.options.link_libc and + self.base.options.link_mode == .Dynamic and is_exe_or_dyn_lib; + const link_in_crt = self.base.options.link_libc and self.base.options.output_mode == .Exe; + const target = self.base.options.target; + const gc_sections = self.base.options.gc_sections orelse !is_obj; + const stack_size = self.base.options.stack_size_override orelse 16777216; + const allow_shlib_undefined = self.base.options.allow_shlib_undefined orelse !self.base.options.is_native_os; + + // Here we want to determine whether we can save time by not invoking LLD when the + // output is unchanged. None of the linker options or the object files that are being + // linked are in the hash that namespaces the directory we are outputting to. Therefore, + // we must hash those now, and the resulting digest will form the "id" of the linking + // job we are about to perform. + // After a successful link, we store the id in the metadata of a symlink named "id.txt" in + // the artifact directory. So, now, we check if this symlink exists, and if it matches + // our digest. If so, we can skip linking. Otherwise, we proceed with invoking LLD. + const id_symlink_basename = "lld.id"; + + var man: Cache.Manifest = undefined; + defer if (!self.base.options.disable_lld_caching) man.deinit(); + + var digest: [Cache.hex_digest_len]u8 = undefined; + + if (!self.base.options.disable_lld_caching) { + man = comp.cache_parent.obtain(); + + // We are about to obtain this lock, so here we give other processes a chance first. + self.base.releaseLock(); + + try man.addOptionalFile(self.base.options.linker_script); + try man.addOptionalFile(self.base.options.version_script); + try man.addListOfFiles(self.base.options.objects); + for (comp.c_object_table.items()) |entry| { + _ = try man.addFile(entry.key.status.success.object_path, null); + } + try man.addOptionalFile(module_obj_path); + // We can skip hashing libc and libc++ components that we are in charge of building from Zig + // installation sources because they are always a product of the compiler version + target information. + man.hash.add(stack_size); + man.hash.add(gc_sections); + man.hash.add(self.base.options.eh_frame_hdr); + man.hash.add(self.base.options.rdynamic); + man.hash.addListOfBytes(self.base.options.extra_lld_args); + man.hash.addListOfBytes(self.base.options.lib_dirs); + man.hash.addListOfBytes(self.base.options.rpath_list); + man.hash.add(self.base.options.each_lib_rpath); + man.hash.add(self.base.options.is_compiler_rt_or_libc); + man.hash.add(self.base.options.z_nodelete); + man.hash.add(self.base.options.z_defs); + if (self.base.options.link_libc) { + man.hash.add(self.base.options.libc_installation != null); + if (self.base.options.libc_installation) |libc_installation| { + man.hash.addBytes(libc_installation.crt_dir.?); + } + if (have_dynamic_linker) { + man.hash.addOptionalBytes(self.base.options.dynamic_linker); + } + } + if (is_dyn_lib) { + man.hash.addOptionalBytes(self.base.options.override_soname); + man.hash.addOptional(self.base.options.version); + } + man.hash.addStringSet(self.base.options.system_libs); + man.hash.add(allow_shlib_undefined); + man.hash.add(self.base.options.bind_global_refs_locally); + + // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. + _ = try man.hit(); + digest = man.final(); + + var prev_digest_buf: [digest.len]u8 = undefined; + const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch |err| blk: { + log.debug("ELF LLD new_digest={} readlink error: {}", .{digest, @errorName(err)}); + // Handle this as a cache miss. + break :blk prev_digest_buf[0..0]; + }; + if (mem.eql(u8, prev_digest, &digest)) { + log.debug("ELF LLD digest={} match - skipping invocation", .{digest}); + // Hot diggity dog! The output binary is already there. + self.base.lock = man.toOwnedLock(); + return; + } + log.debug("ELF LLD prev_digest={} new_digest={}", .{prev_digest, digest}); + + // We are about to change the output file to be different, so we invalidate the build hash now. + directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { + error.FileNotFound => {}, + else => |e| return e, + }; + } + + // Create an LLD command line and invoke it. + var argv = std.ArrayList([]const u8).init(self.base.allocator); + defer argv.deinit(); + // Even though we're calling LLD as a library it thinks the first argument is its own exe name. + try argv.append("lld"); + if (is_obj) { + try argv.append("-r"); + } + + try argv.append("-error-limit=0"); + + if (self.base.options.output_mode == .Exe) { + try argv.append("-z"); + try argv.append(try std.fmt.allocPrint(arena, "stack-size={}", .{stack_size})); + } + + if (self.base.options.linker_script) |linker_script| { + try argv.append("-T"); + try argv.append(linker_script); + } + + if (gc_sections) { + try argv.append("--gc-sections"); + } + + if (self.base.options.eh_frame_hdr) { + try argv.append("--eh-frame-hdr"); + } + + if (self.base.options.rdynamic) { + try argv.append("--export-dynamic"); + } + + try argv.appendSlice(self.base.options.extra_lld_args); + + if (self.base.options.z_nodelete) { + try argv.append("-z"); + try argv.append("nodelete"); + } + if (self.base.options.z_defs) { + try argv.append("-z"); + try argv.append("defs"); + } + + if (getLDMOption(target)) |ldm| { + // Any target ELF will use the freebsd osabi if suffixed with "_fbsd". + const arg = if (target.os.tag == .freebsd) + try std.fmt.allocPrint(arena, "{}_fbsd", .{ldm}) + else + ldm; + try argv.append("-m"); + try argv.append(arg); + } + + if (self.base.options.link_mode == .Static) { + if (target.cpu.arch.isARM() or target.cpu.arch.isThumb()) { + try argv.append("-Bstatic"); + } else { + try argv.append("-static"); + } + } else if (is_dyn_lib) { + try argv.append("-shared"); + } + + if (target_util.requiresPIE(target) and self.base.options.output_mode == .Exe) { + try argv.append("-pie"); + } + + const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path}); + try argv.append("-o"); + try argv.append(full_out_path); + + if (link_in_crt) { + const crt1o: []const u8 = o: { + if (target.os.tag == .netbsd) { + break :o "crt0.o"; + } else if (target.isAndroid()) { + if (self.base.options.link_mode == .Dynamic) { + break :o "crtbegin_dynamic.o"; + } else { + break :o "crtbegin_static.o"; + } + } else if (self.base.options.link_mode == .Static) { + break :o "crt1.o"; + } else { + break :o "Scrt1.o"; + } + }; + try argv.append(try comp.get_libc_crt_file(arena, crt1o)); + if (target_util.libc_needs_crti_crtn(target)) { + try argv.append(try comp.get_libc_crt_file(arena, "crti.o")); + } + } + + // rpaths + var rpath_table = std.StringHashMap(void).init(self.base.allocator); + defer rpath_table.deinit(); + for (self.base.options.rpath_list) |rpath| { + if ((try rpath_table.fetchPut(rpath, {})) == null) { + try argv.append("-rpath"); + try argv.append(rpath); + } + } + if (self.base.options.each_lib_rpath) { + var test_path = std.ArrayList(u8).init(self.base.allocator); + defer test_path.deinit(); + for (self.base.options.lib_dirs) |lib_dir_path| { + for (self.base.options.system_libs.items()) |link_lib| { + test_path.shrinkRetainingCapacity(0); + const sep = fs.path.sep_str; + try test_path.writer().print("{}" ++ sep ++ "lib{}.so", .{ lib_dir_path, link_lib }); + fs.cwd().access(test_path.items, .{}) catch |err| switch (err) { + error.FileNotFound => continue, + else => |e| return e, + }; + if ((try rpath_table.fetchPut(lib_dir_path, {})) == null) { + try argv.append("-rpath"); + try argv.append(lib_dir_path); + } + } + } + } + + for (self.base.options.lib_dirs) |lib_dir| { + try argv.append("-L"); + try argv.append(lib_dir); + } + + if (self.base.options.link_libc) { + if (self.base.options.libc_installation) |libc_installation| { + try argv.append("-L"); + try argv.append(libc_installation.crt_dir.?); + } + + if (have_dynamic_linker) { + if (self.base.options.dynamic_linker) |dynamic_linker| { + try argv.append("-dynamic-linker"); + try argv.append(dynamic_linker); + } + } + } + + if (is_dyn_lib) { + const soname = self.base.options.override_soname orelse if (self.base.options.version) |ver| + try std.fmt.allocPrint(arena, "lib{}.so.{}", .{self.base.options.root_name, ver.major}) + else + try std.fmt.allocPrint(arena, "lib{}.so", .{self.base.options.root_name}); + try argv.append("-soname"); + try argv.append(soname); + + if (self.base.options.version_script) |version_script| { + try argv.append("-version-script"); + try argv.append(version_script); + } + } + + // Positional arguments to the linker such as object files. + try argv.appendSlice(self.base.options.objects); + + for (comp.c_object_table.items()) |entry| { + try argv.append(entry.key.status.success.object_path); + } + + if (module_obj_path) |p| { + try argv.append(p); + } + + // compiler-rt and libc + if (is_exe_or_dyn_lib and !self.base.options.is_compiler_rt_or_libc) { + if (!self.base.options.link_libc) { + try argv.append(comp.libc_static_lib.?.full_object_path); + } + try argv.append(comp.compiler_rt_static_lib.?.full_object_path); + } + + // Shared libraries. + const system_libs = self.base.options.system_libs.items(); + try argv.ensureCapacity(argv.items.len + system_libs.len); + for (system_libs) |entry| { + const link_lib = entry.key; + // By this time, we depend on these libs being dynamically linked libraries and not static libraries + // (the check for that needs to be earlier), but they could be full paths to .so files, in which + // case we want to avoid prepending "-l". + const ext = Compilation.classifyFileExt(link_lib); + const arg = if (ext == .shared_library) link_lib else try std.fmt.allocPrint(arena, "-l{}", .{link_lib}); + argv.appendAssumeCapacity(arg); + } + + if (!is_obj) { + // libc++ dep + if (self.base.options.link_libcpp) { + try argv.append(comp.libcxxabi_static_lib.?.full_object_path); + try argv.append(comp.libcxx_static_lib.?.full_object_path); + } + + // libc dep + if (self.base.options.link_libc) { + if (self.base.options.libc_installation != null) { + if (self.base.options.link_mode == .Static) { + try argv.append("--start-group"); + try argv.append("-lc"); + try argv.append("-lm"); + try argv.append("--end-group"); + } else { + try argv.append("-lc"); + try argv.append("-lm"); + } + + if (target.os.tag == .freebsd or target.os.tag == .netbsd) { + try argv.append("-lpthread"); + } + } else if (target.isGnuLibC()) { + try argv.append(comp.libunwind_static_lib.?.full_object_path); + for (glibc.libs) |lib| { + const lib_path = try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{ + comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover, + }); + try argv.append(lib_path); + } + try argv.append(try comp.get_libc_crt_file(arena, "libc_nonshared.a")); + } else if (target.isMusl()) { + try argv.append(comp.libunwind_static_lib.?.full_object_path); + try argv.append(try comp.get_libc_crt_file(arena, "libc.a")); + } else if (self.base.options.link_libcpp) { + try argv.append(comp.libunwind_static_lib.?.full_object_path); + } else { + unreachable; // Compiler was supposed to emit an error for not being able to provide libc. + } + } + } + + // crt end + if (link_in_crt) { + if (target.isAndroid()) { + try argv.append(try comp.get_libc_crt_file(arena, "crtend_android.o")); + } else if (target_util.libc_needs_crti_crtn(target)) { + try argv.append(try comp.get_libc_crt_file(arena, "crtn.o")); + } + } + + if (allow_shlib_undefined) { + try argv.append("--allow-shlib-undefined"); + } + + if (self.base.options.bind_global_refs_locally) { + try argv.append("-Bsymbolic"); + } + + if (self.base.options.verbose_link) { + Compilation.dump_argv(argv.items); + } + + // Oh, snapplesauce! We need null terminated argv. + const new_argv = try arena.allocSentinel(?[*:0]const u8, argv.items.len, null); + for (argv.items) |arg, i| { + new_argv[i] = try arena.dupeZ(u8, arg); + } + + var stderr_context: LLDContext = .{ + .elf = self, + .data = std.ArrayList(u8).init(self.base.allocator), + }; + defer stderr_context.data.deinit(); + var stdout_context: LLDContext = .{ + .elf = self, + .data = std.ArrayList(u8).init(self.base.allocator), + }; + defer stdout_context.data.deinit(); + const llvm = @import("../llvm.zig"); + const ok = llvm.Link(.ELF, new_argv.ptr, new_argv.len, append_diagnostic, + @ptrToInt(&stdout_context), + @ptrToInt(&stderr_context), + ); + if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory; + if (stdout_context.data.items.len != 0) { + std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items}); + } + if (!ok) { + // TODO parse this output and surface with the Compilation API rather than + // directly outputting to stderr here. + std.debug.print("{}", .{stderr_context.data.items}); + return error.LLDReportedFailure; + } + if (stderr_context.data.items.len != 0) { + std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items}); + } + + if (!self.base.options.disable_lld_caching) { + // Update the dangling symlink with the digest. If it fails we can continue; it only + // means that the next invocation will have an unnecessary cache miss. + directory.handle.symLink(&digest, id_symlink_basename, .{}) catch |err| { + std.log.warn("failed to save linking hash digest symlink: {}", .{@errorName(err)}); + }; + // Again failure here only means an unnecessary cache miss. + man.writeManifest() catch |err| { + std.log.warn("failed to write cache manifest when linking: {}", .{ @errorName(err) }); + }; + // We hang on to this lock so that the output file path can be used without + // other processes clobbering it. + self.base.lock = man.toOwnedLock(); + } +} + +const LLDContext = struct { + data: std.ArrayList(u8), + elf: *Elf, + oom: bool = false, +}; + +fn append_diagnostic(context: usize, ptr: [*]const u8, len: usize) callconv(.C) void { + const lld_context = @intToPtr(*LLDContext, context); + const msg = ptr[0..len]; + lld_context.data.appendSlice(msg) catch |err| switch (err) { + error.OutOfMemory => lld_context.oom = true, + }; +} + +fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64) void { + const target_endian = self.base.options.target.cpu.arch.endian(); + switch (self.ptr_width) { + .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.effectiveOutputMode()) { + .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 Compilation.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 Compilation.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; + const root_src_dir_path_len = if (self.base.options.module.?.root_pkg.root_src_directory.path) |p| p.len else 1; // "." + return @intCast(u32, 53 + directory_entry_format_count * 2 + file_name_entry_format_count * 2 + + directory_count * 8 + file_name_count * 8 + + // These are encoded as DW.FORM_string rather than DW.FORM_strp as we would like + // because of a workaround for readelf and gdb failing to understand DWARFv5 correctly. + root_src_dir_path_len + + self.base.options.module.?.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), + }; +} + +fn getLDMOption(target: std.Target) ?[]const u8 { + switch (target.cpu.arch) { + .i386 => return "elf_i386", + .aarch64 => return "aarch64linux", + .aarch64_be => return "aarch64_be_linux", + .arm, .thumb => return "armelf_linux_eabi", + .armeb, .thumbeb => return "armebelf_linux_eabi", + .powerpc => return "elf32ppclinux", + .powerpc64 => return "elf64ppc", + .powerpc64le => return "elf64lppc", + .sparc, .sparcel => return "elf32_sparc", + .sparcv9 => return "elf64_sparc", + .mips => return "elf32btsmip", + .mipsel => return "elf32ltsmip", + .mips64 => return "elf64btsmip", + .mips64el => return "elf64ltsmip", + .s390x => return "elf64_s390", + .x86_64 => { + if (target.abi == .gnux32) { + return "elf32_x86_64"; + } else { + return "elf_x86_64"; + } + }, + .riscv32 => return "elf32lriscv", + .riscv64 => return "elf64lriscv", + else => return null, + } +} diff --git a/src/link/MachO.zig b/src/link/MachO.zig new file mode 100644 index 0000000000000000000000000000000000000000..961b64a8409637bab6f53158b2c77f0b2750f729 --- /dev/null +++ b/src/link/MachO.zig @@ -0,0 +1,1108 @@ +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 build_options = @import("build_options"); +const Module = @import("../Module.zig"); +const Compilation = @import("../Compilation.zig"); +const link = @import("../link.zig"); +const File = link.File; +const Cache = @import("../Cache.zig"); +const target_util = @import("../target.zig"); + +pub const base_tag: File.Tag = File.Tag.macho; + +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, sub_path: []const u8, options: link.Options) !*MachO { + assert(options.object_format == .macho); + + if (options.use_llvm) return error.LLVM_BackendIsTODO_ForMachO; // TODO + if (options.use_lld) return error.LLD_LinkingIsTODO_ForMachO; // TODO + + const file = try options.emit.?.directory.handle.createFile(sub_path, .{ + .truncate = false, + .read = true, + .mode = link.determineMode(options), + }); + errdefer file.close(); + + const self = try createEmpty(allocator, options); + errdefer self.base.destroy(); + + self.base.file = file; + + switch (options.output_mode) { + .Exe => {}, + .Obj => {}, + .Lib => return error.TODOImplementWritingLibFiles, + } + + try self.populateMissingMetadata(); + + return self; +} + +pub fn createEmpty(gpa: *Allocator, options: link.Options) !*MachO { + const self = try gpa.create(MachO); + self.* = .{ + .base = .{ + .tag = .macho, + .options = options, + .allocator = gpa, + .file = null, + }, + }; + return self; +} + +pub fn flush(self: *MachO, comp: *Compilation) !void { + if (build_options.have_llvm and self.base.options.use_lld) { + return self.linkWithLLD(comp); + } else { + switch (self.base.options.effectiveOutputMode()) { + .Exe, .Obj => {}, + .Lib => return error.TODOImplementWritingLibFiles, + } + return self.flushModule(comp); + } +} + +pub fn flushModule(self: *MachO, comp: *Compilation) !void { + const tracy = trace(@src()); + defer tracy.end(); + + switch (self.base.options.output_mode) { + .Exe => { + var last_cmd_offset: usize = @sizeOf(macho.mach_header_64); + { + // 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(); + } +} + +fn linkWithLLD(self: *MachO, comp: *Compilation) !void { + const tracy = trace(@src()); + defer tracy.end(); + + var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator); + defer arena_allocator.deinit(); + const arena = &arena_allocator.allocator; + + const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type. + + // If there is no Zig code to compile, then we should skip flushing the output file because it + // will not be part of the linker line anyway. + const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: { + const use_stage1 = build_options.is_stage1 and self.base.options.use_llvm; + if (use_stage1) { + const obj_basename = try std.zig.binNameAlloc(arena, .{ + .root_name = self.base.options.root_name, + .target = self.base.options.target, + .output_mode = .Obj, + }); + const o_directory = self.base.options.module.?.zig_cache_artifact_directory; + const full_obj_path = try o_directory.join(arena, &[_][]const u8{obj_basename}); + break :blk full_obj_path; + } + + try self.flushModule(comp); + const obj_basename = self.base.intermediary_basename.?; + const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename}); + break :blk full_obj_path; + } else null; + + const is_lib = self.base.options.output_mode == .Lib; + const is_dyn_lib = self.base.options.link_mode == .Dynamic and is_lib; + const is_exe_or_dyn_lib = is_dyn_lib or self.base.options.output_mode == .Exe; + const target = self.base.options.target; + const stack_size = self.base.options.stack_size_override orelse 16777216; + const allow_shlib_undefined = self.base.options.allow_shlib_undefined orelse !self.base.options.is_native_os; + + const id_symlink_basename = "lld.id"; + + var man: Cache.Manifest = undefined; + defer if (!self.base.options.disable_lld_caching) man.deinit(); + + var digest: [Cache.hex_digest_len]u8 = undefined; + + if (!self.base.options.disable_lld_caching) { + man = comp.cache_parent.obtain(); + + // We are about to obtain this lock, so here we give other processes a chance first. + self.base.releaseLock(); + + try man.addOptionalFile(self.base.options.linker_script); + try man.addOptionalFile(self.base.options.version_script); + try man.addListOfFiles(self.base.options.objects); + for (comp.c_object_table.items()) |entry| { + _ = try man.addFile(entry.key.status.success.object_path, null); + } + try man.addOptionalFile(module_obj_path); + // We can skip hashing libc and libc++ components that we are in charge of building from Zig + // installation sources because they are always a product of the compiler version + target information. + man.hash.add(stack_size); + man.hash.add(self.base.options.rdynamic); + man.hash.addListOfBytes(self.base.options.extra_lld_args); + man.hash.addListOfBytes(self.base.options.lib_dirs); + man.hash.addListOfBytes(self.base.options.framework_dirs); + man.hash.addListOfBytes(self.base.options.frameworks); + man.hash.addListOfBytes(self.base.options.rpath_list); + man.hash.add(self.base.options.is_compiler_rt_or_libc); + man.hash.add(self.base.options.z_nodelete); + man.hash.add(self.base.options.z_defs); + if (is_dyn_lib) { + man.hash.addOptional(self.base.options.version); + } + man.hash.addStringSet(self.base.options.system_libs); + man.hash.add(allow_shlib_undefined); + man.hash.add(self.base.options.bind_global_refs_locally); + + // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. + _ = try man.hit(); + digest = man.final(); + + var prev_digest_buf: [digest.len]u8 = undefined; + const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch |err| blk: { + log.debug("MachO LLD new_digest={} readlink error: {}", .{ digest, @errorName(err) }); + // Handle this as a cache miss. + break :blk prev_digest_buf[0..0]; + }; + if (mem.eql(u8, prev_digest, &digest)) { + log.debug("MachO LLD digest={} match - skipping invocation", .{digest}); + // Hot diggity dog! The output binary is already there. + self.base.lock = man.toOwnedLock(); + return; + } + log.debug("MachO LLD prev_digest={} new_digest={}", .{ prev_digest, digest }); + + // We are about to change the output file to be different, so we invalidate the build hash now. + directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { + error.FileNotFound => {}, + else => |e| return e, + }; + } + + const full_out_path = try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path}); + + if (self.base.options.output_mode == .Obj) { + // LLD's MachO driver does not support the equvialent of `-r` so we do a simple file copy + // here. TODO: think carefully about how we can avoid this redundant operation when doing + // build-obj. See also the corresponding TODO in linkAsArchive. + const the_object_path = blk: { + if (self.base.options.objects.len != 0) + break :blk self.base.options.objects[0]; + + if (comp.c_object_table.count() != 0) + break :blk comp.c_object_table.items()[0].key.status.success.object_path; + + if (module_obj_path) |p| + break :blk p; + + // TODO I think this is unreachable. Audit this situation when solving the above TODO + // regarding eliding redundant object -> object transformations. + return error.NoObjectsToLink; + }; + // This can happen when using --enable-cache and using the stage1 backend. In this case + // we can skip the file copy. + if (!mem.eql(u8, the_object_path, full_out_path)) { + try fs.cwd().copyFile(the_object_path, fs.cwd(), full_out_path, .{}); + } + } else { + // Create an LLD command line and invoke it. + var argv = std.ArrayList([]const u8).init(self.base.allocator); + defer argv.deinit(); + // Even though we're calling LLD as a library it thinks the first argument is its own exe name. + try argv.append("lld"); + + try argv.append("-error-limit"); + try argv.append("0"); + + try argv.append("-demangle"); + + if (self.base.options.rdynamic) { + try argv.append("--export-dynamic"); + } + + try argv.appendSlice(self.base.options.extra_lld_args); + + if (self.base.options.z_nodelete) { + try argv.append("-z"); + try argv.append("nodelete"); + } + if (self.base.options.z_defs) { + try argv.append("-z"); + try argv.append("defs"); + } + + if (is_dyn_lib) { + try argv.append("-static"); + } else { + try argv.append("-dynamic"); + } + + if (is_dyn_lib) { + try argv.append("-dylib"); + + if (self.base.options.version) |ver| { + const compat_vers = try std.fmt.allocPrint(arena, "{d}.0.0", .{ver.major}); + try argv.append("-compatibility_version"); + try argv.append(compat_vers); + + const cur_vers = try std.fmt.allocPrint(arena, "{d}.{d}.{d}", .{ ver.major, ver.minor, ver.patch }); + try argv.append("-current_version"); + try argv.append(cur_vers); + } + + // TODO getting an error when running an executable when doing this rpath thing + //Buf *dylib_install_name = buf_sprintf("@rpath/lib%s.%" ZIG_PRI_usize ".dylib", + // buf_ptr(g->root_out_name), g->version_major); + //try argv.append("-install_name"); + //try argv.append(buf_ptr(dylib_install_name)); + } + + try argv.append("-arch"); + try argv.append(darwinArchString(target.cpu.arch)); + + switch (target.os.tag) { + .macosx => { + try argv.append("-macosx_version_min"); + }, + .ios, .tvos, .watchos => switch (target.cpu.arch) { + .i386, .x86_64 => { + try argv.append("-ios_simulator_version_min"); + }, + else => { + try argv.append("-iphoneos_version_min"); + }, + }, + else => unreachable, + } + const ver = target.os.version_range.semver.min; + const version_string = try std.fmt.allocPrint(arena, "{d}.{d}.{d}", .{ ver.major, ver.minor, ver.patch }); + try argv.append(version_string); + + try argv.append("-sdk_version"); + try argv.append(version_string); + + if (target_util.requiresPIE(target) and self.base.options.output_mode == .Exe) { + try argv.append("-pie"); + } + + try argv.append("-o"); + try argv.append(full_out_path); + + // rpaths + var rpath_table = std.StringHashMap(void).init(self.base.allocator); + defer rpath_table.deinit(); + for (self.base.options.rpath_list) |rpath| { + if ((try rpath_table.fetchPut(rpath, {})) == null) { + try argv.append("-rpath"); + try argv.append(rpath); + } + } + if (is_dyn_lib) { + if ((try rpath_table.fetchPut(full_out_path, {})) == null) { + try argv.append("-rpath"); + try argv.append(full_out_path); + } + } + + for (self.base.options.lib_dirs) |lib_dir| { + try argv.append("-L"); + try argv.append(lib_dir); + } + + // Positional arguments to the linker such as object files. + try argv.appendSlice(self.base.options.objects); + + for (comp.c_object_table.items()) |entry| { + try argv.append(entry.key.status.success.object_path); + } + if (module_obj_path) |p| { + try argv.append(p); + } + + // compiler_rt on darwin is missing some stuff, so we still build it and rely on LinkOnce + if (is_exe_or_dyn_lib and !self.base.options.is_compiler_rt_or_libc) { + try argv.append(comp.compiler_rt_static_lib.?.full_object_path); + } + + // Shared libraries. + const system_libs = self.base.options.system_libs.items(); + try argv.ensureCapacity(argv.items.len + system_libs.len); + for (system_libs) |entry| { + const link_lib = entry.key; + // By this time, we depend on these libs being dynamically linked libraries and not static libraries + // (the check for that needs to be earlier), but they could be full paths to .dylib files, in which + // case we want to avoid prepending "-l". + const ext = Compilation.classifyFileExt(link_lib); + const arg = if (ext == .shared_library) link_lib else try std.fmt.allocPrint(arena, "-l{}", .{link_lib}); + argv.appendAssumeCapacity(arg); + } + + // libc++ dep + if (self.base.options.link_libcpp) { + try argv.append(comp.libcxxabi_static_lib.?.full_object_path); + try argv.append(comp.libcxx_static_lib.?.full_object_path); + } + + // On Darwin, libSystem has libc in it, but also you have to use it + // to make syscalls because the syscall numbers are not documented + // and change between versions. So we always link against libSystem. + // LLD craps out if you do -lSystem cross compiling, so until that + // codebase gets some love from the new maintainers we're left with + // this dirty hack. + if (self.base.options.is_native_os) { + try argv.append("-lSystem"); + } + + for (self.base.options.framework_dirs) |framework_dir| { + try argv.append("-F"); + try argv.append(framework_dir); + } + for (self.base.options.frameworks) |framework| { + try argv.append("-framework"); + try argv.append(framework); + } + + if (allow_shlib_undefined) { + try argv.append("-undefined"); + try argv.append("dynamic_lookup"); + } + if (self.base.options.bind_global_refs_locally) { + try argv.append("-Bsymbolic"); + } + + if (self.base.options.verbose_link) { + Compilation.dump_argv(argv.items); + } + + const new_argv = try arena.allocSentinel(?[*:0]const u8, argv.items.len, null); + for (argv.items) |arg, i| { + new_argv[i] = try arena.dupeZ(u8, arg); + } + + var stderr_context: LLDContext = .{ + .macho = self, + .data = std.ArrayList(u8).init(self.base.allocator), + }; + defer stderr_context.data.deinit(); + var stdout_context: LLDContext = .{ + .macho = self, + .data = std.ArrayList(u8).init(self.base.allocator), + }; + defer stdout_context.data.deinit(); + const llvm = @import("../llvm.zig"); + const ok = llvm.Link( + .MachO, + new_argv.ptr, + new_argv.len, + append_diagnostic, + @ptrToInt(&stdout_context), + @ptrToInt(&stderr_context), + ); + if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory; + if (stdout_context.data.items.len != 0) { + std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items}); + } + if (!ok) { + // TODO parse this output and surface with the Compilation API rather than + // directly outputting to stderr here. + std.debug.print("{}", .{stderr_context.data.items}); + return error.LLDReportedFailure; + } + if (stderr_context.data.items.len != 0) { + std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items}); + } + } + + if (!self.base.options.disable_lld_caching) { + // Update the dangling symlink with the digest. If it fails we can continue; it only + // means that the next invocation will have an unnecessary cache miss. + directory.handle.symLink(&digest, id_symlink_basename, .{}) catch |err| { + std.log.warn("failed to save linking hash digest symlink: {}", .{@errorName(err)}); + }; + // Again failure here only means an unnecessary cache miss. + man.writeManifest() catch |err| { + std.log.warn("failed to write cache manifest when linking: {}", .{@errorName(err)}); + }; + // We hang on to this lock so that the output file path can be used without + // other processes clobbering it. + self.base.lock = man.toOwnedLock(); + } +} + +const LLDContext = struct { + data: std.ArrayList(u8), + macho: *MachO, + oom: bool = false, +}; + +fn append_diagnostic(context: usize, ptr: [*]const u8, len: usize) callconv(.C) void { + const lld_context = @intToPtr(*LLDContext, context); + const msg = ptr[0..len]; + lld_context.data.appendSlice(msg) catch |err| switch (err) { + error.OutOfMemory => lld_context.oom = true, + }; +} + +fn darwinArchString(arch: std.Target.Cpu.Arch) []const u8 { + return switch (arch) { + .aarch64, .aarch64_be, .aarch64_32 => "arm64", + .thumb, .arm => "arm", + .thumbeb, .armeb => "armeb", + .powerpc => "ppc", + .powerpc64 => "ppc64", + .powerpc64le => "ppc64le", + else => @tagName(arch), + }; +} + +pub fn deinit(self: *MachO) void { + self.offset_table.deinit(self.base.allocator); + self.string_table.deinit(self.base.allocator); + 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/link/Wasm.zig b/src/link/Wasm.zig new file mode 100644 index 0000000000000000000000000000000000000000..3f879a3b3234e0c6bfe60d6a93abcfe4e1fca12f --- /dev/null +++ b/src/link/Wasm.zig @@ -0,0 +1,479 @@ +const Wasm = @This(); + +const std = @import("std"); +const mem = std.mem; +const Allocator = std.mem.Allocator; +const assert = std.debug.assert; +const fs = std.fs; +const leb = std.debug.leb; +const log = std.log.scoped(.link); + +const Module = @import("../Module.zig"); +const Compilation = @import("../Compilation.zig"); +const codegen = @import("../codegen/wasm.zig"); +const link = @import("../link.zig"); +const trace = @import("../tracy.zig").trace; +const build_options = @import("build_options"); +const Cache = @import("../Cache.zig"); + +/// Various magic numbers defined by the wasm spec +const spec = struct { + const magic = [_]u8{ 0x00, 0x61, 0x73, 0x6D }; // \0asm + const version = [_]u8{ 0x01, 0x00, 0x00, 0x00 }; // version 1 + + const custom_id = 0; + const types_id = 1; + const imports_id = 2; + const funcs_id = 3; + const tables_id = 4; + const memories_id = 5; + const globals_id = 6; + const exports_id = 7; + const start_id = 8; + const elements_id = 9; + const code_id = 10; + const data_id = 11; +}; + +pub const base_tag = link.File.Tag.wasm; + +pub const FnData = struct { + /// Generated code for the type of the function + functype: std.ArrayListUnmanaged(u8) = .{}, + /// Generated code for the body of the function + code: std.ArrayListUnmanaged(u8) = .{}, + /// Locations in the generated code where function indexes must be filled in. + /// This must be kept ordered by offset. + idx_refs: std.ArrayListUnmanaged(struct { offset: u32, decl: *Module.Decl }) = .{}, +}; + +base: link.File, + +/// List of all function Decls to be written to the output file. The index of +/// each Decl in this list at the time of writing the binary is used as the +/// function index. +/// TODO: can/should we access some data structure in Module directly? +funcs: std.ArrayListUnmanaged(*Module.Decl) = .{}, + +pub fn openPath(allocator: *Allocator, sub_path: []const u8, options: link.Options) !*Wasm { + assert(options.object_format == .wasm); + + if (options.use_llvm) return error.LLVM_BackendIsTODO_ForWasm; // TODO + if (options.use_lld) return error.LLD_LinkingIsTODO_ForWasm; // TODO + + // TODO: read the file and keep vaild parts instead of truncating + const file = try options.emit.?.directory.handle.createFile(sub_path, .{ .truncate = true, .read = true }); + errdefer file.close(); + + const wasm = try createEmpty(allocator, options); + errdefer wasm.base.destroy(); + + wasm.base.file = file; + + try file.writeAll(&(spec.magic ++ spec.version)); + + return wasm; +} + +pub fn createEmpty(gpa: *Allocator, options: link.Options) !*Wasm { + const wasm = try gpa.create(Wasm); + wasm.* = .{ + .base = .{ + .tag = .wasm, + .options = options, + .file = null, + .allocator = gpa, + }, + }; + return wasm; +} + +pub fn deinit(self: *Wasm) void { + for (self.funcs.items) |decl| { + decl.fn_link.wasm.?.functype.deinit(self.base.allocator); + decl.fn_link.wasm.?.code.deinit(self.base.allocator); + decl.fn_link.wasm.?.idx_refs.deinit(self.base.allocator); + } + self.funcs.deinit(self.base.allocator); +} + +// Generate code for the Decl, storing it in memory to be later written to +// the file on flush(). +pub fn updateDecl(self: *Wasm, module: *Module, decl: *Module.Decl) !void { + if (decl.typed_value.most_recent.typed_value.ty.zigTypeTag() != .Fn) + return error.TODOImplementNonFnDeclsForWasm; + + if (decl.fn_link.wasm) |*fn_data| { + fn_data.functype.items.len = 0; + fn_data.code.items.len = 0; + fn_data.idx_refs.items.len = 0; + } else { + decl.fn_link.wasm = .{}; + try self.funcs.append(self.base.allocator, decl); + } + const fn_data = &decl.fn_link.wasm.?; + + var managed_functype = fn_data.functype.toManaged(self.base.allocator); + var managed_code = fn_data.code.toManaged(self.base.allocator); + try codegen.genFunctype(&managed_functype, decl); + try codegen.genCode(&managed_code, decl); + fn_data.functype = managed_functype.toUnmanaged(); + fn_data.code = managed_code.toUnmanaged(); +} + +pub fn updateDeclExports( + self: *Wasm, + module: *Module, + decl: *const Module.Decl, + exports: []const *Module.Export, +) !void {} + +pub fn freeDecl(self: *Wasm, decl: *Module.Decl) void { + // TODO: remove this assert when non-function Decls are implemented + assert(decl.typed_value.most_recent.typed_value.ty.zigTypeTag() == .Fn); + _ = self.funcs.swapRemove(self.getFuncidx(decl).?); + decl.fn_link.wasm.?.functype.deinit(self.base.allocator); + decl.fn_link.wasm.?.code.deinit(self.base.allocator); + decl.fn_link.wasm.?.idx_refs.deinit(self.base.allocator); + decl.fn_link.wasm = null; +} + +pub fn flush(self: *Wasm, comp: *Compilation) !void { + if (build_options.have_llvm and self.base.options.use_lld) { + return self.linkWithLLD(comp); + } else { + return self.flushModule(comp); + } +} + +pub fn flushModule(self: *Wasm, comp: *Compilation) !void { + const tracy = trace(@src()); + defer tracy.end(); + + const file = self.base.file.?; + const header_size = 5 + 1; + + // No need to rewrite the magic/version header + try file.setEndPos(@sizeOf(@TypeOf(spec.magic ++ spec.version))); + try file.seekTo(@sizeOf(@TypeOf(spec.magic ++ spec.version))); + + // Type section + { + const header_offset = try reserveVecSectionHeader(file); + for (self.funcs.items) |decl| { + try file.writeAll(decl.fn_link.wasm.?.functype.items); + } + try writeVecSectionHeader( + file, + header_offset, + spec.types_id, + @intCast(u32, (try file.getPos()) - header_offset - header_size), + @intCast(u32, self.funcs.items.len), + ); + } + + // Function section + { + const header_offset = try reserveVecSectionHeader(file); + const writer = file.writer(); + for (self.funcs.items) |_, typeidx| try leb.writeULEB128(writer, @intCast(u32, typeidx)); + try writeVecSectionHeader( + file, + header_offset, + spec.funcs_id, + @intCast(u32, (try file.getPos()) - header_offset - header_size), + @intCast(u32, self.funcs.items.len), + ); + } + + // Export section + if (self.base.options.module) |module| { + const header_offset = try reserveVecSectionHeader(file); + const writer = file.writer(); + var count: u32 = 0; + for (module.decl_exports.entries.items) |entry| { + for (entry.value) |exprt| { + // Export name length + name + try leb.writeULEB128(writer, @intCast(u32, exprt.options.name.len)); + try writer.writeAll(exprt.options.name); + + switch (exprt.exported_decl.typed_value.most_recent.typed_value.ty.zigTypeTag()) { + .Fn => { + // Type of the export + try writer.writeByte(0x00); + // Exported function index + try leb.writeULEB128(writer, self.getFuncidx(exprt.exported_decl).?); + }, + else => return error.TODOImplementNonFnDeclsForWasm, + } + + count += 1; + } + } + try writeVecSectionHeader( + file, + header_offset, + spec.exports_id, + @intCast(u32, (try file.getPos()) - header_offset - header_size), + count, + ); + } + + // Code section + { + const header_offset = try reserveVecSectionHeader(file); + const writer = file.writer(); + for (self.funcs.items) |decl| { + const fn_data = &decl.fn_link.wasm.?; + + // Write the already generated code to the file, inserting + // function indexes where required. + var current: u32 = 0; + for (fn_data.idx_refs.items) |idx_ref| { + try writer.writeAll(fn_data.code.items[current..idx_ref.offset]); + current = idx_ref.offset; + // Use a fixed width here to make calculating the code size + // in codegen.wasm.genCode() simpler. + var buf: [5]u8 = undefined; + leb.writeUnsignedFixed(5, &buf, self.getFuncidx(idx_ref.decl).?); + try writer.writeAll(&buf); + } + + try writer.writeAll(fn_data.code.items[current..]); + } + try writeVecSectionHeader( + file, + header_offset, + spec.code_id, + @intCast(u32, (try file.getPos()) - header_offset - header_size), + @intCast(u32, self.funcs.items.len), + ); + } +} + +fn linkWithLLD(self: *Wasm, comp: *Compilation) !void { + const tracy = trace(@src()); + defer tracy.end(); + + var arena_allocator = std.heap.ArenaAllocator.init(self.base.allocator); + defer arena_allocator.deinit(); + const arena = &arena_allocator.allocator; + + const directory = self.base.options.emit.?.directory; // Just an alias to make it shorter to type. + + // If there is no Zig code to compile, then we should skip flushing the output file because it + // will not be part of the linker line anyway. + const module_obj_path: ?[]const u8 = if (self.base.options.module) |module| blk: { + const use_stage1 = build_options.is_stage1 and self.base.options.use_llvm; + if (use_stage1) { + const obj_basename = try std.zig.binNameAlloc(arena, .{ + .root_name = self.base.options.root_name, + .target = self.base.options.target, + .output_mode = .Obj, + }); + const o_directory = self.base.options.module.?.zig_cache_artifact_directory; + const full_obj_path = try o_directory.join(arena, &[_][]const u8{obj_basename}); + break :blk full_obj_path; + } + + try self.flushModule(comp); + const obj_basename = self.base.intermediary_basename.?; + const full_obj_path = try directory.join(arena, &[_][]const u8{obj_basename}); + break :blk full_obj_path; + } else null; + + const target = self.base.options.target; + + const id_symlink_basename = "lld.id"; + + var man: Cache.Manifest = undefined; + defer if (!self.base.options.disable_lld_caching) man.deinit(); + + var digest: [Cache.hex_digest_len]u8 = undefined; + + if (!self.base.options.disable_lld_caching) { + man = comp.cache_parent.obtain(); + + // We are about to obtain this lock, so here we give other processes a chance first. + self.base.releaseLock(); + + try man.addListOfFiles(self.base.options.objects); + for (comp.c_object_table.items()) |entry| { + _ = try man.addFile(entry.key.status.success.object_path, null); + } + try man.addOptionalFile(module_obj_path); + man.hash.addOptional(self.base.options.stack_size_override); + man.hash.addListOfBytes(self.base.options.extra_lld_args); + + // We don't actually care whether it's a cache hit or miss; we just need the digest and the lock. + _ = try man.hit(); + digest = man.final(); + + var prev_digest_buf: [digest.len]u8 = undefined; + const prev_digest: []u8 = directory.handle.readLink(id_symlink_basename, &prev_digest_buf) catch |err| blk: { + log.debug("WASM LLD new_digest={} readlink error: {}", .{ digest, @errorName(err) }); + // Handle this as a cache miss. + break :blk prev_digest_buf[0..0]; + }; + if (mem.eql(u8, prev_digest, &digest)) { + log.debug("WASM LLD digest={} match - skipping invocation", .{digest}); + // Hot diggity dog! The output binary is already there. + self.base.lock = man.toOwnedLock(); + return; + } + log.debug("WASM LLD prev_digest={} new_digest={}", .{ prev_digest, digest }); + + // We are about to change the output file to be different, so we invalidate the build hash now. + directory.handle.deleteFile(id_symlink_basename) catch |err| switch (err) { + error.FileNotFound => {}, + else => |e| return e, + }; + } + + const is_obj = self.base.options.output_mode == .Obj; + + // Create an LLD command line and invoke it. + var argv = std.ArrayList([]const u8).init(self.base.allocator); + defer argv.deinit(); + // Even though we're calling LLD as a library it thinks the first argument is its own exe name. + try argv.append("lld"); + if (is_obj) { + try argv.append("-r"); + } + + try argv.append("-error-limit=0"); + + if (self.base.options.output_mode == .Exe) { + // Increase the default stack size to a more reasonable value of 1MB instead of + // the default of 1 Wasm page being 64KB, unless overriden by the user. + try argv.append("-z"); + const stack_size = self.base.options.stack_size_override orelse 1048576; + const arg = try std.fmt.allocPrint(arena, "stack-size={d}", .{stack_size}); + try argv.append(arg); + + // Put stack before globals so that stack overflow results in segfault immediately + // before corrupting globals. See https://github.com/ziglang/zig/issues/4496 + try argv.append("--stack-first"); + } else { + try argv.append("--no-entry"); // So lld doesn't look for _start. + try argv.append("--export-all"); + } + try argv.appendSlice(&[_][]const u8{ + "--allow-undefined", + "-o", + try directory.join(arena, &[_][]const u8{self.base.options.emit.?.sub_path}), + }); + + // Positional arguments to the linker such as object files. + try argv.appendSlice(self.base.options.objects); + + for (comp.c_object_table.items()) |entry| { + try argv.append(entry.key.status.success.object_path); + } + if (module_obj_path) |p| { + try argv.append(p); + } + + if (self.base.options.output_mode == .Exe and !self.base.options.is_compiler_rt_or_libc) { + if (!self.base.options.link_libc) { + try argv.append(comp.libc_static_lib.?.full_object_path); + } + try argv.append(comp.compiler_rt_static_lib.?.full_object_path); + } + + if (self.base.options.verbose_link) { + Compilation.dump_argv(argv.items); + } + + const new_argv = try arena.allocSentinel(?[*:0]const u8, argv.items.len, null); + for (argv.items) |arg, i| { + new_argv[i] = try arena.dupeZ(u8, arg); + } + + var stderr_context: LLDContext = .{ + .wasm = self, + .data = std.ArrayList(u8).init(self.base.allocator), + }; + defer stderr_context.data.deinit(); + var stdout_context: LLDContext = .{ + .wasm = self, + .data = std.ArrayList(u8).init(self.base.allocator), + }; + defer stdout_context.data.deinit(); + const llvm = @import("../llvm.zig"); + const ok = llvm.Link( + .Wasm, + new_argv.ptr, + new_argv.len, + append_diagnostic, + @ptrToInt(&stdout_context), + @ptrToInt(&stderr_context), + ); + if (stderr_context.oom or stdout_context.oom) return error.OutOfMemory; + if (stdout_context.data.items.len != 0) { + std.log.warn("unexpected LLD stdout: {}", .{stdout_context.data.items}); + } + if (!ok) { + // TODO parse this output and surface with the Compilation API rather than + // directly outputting to stderr here. + std.debug.print("{}", .{stderr_context.data.items}); + return error.LLDReportedFailure; + } + if (stderr_context.data.items.len != 0) { + std.log.warn("unexpected LLD stderr: {}", .{stderr_context.data.items}); + } + + if (!self.base.options.disable_lld_caching) { + // Update the dangling symlink with the digest. If it fails we can continue; it only + // means that the next invocation will have an unnecessary cache miss. + directory.handle.symLink(&digest, id_symlink_basename, .{}) catch |err| { + std.log.warn("failed to save linking hash digest symlink: {}", .{@errorName(err)}); + }; + // Again failure here only means an unnecessary cache miss. + man.writeManifest() catch |err| { + std.log.warn("failed to write cache manifest when linking: {}", .{@errorName(err)}); + }; + // We hang on to this lock so that the output file path can be used without + // other processes clobbering it. + self.base.lock = man.toOwnedLock(); + } +} + +const LLDContext = struct { + data: std.ArrayList(u8), + wasm: *Wasm, + oom: bool = false, +}; + +fn append_diagnostic(context: usize, ptr: [*]const u8, len: usize) callconv(.C) void { + const lld_context = @intToPtr(*LLDContext, context); + const msg = ptr[0..len]; + lld_context.data.appendSlice(msg) catch |err| switch (err) { + error.OutOfMemory => lld_context.oom = true, + }; +} + +/// Get the current index of a given Decl in the function list +/// TODO: we could maintain a hash map to potentially make this +fn getFuncidx(self: Wasm, decl: *Module.Decl) ?u32 { + return for (self.funcs.items) |func, idx| { + if (func == decl) break @intCast(u32, idx); + } else null; +} + +fn reserveVecSectionHeader(file: fs.File) !u64 { + // section id + fixed leb contents size + fixed leb vector length + const header_size = 1 + 5 + 5; + // TODO: this should be a single lseek(2) call, but fs.File does not + // currently provide a way to do this. + try file.seekBy(header_size); + return (try file.getPos()) - header_size; +} + +fn writeVecSectionHeader(file: fs.File, offset: u64, section: u8, size: u32, items: u32) !void { + var buf: [1 + 5 + 5]u8 = undefined; + buf[0] = section; + leb.writeUnsignedFixed(5, buf[1..6], size); + leb.writeUnsignedFixed(5, buf[6..], items); + try file.pwriteAll(&buf, offset); +} diff --git a/src/link/cbe.h b/src/link/cbe.h new file mode 100644 index 0000000000000000000000000000000000000000..854032227d1aed97b78f359c282efecb0f5f5e8b --- /dev/null +++ b/src/link/cbe.h @@ -0,0 +1,15 @@ +#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/link/msdos-stub.bin b/src/link/msdos-stub.bin new file mode 100644 index 0000000000000000000000000000000000000000..96ad91198f0de1eb25b9d9846c44706823dffa58 Binary files /dev/null and b/src/link/msdos-stub.bin differ diff --git a/src/list.hpp b/src/list.hpp deleted file mode 100644 index 803a2514371d17c9a228169eb38a2c5269b0bf6b..0000000000000000000000000000000000000000 --- a/src/list.hpp +++ /dev/null @@ -1,96 +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_LIST_HPP -#define ZIG_LIST_HPP - -#include "util.hpp" - -template -struct ZigList { - void deinit() { - heap::c_allocator.deallocate(items, capacity); - } - void append(const T& item) { - ensure_capacity(length + 1); - items[length++] = item; - } - void append_assuming_capacity(const T& item) { - items[length++] = item; - } - // remember that the pointer to this item is invalid after you - // modify the length of the list - const T & at(size_t index) const { - assert(index != SIZE_MAX); - assert(index < length); - return items[index]; - } - T & at(size_t index) { - assert(index != SIZE_MAX); - assert(index < length); - return items[index]; - } - T pop() { - assert(length >= 1); - return items[--length]; - } - - T *add_one() { - resize(length + 1); - return &last(); - } - - const T & last() const { - assert(length >= 1); - return items[length - 1]; - } - - T & last() { - assert(length >= 1); - return items[length - 1]; - } - - void resize(size_t new_length) { - assert(new_length != SIZE_MAX); - ensure_capacity(new_length); - length = new_length; - } - - void clear() { - length = 0; - } - - void ensure_capacity(size_t new_capacity) { - if (capacity >= new_capacity) - return; - - size_t better_capacity = capacity; - do { - better_capacity = better_capacity * 5 / 2 + 8; - } while (better_capacity < new_capacity); - - items = heap::c_allocator.reallocate_nonzero(items, capacity, better_capacity); - capacity = better_capacity; - } - - T swap_remove(size_t index) { - if (length - 1 == index) return pop(); - - assert(index != SIZE_MAX); - assert(index < length); - - T old_item = items[index]; - items[index] = pop(); - return old_item; - } - - T *items; - size_t length; - size_t capacity; -}; - -#endif diff --git a/src/liveness.zig b/src/liveness.zig new file mode 100644 index 0000000000000000000000000000000000000000..d528e09ce7b85cea0ed90f7919e1e8b7cc0a4866 --- /dev/null +++ b/src/liveness.zig @@ -0,0 +1,166 @@ +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/llvm.zig b/src/llvm.zig new file mode 100644 index 0000000000000000000000000000000000000000..3aebf46b8192bc530e00d501a9e94c7221f8ac08 --- /dev/null +++ b/src/llvm.zig @@ -0,0 +1,140 @@ +//! We do this instead of @cImport because the self-hosted compiler is easier +//! to bootstrap if it does not depend on translate-c. + +pub const Link = ZigLLDLink; +extern fn ZigLLDLink( + oformat: ObjectFormatType, + args: [*:null]const ?[*:0]const u8, + arg_count: usize, + append_diagnostic: fn (context: usize, ptr: [*]const u8, len: usize) callconv(.C) void, + context_stdout: usize, + context_stderr: usize, +) bool; + +pub const ObjectFormatType = extern enum(c_int) { + Unknown, + COFF, + ELF, + MachO, + Wasm, + XCOFF, +}; + +pub const GetHostCPUName = LLVMGetHostCPUName; +extern fn LLVMGetHostCPUName() ?[*:0]u8; + +pub const GetNativeFeatures = ZigLLVMGetNativeFeatures; +extern fn ZigLLVMGetNativeFeatures() ?[*:0]u8; + +pub const WriteArchive = ZigLLVMWriteArchive; +extern fn ZigLLVMWriteArchive( + archive_name: [*:0]const u8, + file_names_ptr: [*]const [*:0]const u8, + file_names_len: usize, + os_type: OSType, +) bool; + +pub const OSType = extern enum(c_int) { + UnknownOS = 0, + Ananas = 1, + CloudABI = 2, + Darwin = 3, + DragonFly = 4, + FreeBSD = 5, + Fuchsia = 6, + IOS = 7, + KFreeBSD = 8, + Linux = 9, + Lv2 = 10, + MacOSX = 11, + NetBSD = 12, + OpenBSD = 13, + Solaris = 14, + Win32 = 15, + Haiku = 16, + Minix = 17, + RTEMS = 18, + NaCl = 19, + CNK = 20, + AIX = 21, + CUDA = 22, + NVCL = 23, + AMDHSA = 24, + PS4 = 25, + ELFIAMCU = 26, + TvOS = 27, + WatchOS = 28, + Mesa3D = 29, + Contiki = 30, + AMDPAL = 31, + HermitCore = 32, + Hurd = 33, + WASI = 34, + Emscripten = 35, +}; + +pub const ArchType = extern enum(c_int) { + UnknownArch = 0, + arm = 1, + armeb = 2, + aarch64 = 3, + aarch64_be = 4, + aarch64_32 = 5, + arc = 6, + avr = 7, + bpfel = 8, + bpfeb = 9, + hexagon = 10, + mips = 11, + mipsel = 12, + mips64 = 13, + mips64el = 14, + msp430 = 15, + ppc = 16, + ppc64 = 17, + ppc64le = 18, + r600 = 19, + amdgcn = 20, + riscv32 = 21, + riscv64 = 22, + sparc = 23, + sparcv9 = 24, + sparcel = 25, + systemz = 26, + tce = 27, + tcele = 28, + thumb = 29, + thumbeb = 30, + x86 = 31, + x86_64 = 32, + xcore = 33, + nvptx = 34, + nvptx64 = 35, + le32 = 36, + le64 = 37, + amdil = 38, + amdil64 = 39, + hsail = 40, + hsail64 = 41, + spir = 42, + spir64 = 43, + kalimba = 44, + shave = 45, + lanai = 46, + wasm32 = 47, + wasm64 = 48, + renderscript32 = 49, + renderscript64 = 50, + ve = 51, +}; + +pub const ParseCommandLineOptions = ZigLLVMParseCommandLineOptions; +extern fn ZigLLVMParseCommandLineOptions(argc: usize, argv: [*]const [*:0]const u8) void; + +pub const WriteImportLibrary = ZigLLVMWriteImportLibrary; +extern fn ZigLLVMWriteImportLibrary( + def_path: [*:0]const u8, + arch: ArchType, + output_lib_path: [*c]const u8, + kill_at: bool, +) bool; diff --git a/src/main.cpp b/src/main.cpp deleted file mode 100644 index 348321598c60f6310d501f34cf08aae5a79bed11..0000000000000000000000000000000000000000 --- a/src/main.cpp +++ /dev/null @@ -1,1878 +0,0 @@ -/* - * Copyright (c) 2015 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#include "ast_render.hpp" -#include "buffer.hpp" -#include "codegen.hpp" -#include "compiler.hpp" -#include "config.h" -#include "error.hpp" -#include "heap.hpp" -#include "os.hpp" -#include "target.hpp" -#include "stage2.h" -#include "glibc.hpp" -#include "dump_analysis.hpp" -#include "mem_profile.hpp" - -#include - -static int print_error_usage(const char *arg0) { - fprintf(stderr, "See `%s --help` for detailed usage information\n", arg0); - return EXIT_FAILURE; -} - -static int print_full_usage(const char *arg0, FILE *file, int return_code) { - fprintf(file, - "Usage: %s [command] [options]\n" - "\n" - "Commands:\n" - " build build project from build.zig\n" - " build-exe [source] create executable from source or object files\n" - " build-lib [source] create library from source or object files\n" - " build-obj [source] create object from source or assembly\n" - " builtin show the source code of @import(\"builtin\")\n" - " cc use Zig as a drop-in C compiler\n" - " c++ use Zig as a drop-in C++ compiler\n" - " env print lib path, std path, compiler id and version\n" - " fmt parse files and render in canonical zig format\n" - " id print the base64-encoded compiler id\n" - " init-exe initialize a `zig build` application in the cwd\n" - " init-lib initialize a `zig build` library in the cwd\n" - " libc [paths_file] Display native libc paths file or validate one\n" - " run [source] [-- [args]] create executable and run immediately\n" - " translate-c [source] convert c code to zig code\n" - " targets list available compilation targets\n" - " test [source] create and run a test build\n" - " version print version number and exit\n" - " zen print zen of zig and exit\n" - "\n" - "Compile Options:\n" - " --c-source [options] [file] compile C source code\n" - " --cache-dir [path] override the local cache directory\n" - " --cache [auto|off|on] build in cache, print output path to stdout\n" - " --color [auto|off|on] enable or disable colored error messages\n" - " --disable-valgrind omit valgrind client requests in debug builds\n" - " --eh-frame-hdr enable C++ exception handling by passing --eh-frame-hdr to linker\n" - " --enable-valgrind include valgrind client requests release builds\n" - " -fstack-check enable stack probing in unsafe builds\n" - " -fno-stack-check disable stack probing in safe builds\n" - " -fsanitize-c enable C undefined behavior detection in unsafe builds\n" - " -fno-sanitize-c disable C undefined behavior detection in safe builds\n" - " --emit [asm|bin|llvm-ir] (deprecated) emit a specific file format as compilation output\n" - " -fPIC enable Position Independent Code\n" - " -fno-PIC disable Position Independent Code\n" - " -ftime-report print timing diagnostics\n" - " -fstack-report print stack size diagnostics\n" - " -fmem-report print memory usage diagnostics\n" - " -fdump-analysis write analysis.json file with type information\n" - " -femit-docs create a docs/ dir with html documentation\n" - " -fno-emit-docs do not produce docs/ dir with html documentation\n" - " -femit-bin (default) output machine code\n" - " -fno-emit-bin do not output machine code\n" - " -femit-asm output .s (assembly code)\n" - " -fno-emit-asm (default) do not output .s (assembly code)\n" - " -femit-llvm-ir produce a .ll file with LLVM IR\n" - " -fno-emit-llvm-ir (default) do not produce a .ll file with LLVM IR\n" - " -femit-h generate a C header file (.h)\n" - " -fno-emit-h (default) do not generate a C header file (.h)\n" - " --libc [file] Provide a file which specifies libc paths\n" - " --name [name] override output name\n" - " --output-dir [dir] override output directory (defaults to cwd)\n" - " --pkg-begin [name] [path] make pkg available to import and push current pkg\n" - " --pkg-end pop current pkg\n" - " --main-pkg-path set the directory of the root package\n" - " --release-fast build with optimizations on and safety off\n" - " --release-safe build with optimizations on and safety on\n" - " --release-small build with size optimizations on and safety off\n" - " --single-threaded source may assume it is only used single-threaded\n" - " -dynamic create a shared library (.so; .dll; .dylib)\n" - " --strip exclude debug symbols\n" - " -target [name] -- see the targets command\n" - " --verbose-tokenize enable compiler debug output for tokenization\n" - " --verbose-ast enable compiler debug output for AST parsing\n" - " --verbose-link enable compiler debug output for linking\n" - " --verbose-ir enable compiler debug output for Zig IR\n" - " --verbose-llvm-ir enable compiler debug output for LLVM IR\n" - " --verbose-cimport enable compiler debug output for C imports\n" - " --verbose-cc enable compiler debug output for C compilation\n" - " --verbose-llvm-cpu-features enable compiler debug output for LLVM CPU features\n" - " -dirafter [dir] add directory to AFTER include search path\n" - " -isystem [dir] add directory to SYSTEM include search path\n" - " -I[dir] add directory to include search path\n" - " -mllvm [arg] (unsupported) forward an arg to LLVM's option processing\n" - " --override-lib-dir [arg] override path to Zig lib directory\n" - " -ffunction-sections places each function in a separate section\n" - " -D[macro]=[value] define C [macro] to [value] (1 if [value] omitted)\n" - " -mcpu [cpu] specify target CPU and feature set\n" - " -code-model [default|tiny| set target code model\n" - " small|kernel|\n" - " medium|large]\n" - "\n" - "Link Options:\n" - " --bundle-compiler-rt for static libraries, include compiler-rt symbols\n" - " --dynamic-linker [path] set the path to ld.so\n" - " --each-lib-rpath add rpath for each used dynamic library\n" - " --library [lib] link against lib\n" - " --forbid-library [lib] make it an error to link against lib\n" - " --library-path [dir] add a directory to the library search path\n" - " --linker-script [path] use a custom linker script\n" - " --version-script [path] provide a version .map file\n" - " --object [obj] add object file to build\n" - " -L[dir] alias for --library-path\n" - " -l[lib] alias for --library\n" - " -rdynamic add all symbols to the dynamic symbol table\n" - " -rpath [path] add directory to the runtime library search path\n" - " --stack [size] (linux, windows, Wasm) override default stack size\n" - " --subsystem [subsystem] (windows) /SUBSYSTEM: to the linker\n" - " -F[dir] (darwin) add search path for frameworks\n" - " -framework [name] (darwin) link against framework\n" - " --ver-major [ver] dynamic library semver major version\n" - " --ver-minor [ver] dynamic library semver minor version\n" - " --ver-patch [ver] dynamic library semver patch version\n" - " -Bsymbolic bind global references locally\n" - "\n" - "Test Options:\n" - " --test-filter [text] skip tests that do not match filter\n" - " --test-name-prefix [text] add prefix to all tests\n" - " --test-cmd [arg] specify test execution command one arg at a time\n" - " --test-cmd-bin appends test binary path to test cmd args\n" - " --test-evented-io runs the test in evented I/O mode\n" - , arg0); - return return_code; -} - -static int print_libc_usage(const char *arg0, FILE *file, int return_code) { - fprintf(file, - "Usage: %s libc\n" - "\n" - "Detect the native libc installation and print the resulting paths to stdout.\n" - "You can save this into a file and then edit the paths to create a cross\n" - "compilation libc kit. Then you can pass `--libc [file]` for Zig to use it.\n" - "\n" - "When compiling natively and no `--libc` argument provided, Zig will create\n" - "`%s/native_libc.txt`\n" - "so that it does not have to detect libc on every invocation. You can remove\n" - "this file to have Zig re-detect the native libc.\n" - "\n\n" - "Usage: %s libc [file]\n" - "\n" - "Parse a libc installation text file and validate it.\n" - , arg0, buf_ptr(get_global_cache_dir()), arg0); - return return_code; -} - -enum Cmd { - CmdNone, - CmdBuild, - CmdBuiltin, - CmdRun, - CmdTargets, - CmdTest, - CmdTranslateC, - CmdVersion, - CmdZen, - CmdLibC, -}; - -static const char *default_zig_cache_name = "zig-cache"; - -struct CliPkg { - const char *name; - const char *path; - ZigList children; - CliPkg *parent; -}; - -static void add_package(CodeGen *g, CliPkg *cli_pkg, ZigPackage *pkg) { - for (size_t i = 0; i < cli_pkg->children.length; i += 1) { - CliPkg *child_cli_pkg = cli_pkg->children.at(i); - - Buf *dirname = buf_alloc(); - Buf *basename = buf_alloc(); - os_path_split(buf_create_from_str(child_cli_pkg->path), dirname, basename); - - ZigPackage *child_pkg = codegen_create_package(g, buf_ptr(dirname), buf_ptr(basename), - buf_ptr(buf_sprintf("%s.%s", buf_ptr(&pkg->pkg_path), child_cli_pkg->name))); - auto entry = pkg->package_table.put_unique(buf_create_from_str(child_cli_pkg->name), child_pkg); - if (entry) { - ZigPackage *existing_pkg = entry->value; - Buf *full_path = buf_alloc(); - os_path_join(&existing_pkg->root_src_dir, &existing_pkg->root_src_path, full_path); - fprintf(stderr, "Unable to add package '%s'->'%s': already exists as '%s'\n", - child_cli_pkg->name, child_cli_pkg->path, buf_ptr(full_path)); - exit(EXIT_FAILURE); - } - - add_package(g, child_cli_pkg, child_pkg); - } -} - -enum CacheOpt { - CacheOptAuto, - CacheOptOn, - CacheOptOff, -}; - -static bool get_cache_opt(CacheOpt opt, bool default_value) { - switch (opt) { - case CacheOptAuto: - return default_value; - case CacheOptOn: - return true; - case CacheOptOff: - return false; - } - zig_unreachable(); -} - -static int zig_error_no_build_file(void) { - fprintf(stderr, - "No 'build.zig' file found, in the current directory or any parent directories.\n" - "Initialize a 'build.zig' template file with `zig init-lib` or `zig init-exe`,\n" - "or see `zig --help` for more options.\n" - ); - return EXIT_FAILURE; -} - -static bool str_starts_with(const char *s1, const char *s2) { - size_t s2_len = strlen(s2); - if (strlen(s1) < s2_len) { - return false; - } - return memcmp(s1, s2, s2_len) == 0; -} - -extern "C" int ZigClang_main(int argc, char **argv); - -#ifdef ZIG_ENABLE_MEM_PROFILE -bool mem_report = false; -#endif - -int main_exit(Stage2ProgressNode *root_progress_node, int exit_code) { - if (root_progress_node != nullptr) { - stage2_progress_end(root_progress_node); - } - return exit_code; -} - -static int main0(int argc, char **argv) { - char *arg0 = argv[0]; - Error err; - - if (argc >= 2 && (strcmp(argv[1], "clang") == 0 || - strcmp(argv[1], "-cc1") == 0 || strcmp(argv[1], "-cc1as") == 0)) - { - return ZigClang_main(argc, argv); - } - - if (argc == 2 && strcmp(argv[1], "id") == 0) { - Buf *compiler_id; - if ((err = get_compiler_id(&compiler_id))) { - fprintf(stderr, "Unable to determine compiler id: %s\n", err_str(err)); - return EXIT_FAILURE; - } - printf("%s\n", buf_ptr(compiler_id)); - return EXIT_SUCCESS; - } - - enum InitKind { - InitKindNone, - InitKindExe, - InitKindLib, - }; - InitKind init_kind = InitKindNone; - if (argc >= 2) { - const char *init_cmd = argv[1]; - if (strcmp(init_cmd, "init-exe") == 0) { - init_kind = InitKindExe; - } else if (strcmp(init_cmd, "init-lib") == 0) { - init_kind = InitKindLib; - } - if (init_kind != InitKindNone) { - if (argc >= 3) { - fprintf(stderr, "Unexpected extra argument: %s\n", argv[2]); - return print_error_usage(arg0); - } - Buf *cmd_template_path = buf_alloc(); - os_path_join(get_zig_special_dir(get_zig_lib_dir()), buf_create_from_str(init_cmd), cmd_template_path); - Buf *build_zig_path = buf_alloc(); - os_path_join(cmd_template_path, buf_create_from_str("build.zig"), build_zig_path); - Buf *src_dir_path = buf_alloc(); - os_path_join(cmd_template_path, buf_create_from_str("src"), src_dir_path); - Buf *main_zig_path = buf_alloc(); - os_path_join(src_dir_path, buf_create_from_str("main.zig"), main_zig_path); - - Buf *cwd = buf_alloc(); - if ((err = os_get_cwd(cwd))) { - fprintf(stderr, "Unable to get cwd: %s\n", err_str(err)); - return EXIT_FAILURE; - } - Buf *cwd_basename = buf_alloc(); - os_path_split(cwd, nullptr, cwd_basename); - - Buf *build_zig_contents = buf_alloc(); - if ((err = os_fetch_file_path(build_zig_path, build_zig_contents))) { - fprintf(stderr, "Unable to read %s: %s\n", buf_ptr(build_zig_path), err_str(err)); - return EXIT_FAILURE; - } - Buf *modified_build_zig_contents = buf_alloc(); - for (size_t i = 0; i < buf_len(build_zig_contents); i += 1) { - char c = buf_ptr(build_zig_contents)[i]; - if (c == '$') { - buf_append_buf(modified_build_zig_contents, cwd_basename); - } else { - buf_append_char(modified_build_zig_contents, c); - } - } - - Buf *main_zig_contents = buf_alloc(); - if ((err = os_fetch_file_path(main_zig_path, main_zig_contents))) { - fprintf(stderr, "Unable to read %s: %s\n", buf_ptr(main_zig_path), err_str(err)); - return EXIT_FAILURE; - } - - Buf *out_build_zig_path = buf_create_from_str("build.zig"); - Buf *out_src_dir_path = buf_create_from_str("src"); - Buf *out_main_zig_path = buf_alloc(); - os_path_join(out_src_dir_path, buf_create_from_str("main.zig"), out_main_zig_path); - - bool already_exists; - if ((err = os_file_exists(out_build_zig_path, &already_exists))) { - fprintf(stderr, "Unable test existence of %s: %s\n", buf_ptr(out_build_zig_path), err_str(err)); - return EXIT_FAILURE; - } - if (already_exists) { - fprintf(stderr, "This file would be overwritten: %s\n", buf_ptr(out_build_zig_path)); - return EXIT_FAILURE; - } - - if ((err = os_make_dir(out_src_dir_path))) { - fprintf(stderr, "Unable to make directory: %s: %s\n", buf_ptr(out_src_dir_path), err_str(err)); - return EXIT_FAILURE; - } - if ((err = os_write_file(out_build_zig_path, modified_build_zig_contents))) { - fprintf(stderr, "Unable to write file: %s: %s\n", buf_ptr(out_build_zig_path), err_str(err)); - return EXIT_FAILURE; - } - if ((err = os_write_file(out_main_zig_path, main_zig_contents))) { - fprintf(stderr, "Unable to write file: %s: %s\n", buf_ptr(out_main_zig_path), err_str(err)); - return EXIT_FAILURE; - } - fprintf(stderr, "Created %s\n", buf_ptr(out_build_zig_path)); - fprintf(stderr, "Created %s\n", buf_ptr(out_main_zig_path)); - if (init_kind == InitKindExe) { - fprintf(stderr, "\nNext, try `zig build --help` or `zig build run`\n"); - } else if (init_kind == InitKindLib) { - fprintf(stderr, "\nNext, try `zig build --help` or `zig build test`\n"); - } else { - zig_unreachable(); - } - - return EXIT_SUCCESS; - } - } - - Cmd cmd = CmdNone; - const char *in_file = nullptr; - Buf *output_dir = nullptr; - bool strip = false; - bool is_dynamic = false; - OutType out_type = OutTypeUnknown; - const char *out_name = nullptr; - bool verbose_tokenize = false; - bool verbose_ast = false; - bool verbose_link = false; - bool verbose_ir = false; - bool verbose_llvm_ir = false; - bool verbose_cimport = false; - bool verbose_cc = false; - bool verbose_llvm_cpu_features = false; - bool link_eh_frame_hdr = false; - ErrColor color = ErrColorAuto; - CacheOpt enable_cache = CacheOptAuto; - const char *dynamic_linker = nullptr; - const char *libc_txt = nullptr; - ZigList clang_argv = {0}; - ZigList lib_dirs = {0}; - ZigList link_libs = {0}; - ZigList forbidden_link_libs = {0}; - ZigList framework_dirs = {0}; - ZigList frameworks = {0}; - bool have_libc = false; - bool have_libcpp = false; - const char *target_string = nullptr; - bool rdynamic = false; - const char *linker_script = nullptr; - Buf *version_script = nullptr; - ZigList rpath_list = {0}; - bool each_lib_rpath = false; - ZigList objects = {0}; - ZigList c_source_files = {0}; - const char *test_filter = nullptr; - const char *test_name_prefix = nullptr; - bool test_evented_io = false; - bool is_versioned = false; - size_t ver_major = 0; - size_t ver_minor = 0; - size_t ver_patch = 0; - bool timing_info = false; - bool stack_report = false; - bool enable_dump_analysis = false; - bool enable_doc_generation = false; - bool emit_bin = true; - const char *emit_bin_override_path = nullptr; - bool emit_asm = false; - bool emit_llvm_ir = false; - bool emit_h = false; - const char *cache_dir = nullptr; - CliPkg *cur_pkg = heap::c_allocator.create(); - BuildMode build_mode = BuildModeDebug; - ZigList test_exec_args = {0}; - int runtime_args_start = -1; - bool system_linker_hack = false; - TargetSubsystem subsystem = TargetSubsystemAuto; - bool want_single_threaded = false; - bool bundle_compiler_rt = false; - Buf *override_lib_dir = nullptr; - Buf *main_pkg_path = nullptr; - ValgrindSupport valgrind_support = ValgrindSupportAuto; - WantPIC want_pic = WantPICAuto; - WantStackCheck want_stack_check = WantStackCheckAuto; - WantCSanitize want_sanitize_c = WantCSanitizeAuto; - bool function_sections = false; - const char *mcpu = nullptr; - CodeModel code_model = CodeModelDefault; - const char *override_soname = nullptr; - bool only_pp_or_asm = false; - bool ensure_libc_on_non_freestanding = false; - bool ensure_libcpp_on_non_freestanding = false; - bool disable_c_depfile = false; - bool want_native_include_dirs = false; - Buf *linker_optimization = nullptr; - OptionalBool linker_gc_sections = OptionalBoolNull; - OptionalBool linker_allow_shlib_undefined = OptionalBoolNull; - OptionalBool linker_bind_global_refs_locally = OptionalBoolNull; - bool linker_z_nodelete = false; - bool linker_z_defs = false; - size_t stack_size_override = 0; - - ZigList llvm_argv = {0}; - llvm_argv.append("zig (LLVM option parsing)"); - - if (argc >= 2 && strcmp(argv[1], "build") == 0) { - Buf zig_exe_path_buf = BUF_INIT; - if ((err = os_self_exe_path(&zig_exe_path_buf))) { - fprintf(stderr, "Unable to determine path to zig's own executable\n"); - return EXIT_FAILURE; - } - const char *zig_exe_path = buf_ptr(&zig_exe_path_buf); - const char *build_file = nullptr; - - init_all_targets(); - - ZigList args = {0}; - args.append(NULL); // placeholder - args.append(zig_exe_path); - args.append(NULL); // placeholder - args.append(NULL); // placeholder - for (int i = 2; i < argc; i += 1) { - if (strcmp(argv[i], "--help") == 0) { - args.append(argv[i]); - } else if (i + 1 < argc && strcmp(argv[i], "--build-file") == 0) { - build_file = argv[i + 1]; - i += 1; - } else if (i + 1 < argc && strcmp(argv[i], "--cache-dir") == 0) { - cache_dir = argv[i + 1]; - i += 1; - } else if (i + 1 < argc && strcmp(argv[i], "--override-lib-dir") == 0) { - override_lib_dir = buf_create_from_str(argv[i + 1]); - i += 1; - - args.append("--override-lib-dir"); - args.append(buf_ptr(override_lib_dir)); - } else { - args.append(argv[i]); - } - } - - Buf *zig_lib_dir = (override_lib_dir == nullptr) ? get_zig_lib_dir() : override_lib_dir; - - Buf *build_runner_path = buf_alloc(); - os_path_join(get_zig_special_dir(zig_lib_dir), buf_create_from_str("build_runner.zig"), build_runner_path); - - ZigTarget target; - if ((err = target_parse_triple(&target, "native", nullptr, nullptr))) { - fprintf(stderr, "Unable to get native target: %s\n", err_str(err)); - return EXIT_FAILURE; - } - - Buf *build_file_buf = buf_create_from_str((build_file != nullptr) ? build_file : "build.zig"); - Buf build_file_abs = os_path_resolve(&build_file_buf, 1); - Buf build_file_basename = BUF_INIT; - Buf build_file_dirname = BUF_INIT; - os_path_split(&build_file_abs, &build_file_dirname, &build_file_basename); - - for (;;) { - bool build_file_exists; - if ((err = os_file_exists(&build_file_abs, &build_file_exists))) { - fprintf(stderr, "unable to check existence of '%s': %s\n", buf_ptr(&build_file_abs), err_str(err)); - return 1; - } - if (build_file_exists) - break; - - if (build_file != nullptr) { - // they asked for a specific build file path. only look for that one - return zig_error_no_build_file(); - } - - Buf *next_dir = buf_alloc(); - os_path_dirname(&build_file_dirname, next_dir); - if (buf_eql_buf(&build_file_dirname, next_dir)) { - // no more parent directories to search, give up - return zig_error_no_build_file(); - } - os_path_join(next_dir, &build_file_basename, &build_file_abs); - buf_init_from_buf(&build_file_dirname, next_dir); - } - - Buf full_cache_dir = BUF_INIT; - if (cache_dir == nullptr) { - os_path_join(&build_file_dirname, buf_create_from_str(default_zig_cache_name), &full_cache_dir); - } else { - Buf *cache_dir_buf = buf_create_from_str(cache_dir); - full_cache_dir = os_path_resolve(&cache_dir_buf, 1); - } - Stage2ProgressNode *root_progress_node = stage2_progress_start_root(stage2_progress_create(), "", 0, 0); - - CodeGen *g = codegen_create(main_pkg_path, build_runner_path, &target, OutTypeExe, - BuildModeDebug, override_lib_dir, nullptr, &full_cache_dir, false, root_progress_node); - g->valgrind_support = valgrind_support; - g->enable_time_report = timing_info; - codegen_set_out_name(g, buf_create_from_str("build")); - - args.items[2] = buf_ptr(&build_file_dirname); - args.items[3] = buf_ptr(&full_cache_dir); - - ZigPackage *build_pkg = codegen_create_package(g, buf_ptr(&build_file_dirname), - buf_ptr(&build_file_basename), "std.special"); - g->main_pkg->package_table.put(buf_create_from_str("@build"), build_pkg); - g->enable_cache = get_cache_opt(enable_cache, true); - codegen_build_and_link(g); - if (root_progress_node != nullptr) { - stage2_progress_end(root_progress_node); - root_progress_node = nullptr; - } - - Termination term; - args.items[0] = buf_ptr(&g->bin_file_output_path); - os_spawn_process(args, &term); - if (term.how != TerminationIdClean || term.code != 0) { - fprintf(stderr, "\nBuild failed. The following command failed:\n"); - const char *prefix = ""; - for (size_t i = 0; i < args.length; i += 1) { - fprintf(stderr, "%s%s", prefix, args.at(i)); - prefix = " "; - } - fprintf(stderr, "\n"); - } - return (term.how == TerminationIdClean) ? term.code : -1; - } else if (argc >= 2 && strcmp(argv[1], "fmt") == 0) { - return stage2_fmt(argc, argv); - } else if (argc >= 2 && strcmp(argv[1], "env") == 0) { - return stage2_env(argc, argv); - } else if (argc >= 2 && (strcmp(argv[1], "cc") == 0 || strcmp(argv[1], "c++") == 0)) { - emit_h = false; - strip = true; - ensure_libc_on_non_freestanding = true; - ensure_libcpp_on_non_freestanding = (strcmp(argv[1], "c++") == 0); - want_native_include_dirs = true; - - bool c_arg = false; - Stage2ClangArgIterator it; - stage2_clang_arg_iterator(&it, argc, argv); - bool is_shared_lib = false; - ZigList linker_args = {}; - while (it.has_next) { - if ((err = stage2_clang_arg_next(&it))) { - fprintf(stderr, "unable to parse command line parameters: %s\n", err_str(err)); - return EXIT_FAILURE; - } - switch (it.kind) { - case Stage2ClangArgTarget: // example: -target riscv64-linux-unknown - target_string = it.only_arg; - break; - case Stage2ClangArgO: // -o - emit_bin_override_path = it.only_arg; - enable_cache = CacheOptOn; - break; - case Stage2ClangArgC: // -c - c_arg = true; - break; - case Stage2ClangArgOther: - for (size_t i = 0; i < it.other_args_len; i += 1) { - clang_argv.append(it.other_args_ptr[i]); - } - break; - case Stage2ClangArgPositional: { - FileExt file_ext = classify_file_ext(it.only_arg, strlen(it.only_arg)); - switch (file_ext) { - case FileExtAsm: - case FileExtC: - case FileExtCpp: - case FileExtLLVMIr: - case FileExtLLVMBitCode: - case FileExtHeader: { - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = it.only_arg; - c_source_files.append(c_file); - break; - } - case FileExtUnknown: - objects.append(it.only_arg); - break; - } - break; - } - case Stage2ClangArgL: // -l - if (strcmp(it.only_arg, "c") == 0) { - have_libc = true; - link_libs.append("c"); - } else if (strcmp(it.only_arg, "c++") == 0 || - strcmp(it.only_arg, "stdc++") == 0) - { - have_libcpp = true; - link_libs.append("c++"); - } else { - link_libs.append(it.only_arg); - } - break; - case Stage2ClangArgIgnore: - break; - case Stage2ClangArgDriverPunt: - // Never mind what we're doing, just pass the args directly. For example --help. - return ZigClang_main(argc, argv); - case Stage2ClangArgPIC: - want_pic = WantPICEnabled; - break; - case Stage2ClangArgNoPIC: - want_pic = WantPICDisabled; - break; - case Stage2ClangArgNoStdLib: - ensure_libc_on_non_freestanding = false; - break; - case Stage2ClangArgNoStdLibCpp: - ensure_libcpp_on_non_freestanding = false; - break; - case Stage2ClangArgShared: - is_dynamic = true; - is_shared_lib = true; - break; - case Stage2ClangArgRDynamic: - rdynamic = true; - break; - case Stage2ClangArgWL: { - const char *arg = it.only_arg; - for (;;) { - size_t pos = 0; - while (arg[pos] != ',' && arg[pos] != 0) pos += 1; - linker_args.append(buf_create_from_mem(arg, pos)); - if (arg[pos] == 0) break; - arg += pos + 1; - } - break; - } - case Stage2ClangArgPreprocessOrAsm: - // this handles both -E and -S - only_pp_or_asm = true; - for (size_t i = 0; i < it.other_args_len; i += 1) { - clang_argv.append(it.other_args_ptr[i]); - } - break; - case Stage2ClangArgOptimize: - // alright what release mode do they want? - if (strcmp(it.only_arg, "Os") == 0) { - build_mode = BuildModeSmallRelease; - } else if (strcmp(it.only_arg, "O2") == 0 || - strcmp(it.only_arg, "O3") == 0 || - strcmp(it.only_arg, "O4") == 0) - { - build_mode = BuildModeFastRelease; - } else if (strcmp(it.only_arg, "Og") == 0 || - strcmp(it.only_arg, "O0") == 0) - { - build_mode = BuildModeDebug; - } else { - for (size_t i = 0; i < it.other_args_len; i += 1) { - clang_argv.append(it.other_args_ptr[i]); - } - } - break; - case Stage2ClangArgDebug: - strip = false; - if (strcmp(it.only_arg, "-g") == 0) { - // we handled with strip = false above - } else { - for (size_t i = 0; i < it.other_args_len; i += 1) { - clang_argv.append(it.other_args_ptr[i]); - } - } - break; - case Stage2ClangArgSanitize: - if (strcmp(it.only_arg, "undefined") == 0) { - want_sanitize_c = WantCSanitizeEnabled; - } else { - for (size_t i = 0; i < it.other_args_len; i += 1) { - clang_argv.append(it.other_args_ptr[i]); - } - } - break; - case Stage2ClangArgLinkerScript: - linker_script = it.only_arg; - break; - case Stage2ClangArgVerboseCmds: - verbose_cc = true; - verbose_link = true; - break; - case Stage2ClangArgForLinker: - linker_args.append(buf_create_from_str(it.only_arg)); - break; - case Stage2ClangArgLinkerInputZ: - linker_args.append(buf_create_from_str("-z")); - linker_args.append(buf_create_from_str(it.only_arg)); - break; - case Stage2ClangArgLibDir: - lib_dirs.append(it.only_arg); - break; - case Stage2ClangArgMCpu: - mcpu = it.only_arg; - break; - case Stage2ClangArgDepFile: - disable_c_depfile = true; - for (size_t i = 0; i < it.other_args_len; i += 1) { - clang_argv.append(it.other_args_ptr[i]); - } - break; - case Stage2ClangArgFrameworkDir: - framework_dirs.append(it.only_arg); - break; - case Stage2ClangArgFramework: - frameworks.append(it.only_arg); - break; - case Stage2ClangArgNoStdLibInc: - want_native_include_dirs = false; - break; - } - } - // Parse linker args - for (size_t i = 0; i < linker_args.length; i += 1) { - Buf *arg = linker_args.at(i); - if (buf_eql_str(arg, "-soname")) { - i += 1; - if (i >= linker_args.length) { - fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg)); - return EXIT_FAILURE; - } - Buf *soname_buf = linker_args.at(i); - override_soname = buf_ptr(soname_buf); - // use it as --name - // example: libsoundio.so.2 - size_t prefix = 0; - if (buf_starts_with_str(soname_buf, "lib")) { - prefix = 3; - } - size_t end = buf_len(soname_buf); - if (buf_ends_with_str(soname_buf, ".so")) { - end -= 3; - } else { - bool found_digit = false; - while (end > 0 && isdigit(buf_ptr(soname_buf)[end - 1])) { - found_digit = true; - end -= 1; - } - if (found_digit && end > 0 && buf_ptr(soname_buf)[end - 1] == '.') { - end -= 1; - } else { - end = buf_len(soname_buf); - } - if (buf_ends_with_str(buf_slice(soname_buf, prefix, end), ".so")) { - end -= 3; - } - } - out_name = buf_ptr(buf_slice(soname_buf, prefix, end)); - } else if (buf_eql_str(arg, "-rpath")) { - i += 1; - if (i >= linker_args.length) { - fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg)); - return EXIT_FAILURE; - } - Buf *rpath = linker_args.at(i); - rpath_list.append(buf_ptr(rpath)); - } else if (buf_eql_str(arg, "-I") || - buf_eql_str(arg, "--dynamic-linker") || - buf_eql_str(arg, "-dynamic-linker")) - { - i += 1; - if (i >= linker_args.length) { - fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg)); - return EXIT_FAILURE; - } - dynamic_linker = buf_ptr(linker_args.at(i)); - } else if (buf_eql_str(arg, "-E") || - buf_eql_str(arg, "--export-dynamic") || - buf_eql_str(arg, "-export-dynamic")) - { - rdynamic = true; - } else if (buf_eql_str(arg, "--version-script")) { - i += 1; - if (i >= linker_args.length) { - fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg)); - return EXIT_FAILURE; - } - version_script = linker_args.at(i); - } else if (buf_starts_with_str(arg, "-O")) { - linker_optimization = arg; - } else if (buf_eql_str(arg, "--gc-sections")) { - linker_gc_sections = OptionalBoolTrue; - } else if (buf_eql_str(arg, "--no-gc-sections")) { - linker_gc_sections = OptionalBoolFalse; - } else if (buf_eql_str(arg, "--allow-shlib-undefined") || - buf_eql_str(arg, "-allow-shlib-undefined")) - { - linker_allow_shlib_undefined = OptionalBoolTrue; - } else if (buf_eql_str(arg, "--no-allow-shlib-undefined") || - buf_eql_str(arg, "-no-allow-shlib-undefined")) - { - linker_allow_shlib_undefined = OptionalBoolFalse; - } else if (buf_eql_str(arg, "-Bsymbolic")) { - linker_bind_global_refs_locally = OptionalBoolTrue; - } else if (buf_eql_str(arg, "-z")) { - i += 1; - if (i >= linker_args.length) { - fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg)); - return EXIT_FAILURE; - } - Buf *z_arg = linker_args.at(i); - if (buf_eql_str(z_arg, "nodelete")) { - linker_z_nodelete = true; - } else if (buf_eql_str(z_arg, "defs")) { - linker_z_defs = true; - } else { - fprintf(stderr, "warning: unsupported linker arg: -z %s\n", buf_ptr(z_arg)); - } - } else if (buf_eql_str(arg, "--major-image-version")) { - i += 1; - if (i >= linker_args.length) { - fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg)); - return EXIT_FAILURE; - } - is_versioned = true; - ver_major = atoi(buf_ptr(linker_args.at(i))); - } else if (buf_eql_str(arg, "--minor-image-version")) { - i += 1; - if (i >= linker_args.length) { - fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg)); - return EXIT_FAILURE; - } - is_versioned = true; - ver_minor = atoi(buf_ptr(linker_args.at(i))); - } else if (buf_eql_str(arg, "--stack")) { - i += 1; - if (i >= linker_args.length) { - fprintf(stderr, "expected linker arg after '%s'\n", buf_ptr(arg)); - return EXIT_FAILURE; - } - stack_size_override = atoi(buf_ptr(linker_args.at(i))); - } else { - fprintf(stderr, "warning: unsupported linker arg: %s\n", buf_ptr(arg)); - } - } - - if (want_sanitize_c == WantCSanitizeEnabled && build_mode == BuildModeFastRelease) { - build_mode = BuildModeSafeRelease; - } - - if (only_pp_or_asm) { - cmd = CmdBuild; - out_type = OutTypeObj; - emit_bin = false; - // Transfer "objects" into c_source_files - for (size_t i = 0; i < objects.length; i += 1) { - CFile *c_file = heap::c_allocator.create(); - c_file->source_path = objects.at(i); - c_source_files.append(c_file); - } - for (size_t i = 0; i < c_source_files.length; i += 1) { - Buf *src_path; - if (emit_bin_override_path != nullptr) { - src_path = buf_create_from_str(emit_bin_override_path); - } else { - src_path = buf_create_from_str(c_source_files.at(i)->source_path); - } - Buf basename = BUF_INIT; - os_path_split(src_path, nullptr, &basename); - c_source_files.at(i)->preprocessor_only_basename = buf_ptr(&basename); - } - } else if (!c_arg) { - cmd = CmdBuild; - if (is_shared_lib) { - out_type = OutTypeLib; - } else { - out_type = OutTypeExe; - } - if (emit_bin_override_path == nullptr) { - emit_bin_override_path = "a.out"; - enable_cache = CacheOptOn; - } - } else { - cmd = CmdBuild; - out_type = OutTypeObj; - } - if (c_source_files.length == 0 && objects.length == 0) { - // For example `zig cc` and no args should print the "no input files" message. - return ZigClang_main(argc, argv); - } - } else for (int i = 1; i < argc; i += 1) { - char *arg = argv[i]; - - if (arg[0] == '-') { - if (strcmp(arg, "--") == 0) { - if (cmd == CmdRun) { - runtime_args_start = i + 1; - break; // rest of the args are for the program - } else { - fprintf(stderr, "Unexpected end-of-parameter mark: %s\n", arg); - } - } else if (strcmp(arg, "--release-fast") == 0) { - build_mode = BuildModeFastRelease; - } else if (strcmp(arg, "--release-safe") == 0) { - build_mode = BuildModeSafeRelease; - } else if (strcmp(arg, "--release-small") == 0) { - build_mode = BuildModeSmallRelease; - } else if (strcmp(arg, "--help") == 0) { - if (cmd == CmdLibC) { - return print_libc_usage(arg0, stdout, EXIT_SUCCESS); - } else { - return print_full_usage(arg0, stdout, EXIT_SUCCESS); - } - } else if (strcmp(arg, "--strip") == 0) { - strip = true; - } else if (strcmp(arg, "-dynamic") == 0) { - is_dynamic = true; - } else if (strcmp(arg, "--verbose-tokenize") == 0) { - verbose_tokenize = true; - } else if (strcmp(arg, "--verbose-ast") == 0) { - verbose_ast = true; - } else if (strcmp(arg, "--verbose-link") == 0) { - verbose_link = true; - } else if (strcmp(arg, "--verbose-ir") == 0) { - verbose_ir = true; - } else if (strcmp(arg, "--verbose-llvm-ir") == 0) { - verbose_llvm_ir = true; - } else if (strcmp(arg, "--verbose-cimport") == 0) { - verbose_cimport = true; - } else if (strcmp(arg, "--verbose-cc") == 0) { - verbose_cc = true; - } else if (strcmp(arg, "--verbose-llvm-cpu-features") == 0) { - verbose_llvm_cpu_features = true; - } else if (strcmp(arg, "-rdynamic") == 0) { - rdynamic = true; - } else if (strcmp(arg, "--each-lib-rpath") == 0) { - each_lib_rpath = true; - } else if (strcmp(arg, "-ftime-report") == 0) { - timing_info = true; - } else if (strcmp(arg, "-fstack-report") == 0) { - stack_report = true; - } else if (strcmp(arg, "-fmem-report") == 0) { -#ifdef ZIG_ENABLE_MEM_PROFILE - mem_report = true; - mem::report_print = true; -#else - fprintf(stderr, "-fmem-report requires configuring with -DZIG_ENABLE_MEM_PROFILE=ON\n"); - return print_error_usage(arg0); -#endif - } else if (strcmp(arg, "-fdump-analysis") == 0) { - enable_dump_analysis = true; - } else if (strcmp(arg, "-femit-docs") == 0) { - enable_doc_generation = true; - } else if (strcmp(arg, "--enable-valgrind") == 0) { - valgrind_support = ValgrindSupportEnabled; - } else if (strcmp(arg, "--disable-valgrind") == 0) { - valgrind_support = ValgrindSupportDisabled; - } else if (strcmp(arg, "--eh-frame-hdr") == 0) { - link_eh_frame_hdr = true; - } else if (strcmp(arg, "-fPIC") == 0) { - want_pic = WantPICEnabled; - } else if (strcmp(arg, "-fno-PIC") == 0) { - want_pic = WantPICDisabled; - } else if (strcmp(arg, "-fstack-check") == 0) { - want_stack_check = WantStackCheckEnabled; - } else if (strcmp(arg, "-fno-stack-check") == 0) { - want_stack_check = WantStackCheckDisabled; - } else if (strcmp(arg, "-fsanitize-c") == 0) { - want_sanitize_c = WantCSanitizeEnabled; - } else if (strcmp(arg, "-fno-sanitize-c") == 0) { - want_sanitize_c = WantCSanitizeDisabled; - } else if (strcmp(arg, "--system-linker-hack") == 0) { - system_linker_hack = true; - } else if (strcmp(arg, "--single-threaded") == 0) { - want_single_threaded = true;; - } else if (strcmp(arg, "--bundle-compiler-rt") == 0) { - bundle_compiler_rt = true; - } else if (strcmp(arg, "-Bsymbolic") == 0) { - linker_bind_global_refs_locally = OptionalBoolTrue; - } else if (strcmp(arg, "--test-cmd-bin") == 0) { - test_exec_args.append(nullptr); - } else if (arg[1] == 'D' && arg[2] != 0) { - clang_argv.append("-D"); - clang_argv.append(&arg[2]); - } else if (arg[1] == 'L' && arg[2] != 0) { - // alias for --library-path - lib_dirs.append(&arg[2]); - } else if (arg[1] == 'l' && arg[2] != 0) { - // alias for --library - const char *l = &arg[2]; - if (strcmp(l, "c") == 0) { - have_libc = true; - link_libs.append("c"); - } else if (strcmp(l, "c++") == 0 || strcmp(l, "stdc++") == 0) { - have_libcpp = true; - link_libs.append("c++"); - } else { - link_libs.append(l); - } - } else if (arg[1] == 'I' && arg[2] != 0) { - clang_argv.append("-I"); - clang_argv.append(&arg[2]); - } else if (arg[1] == 'F' && arg[2] != 0) { - framework_dirs.append(&arg[2]); - } else if (strcmp(arg, "--pkg-begin") == 0) { - if (i + 2 >= argc) { - fprintf(stderr, "Expected 2 arguments after --pkg-begin\n"); - return print_error_usage(arg0); - } - CliPkg *new_cur_pkg = heap::c_allocator.create(); - i += 1; - new_cur_pkg->name = argv[i]; - i += 1; - new_cur_pkg->path = argv[i]; - new_cur_pkg->parent = cur_pkg; - cur_pkg->children.append(new_cur_pkg); - cur_pkg = new_cur_pkg; - } else if (strcmp(arg, "--pkg-end") == 0) { - if (cur_pkg->parent == nullptr) { - fprintf(stderr, "Encountered --pkg-end with no matching --pkg-begin\n"); - return EXIT_FAILURE; - } - cur_pkg = cur_pkg->parent; - } else if (strcmp(arg, "-ffunction-sections") == 0) { - function_sections = true; - } else if (strcmp(arg, "--test-evented-io") == 0) { - test_evented_io = true; - } else if (strcmp(arg, "-femit-bin") == 0) { - emit_bin = true; - } else if (strcmp(arg, "-fno-emit-bin") == 0) { - emit_bin = false; - } else if (strcmp(arg, "-femit-asm") == 0) { - emit_asm = true; - } else if (strcmp(arg, "-fno-emit-asm") == 0) { - emit_asm = false; - } else if (strcmp(arg, "-femit-llvm-ir") == 0) { - emit_llvm_ir = true; - } else if (strcmp(arg, "-fno-emit-llvm-ir") == 0) { - emit_llvm_ir = false; - } else if (strcmp(arg, "-femit-h") == 0) { - emit_h = true; - } else if (strcmp(arg, "-fno-emit-h") == 0 || strcmp(arg, "--disable-gen-h") == 0) { - // the --disable-gen-h is there to support godbolt. once they upgrade to -fno-emit-h then we can remove this - emit_h = false; - } else if (str_starts_with(arg, "-mcpu=")) { - mcpu = arg + strlen("-mcpu="); - } else if (i + 1 >= argc) { - fprintf(stderr, "Expected another argument after %s\n", arg); - return print_error_usage(arg0); - } else { - i += 1; - if (strcmp(arg, "--output-dir") == 0) { - output_dir = buf_create_from_str(argv[i]); - } else if (strcmp(arg, "--color") == 0) { - if (strcmp(argv[i], "auto") == 0) { - color = ErrColorAuto; - } else if (strcmp(argv[i], "on") == 0) { - color = ErrColorOn; - } else if (strcmp(argv[i], "off") == 0) { - color = ErrColorOff; - } else { - fprintf(stderr, "--color options are 'auto', 'on', or 'off'\n"); - return print_error_usage(arg0); - } - } else if (strcmp(arg, "--cache") == 0) { - if (strcmp(argv[i], "auto") == 0) { - enable_cache = CacheOptAuto; - } else if (strcmp(argv[i], "on") == 0) { - enable_cache = CacheOptOn; - } else if (strcmp(argv[i], "off") == 0) { - enable_cache = CacheOptOff; - } else { - fprintf(stderr, "--cache options are 'auto', 'on', or 'off'\n"); - return print_error_usage(arg0); - } - } else if (strcmp(arg, "--emit") == 0) { - if (strcmp(argv[i], "asm") == 0) { - emit_asm = true; - emit_bin = false; - } else if (strcmp(argv[i], "bin") == 0) { - emit_bin = true; - } else if (strcmp(argv[i], "llvm-ir") == 0) { - emit_llvm_ir = true; - emit_bin = false; - } else { - fprintf(stderr, "--emit options are 'asm', 'bin', or 'llvm-ir'\n"); - return print_error_usage(arg0); - } - } else if (strcmp(arg, "--name") == 0) { - out_name = argv[i]; - } else if (strcmp(arg, "--dynamic-linker") == 0) { - dynamic_linker = argv[i]; - } else if (strcmp(arg, "--libc") == 0) { - libc_txt = argv[i]; - } else if (strcmp(arg, "-D") == 0) { - clang_argv.append("-D"); - clang_argv.append(argv[i]); - } else if (strcmp(arg, "-isystem") == 0) { - clang_argv.append("-isystem"); - clang_argv.append(argv[i]); - } else if (strcmp(arg, "-I") == 0) { - clang_argv.append("-I"); - clang_argv.append(argv[i]); - } else if (strcmp(arg, "-dirafter") == 0) { - clang_argv.append("-dirafter"); - clang_argv.append(argv[i]); - } else if (strcmp(arg, "-mllvm") == 0) { - clang_argv.append("-mllvm"); - clang_argv.append(argv[i]); - - llvm_argv.append(argv[i]); - } else if (strcmp(arg, "-code-model") == 0) { - if (strcmp(argv[i], "default") == 0) { - code_model = CodeModelDefault; - } else if (strcmp(argv[i], "tiny") == 0) { - code_model = CodeModelTiny; - } else if (strcmp(argv[i], "small") == 0) { - code_model = CodeModelSmall; - } else if (strcmp(argv[i], "kernel") == 0) { - code_model = CodeModelKernel; - } else if (strcmp(argv[i], "medium") == 0) { - code_model = CodeModelMedium; - } else if (strcmp(argv[i], "large") == 0) { - code_model = CodeModelLarge; - } else { - fprintf(stderr, "-code-model options are 'default', 'tiny', 'small', 'kernel', 'medium', or 'large'\n"); - return print_error_usage(arg0); - } - } else if (strcmp(arg, "--override-lib-dir") == 0) { - override_lib_dir = buf_create_from_str(argv[i]); - } else if (strcmp(arg, "--main-pkg-path") == 0) { - main_pkg_path = buf_create_from_str(argv[i]); - } else if (strcmp(arg, "--library-path") == 0 || strcmp(arg, "-L") == 0) { - lib_dirs.append(argv[i]); - } else if (strcmp(arg, "-F") == 0) { - framework_dirs.append(argv[i]); - } else if (strcmp(arg, "--library") == 0 || strcmp(arg, "-l") == 0) { - if (strcmp(argv[i], "c") == 0) { - have_libc = true; - link_libs.append("c"); - } else if (strcmp(argv[i], "c++") == 0 || strcmp(argv[i], "stdc++") == 0) { - have_libcpp = true; - link_libs.append("c++"); - } else { - link_libs.append(argv[i]); - } - } else if (strcmp(arg, "--forbid-library") == 0) { - forbidden_link_libs.append(argv[i]); - } else if (strcmp(arg, "--object") == 0) { - objects.append(argv[i]); - } else if (strcmp(arg, "--c-source") == 0) { - CFile *c_file = heap::c_allocator.create(); - for (;;) { - if (argv[i][0] == '-') { - c_file->args.append(argv[i]); - i += 1; - if (i < argc) { - continue; - } - - break; - } else { - c_file->source_path = argv[i]; - c_source_files.append(c_file); - break; - } - } - } else if (strcmp(arg, "--cache-dir") == 0) { - cache_dir = argv[i]; - } else if (strcmp(arg, "-target") == 0) { - target_string = argv[i]; - } else if (strcmp(arg, "-framework") == 0) { - frameworks.append(argv[i]); - } else if (strcmp(arg, "--linker-script") == 0) { - linker_script = argv[i]; - } else if (strcmp(arg, "--version-script") == 0) { - version_script = buf_create_from_str(argv[i]); - } else if (strcmp(arg, "-rpath") == 0) { - rpath_list.append(argv[i]); - } else if (strcmp(arg, "--test-filter") == 0) { - test_filter = argv[i]; - } else if (strcmp(arg, "--test-name-prefix") == 0) { - test_name_prefix = argv[i]; - } else if (strcmp(arg, "--ver-major") == 0) { - is_versioned = true; - ver_major = atoi(argv[i]); - } else if (strcmp(arg, "--ver-minor") == 0) { - is_versioned = true; - ver_minor = atoi(argv[i]); - } else if (strcmp(arg, "--ver-patch") == 0) { - is_versioned = true; - ver_patch = atoi(argv[i]); - } else if (strcmp(arg, "--test-cmd") == 0) { - test_exec_args.append(argv[i]); - } else if (strcmp(arg, "--stack") == 0) { - stack_size_override = atoi(argv[i]); - } else if (strcmp(arg, "--subsystem") == 0) { - if (strcmp(argv[i], "console") == 0) { - subsystem = TargetSubsystemConsole; - } else if (strcmp(argv[i], "windows") == 0) { - subsystem = TargetSubsystemWindows; - } else if (strcmp(argv[i], "posix") == 0) { - subsystem = TargetSubsystemPosix; - } else if (strcmp(argv[i], "native") == 0) { - subsystem = TargetSubsystemNative; - } else if (strcmp(argv[i], "efi_application") == 0) { - subsystem = TargetSubsystemEfiApplication; - } else if (strcmp(argv[i], "efi_boot_service_driver") == 0) { - subsystem = TargetSubsystemEfiBootServiceDriver; - } else if (strcmp(argv[i], "efi_rom") == 0) { - subsystem = TargetSubsystemEfiRom; - } else if (strcmp(argv[i], "efi_runtime_driver") == 0) { - subsystem = TargetSubsystemEfiRuntimeDriver; - } else { - fprintf(stderr, "invalid: --subsystem %s\n" - "Options are:\n" - " console\n" - " windows\n" - " posix\n" - " native\n" - " efi_application\n" - " efi_boot_service_driver\n" - " efi_rom\n" - " efi_runtime_driver\n" - , argv[i]); - return EXIT_FAILURE; - } - } else if (strcmp(arg, "-mcpu") == 0) { - mcpu = argv[i]; - } else { - fprintf(stderr, "Invalid argument: %s\n", arg); - return print_error_usage(arg0); - } - } - } else if (cmd == CmdNone) { - if (strcmp(arg, "build-exe") == 0) { - cmd = CmdBuild; - out_type = OutTypeExe; - } else if (strcmp(arg, "build-obj") == 0) { - cmd = CmdBuild; - out_type = OutTypeObj; - } else if (strcmp(arg, "build-lib") == 0) { - cmd = CmdBuild; - out_type = OutTypeLib; - } else if (strcmp(arg, "run") == 0) { - cmd = CmdRun; - out_type = OutTypeExe; - } else if (strcmp(arg, "version") == 0) { - cmd = CmdVersion; - } else if (strcmp(arg, "zen") == 0) { - cmd = CmdZen; - } else if (strcmp(arg, "libc") == 0) { - cmd = CmdLibC; - } else if (strcmp(arg, "translate-c") == 0) { - cmd = CmdTranslateC; - } else if (strcmp(arg, "test") == 0) { - cmd = CmdTest; - out_type = OutTypeExe; - } else if (strcmp(arg, "targets") == 0) { - cmd = CmdTargets; - } else if (strcmp(arg, "builtin") == 0) { - cmd = CmdBuiltin; - } else { - fprintf(stderr, "Unrecognized command: %s\n", arg); - return print_error_usage(arg0); - } - } else { - switch (cmd) { - case CmdBuild: - case CmdRun: - case CmdTranslateC: - case CmdTest: - case CmdLibC: - if (!in_file) { - in_file = arg; - } else { - fprintf(stderr, "Unexpected extra parameter: %s\n", arg); - return print_error_usage(arg0); - } - break; - case CmdBuiltin: - case CmdVersion: - case CmdZen: - case CmdTargets: - fprintf(stderr, "Unexpected extra parameter: %s\n", arg); - return print_error_usage(arg0); - case CmdNone: - zig_unreachable(); - } - } - } - - if (cur_pkg->parent != nullptr) { - fprintf(stderr, "Unmatched --pkg-begin\n"); - return EXIT_FAILURE; - } - - Stage2Progress *progress = stage2_progress_create(); - Stage2ProgressNode *root_progress_node = stage2_progress_start_root(progress, "", 0, 0); - if (color == ErrColorOff) stage2_progress_disable_tty(progress); - - init_all_targets(); - - ZigTarget target; - if ((err = target_parse_triple(&target, target_string, mcpu, dynamic_linker))) { - fprintf(stderr, "invalid target: %s\n" - "See `%s targets` to display valid targets.\n", err_str(err), arg0); - return print_error_usage(arg0); - } - - if (!have_libc && ensure_libc_on_non_freestanding && target.os != OsFreestanding) { - have_libc = true; - link_libs.append("c"); - } - if (!have_libcpp && ensure_libcpp_on_non_freestanding && target.os != OsFreestanding) { - have_libcpp = true; - link_libs.append("c++"); - } - - Buf zig_triple_buf = BUF_INIT; - target_triple_zig(&zig_triple_buf, &target); - - // If both output_dir and enable_cache are provided, and doing build-lib, we - // will just do a file copy at the end. This helps when bootstrapping zig from zig0 - // because we want to pass something like this: - // zig0 build-lib --cache on --output-dir ${CMAKE_BINARY_DIR} - // And we don't have access to `zig0 build` because that would require detecting native libc - // on systems where we are not able to build a libc from source for them. - // But that's the only reason this works, so otherwise we give an error here. - Buf *final_output_dir_step = nullptr; - if (output_dir != nullptr && enable_cache == CacheOptOn) { - if (cmd == CmdBuild && out_type == OutTypeLib) { - final_output_dir_step = output_dir; - output_dir = nullptr; - } else { - fprintf(stderr, "`--output-dir` is incompatible with --cache on.\n"); - return print_error_usage(arg0); - } - } - - if (target_requires_pic(&target, have_libc) && want_pic == WantPICDisabled) { - fprintf(stderr, "`--disable-pic` is incompatible with target '%s'\n", buf_ptr(&zig_triple_buf)); - return print_error_usage(arg0); - } - - if ((emit_asm || emit_llvm_ir) && in_file == nullptr) { - fprintf(stderr, "A root source file is required when using `-femit-asm` or `-femit-llvm-ir`\n"); - return print_error_usage(arg0); - } - - if (llvm_argv.length > 1) { - llvm_argv.append(nullptr); - ZigLLVMParseCommandLineOptions(llvm_argv.length - 1, llvm_argv.items); - } - - switch (cmd) { - case CmdLibC: { - if (in_file) { - Stage2LibCInstallation libc; - if ((err = stage2_libc_parse(&libc, in_file))) { - fprintf(stderr, "unable to parse libc file: %s\n", err_str(err)); - return main_exit(root_progress_node, EXIT_FAILURE); - } - return main_exit(root_progress_node, EXIT_SUCCESS); - } - Stage2LibCInstallation libc; - if ((err = stage2_libc_find_native(&libc))) { - fprintf(stderr, "unable to find native libc file: %s\n", err_str(err)); - return main_exit(root_progress_node, EXIT_FAILURE); - } - if ((err = stage2_libc_render(&libc, stdout))) { - fprintf(stderr, "unable to print libc file: %s\n", err_str(err)); - return main_exit(root_progress_node, EXIT_FAILURE); - } - return main_exit(root_progress_node, EXIT_SUCCESS); - } - case CmdBuiltin: { - CodeGen *g = codegen_create(main_pkg_path, nullptr, &target, - out_type, build_mode, override_lib_dir, nullptr, nullptr, false, root_progress_node); - codegen_set_strip(g, strip); - for (size_t i = 0; i < link_libs.length; i += 1) { - LinkLib *link_lib = codegen_add_link_lib(g, buf_create_from_str(link_libs.at(i))); - link_lib->provided_explicitly = true; - } - g->subsystem = subsystem; - g->valgrind_support = valgrind_support; - g->want_pic = want_pic; - g->want_stack_check = want_stack_check; - g->want_sanitize_c = want_sanitize_c; - g->want_single_threaded = want_single_threaded; - g->test_is_evented = test_evented_io; - Buf *builtin_source = codegen_generate_builtin_source(g); - if (fwrite(buf_ptr(builtin_source), 1, buf_len(builtin_source), stdout) != buf_len(builtin_source)) { - fprintf(stderr, "unable to write to stdout: %s\n", strerror(ferror(stdout))); - return main_exit(root_progress_node, EXIT_FAILURE); - } - return main_exit(root_progress_node, EXIT_SUCCESS); - } - case CmdRun: - case CmdBuild: - case CmdTranslateC: - case CmdTest: - { - if (cmd == CmdBuild && !in_file && objects.length == 0 && - c_source_files.length == 0) - { - fprintf(stderr, - "Expected at least one of these things:\n" - " * Zig root source file argument\n" - " * --object argument\n" - " * --c-source argument\n"); - return print_error_usage(arg0); - } else if ((cmd == CmdTranslateC || - cmd == CmdTest || cmd == CmdRun) && !in_file) - { - fprintf(stderr, "Expected source file argument.\n"); - return print_error_usage(arg0); - } else if (cmd == CmdRun && !emit_bin) { - fprintf(stderr, "Cannot run without emitting a binary file.\n"); - return print_error_usage(arg0); - } - - bool any_system_lib_dependencies = false; - for (size_t i = 0; i < link_libs.length; i += 1) { - if (!target_is_libc_lib_name(&target, link_libs.at(i)) && - !target_is_libcpp_lib_name(&target, link_libs.at(i))) - { - any_system_lib_dependencies = true; - break; - } - } - - if (target.is_native_os && (any_system_lib_dependencies || want_native_include_dirs)) { - Error err; - Stage2NativePaths paths; - if ((err = stage2_detect_native_paths(&paths))) { - fprintf(stderr, "unable to detect native system paths: %s\n", err_str(err)); - exit(1); - } - for (size_t i = 0; i < paths.warnings_len; i += 1) { - const char *warning = paths.warnings_ptr[i]; - fprintf(stderr, "warning: %s\n", warning); - } - for (size_t i = 0; i < paths.include_dirs_len; i += 1) { - const char *include_dir = paths.include_dirs_ptr[i]; - clang_argv.append("-isystem"); - clang_argv.append(include_dir); - } - for (size_t i = 0; i < paths.lib_dirs_len; i += 1) { - const char *lib_dir = paths.lib_dirs_ptr[i]; - lib_dirs.append(lib_dir); - } - for (size_t i = 0; i < paths.rpaths_len; i += 1) { - const char *rpath = paths.rpaths_ptr[i]; - rpath_list.append(rpath); - } - } - - - assert(cmd != CmdBuild || out_type != OutTypeUnknown); - - bool need_name = (cmd == CmdBuild || cmd == CmdTranslateC); - - if (cmd == CmdRun) { - out_name = "run"; - } - - Buf *in_file_buf = nullptr; - - Buf *buf_out_name = (cmd == CmdTest) ? buf_create_from_str("test") : - (out_name == nullptr) ? nullptr : buf_create_from_str(out_name); - - if (in_file) { - in_file_buf = buf_create_from_str(in_file); - - if (need_name && buf_out_name == nullptr) { - Buf basename = BUF_INIT; - os_path_split(in_file_buf, nullptr, &basename); - buf_out_name = buf_alloc(); - os_path_extname(&basename, buf_out_name, nullptr); - } - } - - if (need_name && buf_out_name == nullptr && c_source_files.length == 1) { - Buf basename = BUF_INIT; - os_path_split(buf_create_from_str(c_source_files.at(0)->source_path), nullptr, &basename); - buf_out_name = buf_alloc(); - os_path_extname(&basename, buf_out_name, nullptr); - } - if (need_name && buf_out_name == nullptr && objects.length == 1) { - Buf basename = BUF_INIT; - os_path_split(buf_create_from_str(objects.at(0)), nullptr, &basename); - buf_out_name = buf_alloc(); - os_path_extname(&basename, buf_out_name, nullptr); - } - if (need_name && buf_out_name == nullptr && emit_bin_override_path != nullptr) { - Buf basename = BUF_INIT; - os_path_split(buf_create_from_str(emit_bin_override_path), nullptr, &basename); - buf_out_name = buf_alloc(); - os_path_extname(&basename, buf_out_name, nullptr); - } - - if (need_name && buf_out_name == nullptr) { - fprintf(stderr, "--name [name] not provided and unable to infer\n\n"); - return print_error_usage(arg0); - } - - Buf *zig_root_source_file = (cmd == CmdTranslateC) ? nullptr : in_file_buf; - - if (cmd == CmdRun && buf_out_name == nullptr) { - buf_out_name = buf_create_from_str("run"); - } - Stage2LibCInstallation *libc = nullptr; - if (libc_txt != nullptr) { - libc = heap::c_allocator.create(); - if ((err = stage2_libc_parse(libc, libc_txt))) { - fprintf(stderr, "Unable to parse --libc text file: %s\n", err_str(err)); - return main_exit(root_progress_node, EXIT_FAILURE); - } - } - Buf *cache_dir_buf; - if (cache_dir == nullptr) { - if (cmd == CmdRun) { - cache_dir_buf = get_global_cache_dir(); - } else { - cache_dir_buf = buf_create_from_str(default_zig_cache_name); - } - } else { - cache_dir_buf = buf_create_from_str(cache_dir); - } - CodeGen *g = codegen_create(main_pkg_path, zig_root_source_file, &target, out_type, build_mode, - override_lib_dir, libc, cache_dir_buf, cmd == CmdTest, root_progress_node); - if (llvm_argv.length >= 2) codegen_set_llvm_argv(g, llvm_argv.items + 1, llvm_argv.length - 2); - g->valgrind_support = valgrind_support; - g->link_eh_frame_hdr = link_eh_frame_hdr; - g->want_pic = want_pic; - g->want_stack_check = want_stack_check; - g->want_sanitize_c = want_sanitize_c; - g->subsystem = subsystem; - - g->enable_time_report = timing_info; - g->enable_stack_report = stack_report; - g->enable_dump_analysis = enable_dump_analysis; - g->enable_doc_generation = enable_doc_generation; - g->emit_bin = emit_bin; - g->emit_asm = emit_asm; - g->emit_llvm_ir = emit_llvm_ir; - - codegen_set_out_name(g, buf_out_name); - codegen_set_lib_version(g, is_versioned, ver_major, ver_minor, ver_patch); - g->want_single_threaded = want_single_threaded; - codegen_set_linker_script(g, linker_script); - g->version_script_path = version_script; - if (each_lib_rpath) - codegen_set_each_lib_rpath(g, each_lib_rpath); - - codegen_set_clang_argv(g, clang_argv.items, clang_argv.length); - - codegen_set_strip(g, strip); - g->is_dynamic = is_dynamic; - g->verbose_tokenize = verbose_tokenize; - g->verbose_ast = verbose_ast; - g->verbose_link = verbose_link; - g->verbose_ir = verbose_ir; - g->verbose_llvm_ir = verbose_llvm_ir; - g->verbose_cimport = verbose_cimport; - g->verbose_cc = verbose_cc; - g->verbose_llvm_cpu_features = verbose_llvm_cpu_features; - g->output_dir = output_dir; - g->disable_gen_h = !emit_h; - g->bundle_compiler_rt = bundle_compiler_rt; - codegen_set_errmsg_color(g, color); - g->system_linker_hack = system_linker_hack; - g->function_sections = function_sections; - g->code_model = code_model; - g->disable_c_depfile = disable_c_depfile; - - g->linker_optimization = linker_optimization; - g->linker_gc_sections = linker_gc_sections; - g->linker_allow_shlib_undefined = linker_allow_shlib_undefined; - g->linker_bind_global_refs_locally = linker_bind_global_refs_locally; - g->linker_z_nodelete = linker_z_nodelete; - g->linker_z_defs = linker_z_defs; - g->stack_size_override = stack_size_override; - - if (override_soname) { - g->override_soname = buf_create_from_str(override_soname); - } - - for (size_t i = 0; i < lib_dirs.length; i += 1) { - codegen_add_lib_dir(g, lib_dirs.at(i)); - } - for (size_t i = 0; i < framework_dirs.length; i += 1) { - g->framework_dirs.append(framework_dirs.at(i)); - } - for (size_t i = 0; i < link_libs.length; i += 1) { - LinkLib *link_lib = codegen_add_link_lib(g, buf_create_from_str(link_libs.at(i))); - link_lib->provided_explicitly = true; - } - for (size_t i = 0; i < forbidden_link_libs.length; i += 1) { - Buf *forbidden_link_lib = buf_create_from_str(forbidden_link_libs.at(i)); - codegen_add_forbidden_lib(g, forbidden_link_lib); - } - for (size_t i = 0; i < frameworks.length; i += 1) { - codegen_add_framework(g, frameworks.at(i)); - } - for (size_t i = 0; i < rpath_list.length; i += 1) { - codegen_add_rpath(g, rpath_list.at(i)); - } - - codegen_set_rdynamic(g, rdynamic); - - if (test_filter) { - codegen_set_test_filter(g, buf_create_from_str(test_filter)); - } - g->test_is_evented = test_evented_io; - - if (test_name_prefix) { - codegen_set_test_name_prefix(g, buf_create_from_str(test_name_prefix)); - } - - add_package(g, cur_pkg, g->main_pkg); - - if (cmd == CmdBuild || cmd == CmdRun || cmd == CmdTest) { - g->c_source_files = c_source_files; - for (size_t i = 0; i < objects.length; i += 1) { - codegen_add_object(g, buf_create_from_str(objects.at(i))); - } - } - - - if (cmd == CmdBuild || cmd == CmdRun) { - g->enable_cache = get_cache_opt(enable_cache, cmd == CmdRun); - codegen_build_and_link(g); - if (root_progress_node != nullptr) { - stage2_progress_end(root_progress_node); - root_progress_node = nullptr; - } - if (timing_info) - codegen_print_timing_report(g, stdout); - if (stack_report) - zig_print_stack_report(g, stdout); - - if (cmd == CmdRun) { -#ifdef ZIG_ENABLE_MEM_PROFILE - if (mem::report_print) - mem::print_report(); -#endif - - const char *exec_path = buf_ptr(&g->bin_file_output_path); - ZigList args = {0}; - - args.append(exec_path); - if (runtime_args_start != -1) { - for (int i = runtime_args_start; i < argc; ++i) { - args.append(argv[i]); - } - } - args.append(nullptr); - - os_execv(exec_path, args.items); - - args.pop(); - Termination term; - os_spawn_process(args, &term); - return term.code; - } else if (cmd == CmdBuild) { - if (emit_bin_override_path != nullptr) { -#if defined(ZIG_OS_WINDOWS) - buf_replace(g->output_dir, '/', '\\'); -#endif - Buf *dest_path = buf_create_from_str(emit_bin_override_path); - Buf *source_path; - if (only_pp_or_asm) { - source_path = buf_alloc(); - Buf *pp_only_basename = buf_create_from_str( - c_source_files.at(0)->preprocessor_only_basename); - os_path_join(g->output_dir, pp_only_basename, source_path); - - } else { - source_path = &g->bin_file_output_path; - } - if ((err = os_update_file(source_path, dest_path))) { - fprintf(stderr, "unable to copy %s to %s: %s\n", buf_ptr(source_path), - buf_ptr(dest_path), err_str(err)); - return main_exit(root_progress_node, EXIT_FAILURE); - } - } else if (only_pp_or_asm) { -#if defined(ZIG_OS_WINDOWS) - buf_replace(g->c_artifact_dir, '/', '\\'); -#endif - // dump the preprocessed output to stdout - for (size_t i = 0; i < c_source_files.length; i += 1) { - Buf *source_path = buf_alloc(); - Buf *pp_only_basename = buf_create_from_str( - c_source_files.at(i)->preprocessor_only_basename); - os_path_join(g->c_artifact_dir, pp_only_basename, source_path); - if ((err = os_dump_file(source_path, stdout))) { - fprintf(stderr, "unable to read %s: %s\n", buf_ptr(source_path), - err_str(err)); - return main_exit(root_progress_node, EXIT_FAILURE); - } - } - } else if (g->enable_cache) { -#if defined(ZIG_OS_WINDOWS) - buf_replace(&g->bin_file_output_path, '/', '\\'); - buf_replace(g->output_dir, '/', '\\'); -#endif - if (final_output_dir_step != nullptr) { - Buf *dest_basename = buf_alloc(); - os_path_split(&g->bin_file_output_path, nullptr, dest_basename); - Buf *dest_path = buf_alloc(); - os_path_join(final_output_dir_step, dest_basename, dest_path); - - if ((err = os_update_file(&g->bin_file_output_path, dest_path))) { - fprintf(stderr, "unable to copy %s to %s: %s\n", buf_ptr(&g->bin_file_output_path), - buf_ptr(dest_path), err_str(err)); - return main_exit(root_progress_node, EXIT_FAILURE); - } - } else { - if (printf("%s\n", buf_ptr(g->output_dir)) < 0) - return main_exit(root_progress_node, EXIT_FAILURE); - } - } - return main_exit(root_progress_node, EXIT_SUCCESS); - } else { - zig_unreachable(); - } - } else if (cmd == CmdTranslateC) { - g->enable_cache = get_cache_opt(enable_cache, false); - codegen_translate_c(g, in_file_buf); - if (timing_info) - codegen_print_timing_report(g, stderr); - return main_exit(root_progress_node, EXIT_SUCCESS); - } else if (cmd == CmdTest) { - ZigTarget native; - if ((err = target_parse_triple(&native, "native", nullptr, nullptr))) { - fprintf(stderr, "Unable to get native target: %s\n", err_str(err)); - return EXIT_FAILURE; - } - - g->enable_cache = get_cache_opt(enable_cache, output_dir == nullptr); - codegen_build_and_link(g); - if (root_progress_node != nullptr) { - stage2_progress_end(root_progress_node); - root_progress_node = nullptr; - } - - if (timing_info) { - codegen_print_timing_report(g, stdout); - } - - if (stack_report) { - zig_print_stack_report(g, stdout); - } - - if (!g->emit_bin) { - fprintf(stderr, "Semantic analysis complete. No binary produced due to -fno-emit-bin.\n"); - return main_exit(root_progress_node, EXIT_SUCCESS); - } - - Buf *test_exe_path_unresolved = &g->bin_file_output_path; - Buf *test_exe_path = buf_alloc(); - *test_exe_path = os_path_resolve(&test_exe_path_unresolved, 1); - - if (!g->emit_bin) { - fprintf(stderr, "Created %s but skipping execution because no binary generated.\n", - buf_ptr(test_exe_path)); - return main_exit(root_progress_node, EXIT_SUCCESS); - } - - for (size_t i = 0; i < test_exec_args.length; i += 1) { - if (test_exec_args.items[i] == nullptr) { - test_exec_args.items[i] = buf_ptr(test_exe_path); - } - } - - if (!target_can_exec(&native, &target) && test_exec_args.length == 0) { - fprintf(stderr, "Created %s but skipping execution because it is non-native.\n", - buf_ptr(test_exe_path)); - return main_exit(root_progress_node, EXIT_SUCCESS); - } - - Termination term; - if (test_exec_args.length == 0) { - test_exec_args.append(buf_ptr(test_exe_path)); - } - os_spawn_process(test_exec_args, &term); - if (term.how != TerminationIdClean || term.code != 0) { - fprintf(stderr, "\nTests failed. Use the following command to reproduce the failure:\n"); - fprintf(stderr, "%s\n", buf_ptr(test_exe_path)); - } - return main_exit(root_progress_node, (term.how == TerminationIdClean) ? term.code : -1); - } else { - zig_unreachable(); - } - } - case CmdVersion: - printf("%s\n", ZIG_VERSION_STRING); - return main_exit(root_progress_node, EXIT_SUCCESS); - case CmdZen: { - const char *ptr; - size_t len; - stage2_zen(&ptr, &len); - fwrite(ptr, len, 1, stdout); - return main_exit(root_progress_node, EXIT_SUCCESS); - } - case CmdTargets: - return stage2_cmd_targets(target_string, mcpu, dynamic_linker); - case CmdNone: - return print_full_usage(arg0, stderr, EXIT_FAILURE); - } - zig_unreachable(); -} - -int main(int argc, char **argv) { - stage2_attach_segfault_handler(); - os_init(); - mem::init(); - - auto result = main0(argc, argv); - -#ifdef ZIG_ENABLE_MEM_PROFILE - if (mem::report_print) - mem::intern_counters.print_report(); -#endif - mem::deinit(); - return result; -} diff --git a/src/main.zig b/src/main.zig new file mode 100644 index 0000000000000000000000000000000000000000..d421322c1739caf68571159c424e481ececfbc74 --- /dev/null +++ b/src/main.zig @@ -0,0 +1,3039 @@ +const std = @import("std"); +const assert = std.debug.assert; +const io = std.io; +const fs = std.fs; +const mem = std.mem; +const process = std.process; +const Allocator = mem.Allocator; +const ArrayList = std.ArrayList; +const ast = std.zig.ast; +const warn = std.log.warn; + +const Compilation = @import("Compilation.zig"); +const link = @import("link.zig"); +const Package = @import("Package.zig"); +const zir = @import("zir.zig"); +const build_options = @import("build_options"); +const introspect = @import("introspect.zig"); +const LibCInstallation = @import("libc_installation.zig").LibCInstallation; +const translate_c = @import("translate_c.zig"); +const Cache = @import("Cache.zig"); +const target_util = @import("target.zig"); + +pub fn fatal(comptime format: []const u8, args: anytype) noreturn { + std.log.emerg(format, args); + process.exit(1); +} + +pub const max_src_size = 2 * 1024 * 1024 * 1024; // 2 GiB + +pub const Color = enum { + Auto, + Off, + On, +}; + +const usage = + \\Usage: zig [command] [options] + \\ + \\Commands: + \\ + \\ build Build project from build.zig + \\ build-exe Create executable from source or object files + \\ build-lib Create library from source or object files + \\ build-obj Create object from source or assembly + \\ cc Use Zig as a drop-in C compiler + \\ c++ Use Zig as a drop-in C++ compiler + \\ env Print lib path, std path, compiler id and version + \\ fmt Parse file and render in canonical zig format + \\ init-exe Initialize a `zig build` application in the cwd + \\ init-lib Initialize a `zig build` library in the cwd + \\ libc Display native libc paths file or validate one + \\ run Create executable and run immediately + \\ translate-c Convert C code to Zig code + \\ targets List available compilation targets + \\ test Create and run a test build + \\ version Print version number and exit + \\ zen Print zen of zig and exit + \\ + \\General Options: + \\ + \\ --help Print command-specific usage + \\ +; + +pub const log_level: std.log.Level = switch (std.builtin.mode) { + .Debug => .debug, + .ReleaseSafe, .ReleaseFast => .info, + .ReleaseSmall => .crit, +}; + +pub fn log( + comptime level: std.log.Level, + comptime scope: @TypeOf(.EnumLiteral), + comptime format: []const u8, + args: anytype, +) void { + // Hide debug messages unless added with `-Dlog=foo`. + if (@enumToInt(level) > @enumToInt(std.log.level) or + @enumToInt(level) > @enumToInt(std.log.Level.info)) + { + const scope_name = @tagName(scope); + const ok = comptime for (build_options.log_scopes) |log_scope| { + if (mem.eql(u8, log_scope, scope_name)) + break true; + } else return; + } + + // We only recognize 4 log levels in this application. + const level_txt = switch (level) { + .emerg, .alert, .crit, .err => "error", + .warn => "warning", + .notice, .info => "info", + .debug => "debug", + }; + const prefix1 = level_txt; + const prefix2 = if (scope == .default) ": " else "(" ++ @tagName(scope) ++ "): "; + + // Print the message to stderr, silently ignoring any errors + std.debug.print(prefix1 ++ prefix2 ++ format ++ "\n", args); +} + +var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){}; + +pub fn main() anyerror!void { + const gpa = if (std.builtin.link_libc) std.heap.c_allocator else &general_purpose_allocator.allocator; + defer if (!std.builtin.link_libc) { + _ = general_purpose_allocator.deinit(); + }; + var arena_instance = std.heap.ArenaAllocator.init(gpa); + defer arena_instance.deinit(); + const arena = &arena_instance.allocator; + + const args = try process.argsAlloc(arena); + return mainArgs(gpa, arena, args); +} + +pub fn mainArgs(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !void { + if (args.len <= 1) { + std.log.info("{}", .{usage}); + fatal("expected command argument", .{}); + } + + const cmd = args[1]; + const cmd_args = args[2..]; + if (mem.eql(u8, cmd, "build-exe")) { + return buildOutputType(gpa, arena, args, .{ .build = .Exe }); + } else if (mem.eql(u8, cmd, "build-lib")) { + return buildOutputType(gpa, arena, args, .{ .build = .Lib }); + } else if (mem.eql(u8, cmd, "build-obj")) { + return buildOutputType(gpa, arena, args, .{ .build = .Obj }); + } else if (mem.eql(u8, cmd, "test")) { + return buildOutputType(gpa, arena, args, .zig_test); + } else if (mem.eql(u8, cmd, "run")) { + return buildOutputType(gpa, arena, args, .run); + } else if (mem.eql(u8, cmd, "cc")) { + return buildOutputType(gpa, arena, args, .cc); + } else if (mem.eql(u8, cmd, "c++")) { + return buildOutputType(gpa, arena, args, .cpp); + } else if (mem.eql(u8, cmd, "translate-c")) { + return buildOutputType(gpa, arena, args, .translate_c); + } else if (mem.eql(u8, cmd, "clang") or + mem.eql(u8, cmd, "-cc1") or mem.eql(u8, cmd, "-cc1as")) + { + return punt_to_clang(arena, args); + } else if (mem.eql(u8, cmd, "build")) { + return cmdBuild(gpa, arena, cmd_args); + } else if (mem.eql(u8, cmd, "fmt")) { + return cmdFmt(gpa, cmd_args); + } else if (mem.eql(u8, cmd, "libc")) { + return cmdLibC(gpa, cmd_args); + } else if (mem.eql(u8, cmd, "init-exe")) { + return cmdInit(gpa, arena, cmd_args, .Exe); + } else if (mem.eql(u8, cmd, "init-lib")) { + return cmdInit(gpa, arena, cmd_args, .Lib); + } else if (mem.eql(u8, cmd, "targets")) { + const info = try detectNativeTargetInfo(arena, .{}); + const stdout = io.getStdOut().outStream(); + return @import("print_targets.zig").cmdTargets(arena, cmd_args, stdout, info.target); + } else if (mem.eql(u8, cmd, "version")) { + try std.io.getStdOut().writeAll(build_options.version ++ "\n"); + } else if (mem.eql(u8, cmd, "env")) { + try @import("print_env.zig").cmdEnv(arena, cmd_args, io.getStdOut().outStream()); + } else if (mem.eql(u8, cmd, "zen")) { + try io.getStdOut().writeAll(info_zen); + } else if (mem.eql(u8, cmd, "help")) { + try io.getStdOut().writeAll(usage); + } else { + std.log.info("{}", .{usage}); + fatal("unknown command: {}", .{args[1]}); + } +} + +const usage_build_generic = + \\Usage: zig build-exe [files] + \\ zig build-lib [files] + \\ zig build-obj [files] + \\ zig test [files] + \\ zig run [file] [-- [args]] + \\ + \\Supported file types: + \\ .zig Zig source code + \\ .zir Zig Intermediate Representation code + \\ .o ELF object file + \\ .o MACH-O (macOS) object file + \\ .obj COFF (Windows) object file + \\ .lib COFF (Windows) static library + \\ .a ELF static library + \\ .so ELF shared object (dynamic link) + \\ .dll Windows Dynamic Link Library + \\ .dylib MACH-O (macOS) dynamic library + \\ .s Target-specific assembly source code + \\ .S Assembly with C preprocessor (requires LLVM extensions) + \\ .c C source code (requires LLVM extensions) + \\ .cpp C++ source code (requires LLVM extensions) + \\ Other C++ extensions: .C .cc .cxx + \\ + \\General Options: + \\ -h, --help Print this help and exit + \\ --watch Enable compiler REPL + \\ --color [auto|off|on] Enable or disable colored error messages + \\ -femit-bin[=path] (default) Output machine code + \\ -fno-emit-bin Do not output machine code + \\ -femit-asm[=path] Output .s (assembly code) + \\ -fno-emit-asm (default) Do not output .s (assembly code) + \\ -femit-zir[=path] Produce a .zir file with Zig IR + \\ -fno-emit-zir (default) Do not produce a .zir file with Zig IR + \\ -femit-llvm-ir[=path] Produce a .ll file with LLVM IR (requires LLVM extensions) + \\ -fno-emit-llvm-ir (default) Do not produce a .ll file with LLVM IR + \\ -femit-h[=path] Generate a C header file (.h) + \\ -fno-emit-h (default) Do not generate a C header file (.h) + \\ -femit-docs[=path] Create a docs/ dir with html documentation + \\ -fno-emit-docs (default) Do not produce docs/ dir with html documentation + \\ -femit-analysis[=path] Write analysis JSON file with type information + \\ -fno-emit-analysis (default) Do not write analysis JSON file with type information + \\ --show-builtin Output the source of @import("builtin") then exit + \\ --cache-dir [path] Override the local cache directory + \\ --global-cache-dir [path] Override the global cache directory + \\ --override-lib-dir [path] Override path to Zig installation lib directory + \\ --enable-cache Output to cache directory; print path to stdout + \\ + \\Compile Options: + \\ -target [name] -- see the targets command + \\ -mcpu [cpu] Specify target CPU and feature set + \\ -mcmodel=[default|tiny| Limit range of code and data virtual addresses + \\ small|kernel| + \\ medium|large] + \\ --name [name] Override root name (not a file path) + \\ -O [mode] Choose what to optimize for + \\ Debug (default) Optimizations off, safety on + \\ ReleaseFast Optimizations on, safety off + \\ ReleaseSafe Optimizations on, safety on + \\ ReleaseSmall Optimize for small binary, safety off + \\ --pkg-begin [name] [path] Make pkg available to import and push current pkg + \\ --pkg-end Pop current pkg + \\ --main-pkg-path Set the directory of the root package + \\ -fPIC Force-enable Position Independent Code + \\ -fno-PIC Force-disable Position Independent Code + \\ -fstack-check Enable stack probing in unsafe builds + \\ -fno-stack-check Disable stack probing in safe builds + \\ -fsanitize-c Enable C undefined behavior detection in unsafe builds + \\ -fno-sanitize-c Disable C undefined behavior detection in safe builds + \\ -fvalgrind Include valgrind client requests in release builds + \\ -fno-valgrind Omit valgrind client requests in debug builds + \\ -fdll-export-fns Mark exported functions as DLL exports (Windows) + \\ -fno-dll-export-fns Force-disable marking exported functions as DLL exports + \\ --strip Omit debug symbols + \\ --single-threaded Code assumes it is only used single-threaded + \\ -ofmt=[mode] Override target object format + \\ elf Executable and Linking Format + \\ c Compile to C source code + \\ wasm WebAssembly + \\ pe Portable Executable (Windows) + \\ coff Common Object File Format (Windows) + \\ macho macOS relocatables + \\ hex (planned) Intel IHEX + \\ raw (planned) Dump machine code directly + \\ -dirafter [dir] Add directory to AFTER include search path + \\ -isystem [dir] Add directory to SYSTEM include search path + \\ -I[dir] Add directory to include search path + \\ -D[macro]=[value] Define C [macro] to [value] (1 if [value] omitted) + \\ --libc [file] Provide a file which specifies libc paths + \\ -cflags [flags] -- Set extra flags for the next positional C source files + \\ -ffunction-sections Places each function in a separate section + \\ + \\Link Options: + \\ -l[lib], --library [lib] Link against system library + \\ -L[d], --library-directory [d] Add a directory to the library search path + \\ -T[script], --script [script] Use a custom linker script + \\ --version-script [path] Provide a version .map file + \\ --dynamic-linker [path] Set the dynamic interpreter path (usually ld.so) + \\ --each-lib-rpath Add rpath for each used dynamic library + \\ --version [ver] Dynamic library semver + \\ -rdynamic Add all symbols to the dynamic symbol table + \\ -rpath [path] Add directory to the runtime library search path + \\ --eh-frame-hdr Enable C++ exception handling by passing --eh-frame-hdr to linker + \\ -dynamic Force output to be dynamically linked + \\ -static Force output to be statically linked + \\ -Bsymbolic Bind global references locally + \\ --subsystem [subsystem] (windows) /SUBSYSTEM: to the linker\n" + \\ --stack [size] Override default stack size + \\ -framework [name] (darwin) link against framework + \\ -F[dir] (darwin) add search path for frameworks + \\ + \\Test Options: + \\ --test-filter [text] Skip tests that do not match filter + \\ --test-name-prefix [text] Add prefix to all tests + \\ --test-cmd [arg] Specify test execution command one arg at a time + \\ --test-cmd-bin Appends test binary path to test cmd args + \\ --test-evented-io Runs the test in evented I/O mode + \\ + \\Debug Options (Zig Compiler Development): + \\ -ftime-report Print timing diagnostics + \\ -fstack-report Print stack size diagnostics + \\ --verbose-link Display linker invocations + \\ --verbose-cc Display C compiler invocations + \\ --verbose-tokenize Enable compiler debug output for tokenization + \\ --verbose-ast Enable compiler debug output for AST parsing + \\ --verbose-ir Enable compiler debug output for Zig IR + \\ --verbose-llvm-ir Enable compiler debug output for LLVM IR + \\ --verbose-cimport Enable compiler debug output for C imports + \\ --verbose-llvm-cpu-features Enable compiler debug output for LLVM CPU features + \\ +; + +const repl_help = + \\Commands: + \\ update Detect changes to source files and update output files. + \\ help Print this text + \\ exit Quit this repl + \\ +; + +const Emit = union(enum) { + no, + yes_default_path, + yes: []const u8, + + const Resolved = struct { + data: ?Compilation.EmitLoc, + dir: ?fs.Dir, + + fn deinit(self: *Resolved) void { + if (self.dir) |*dir| { + dir.close(); + } + } + }; + + fn resolve(emit: Emit, default_basename: []const u8) !Resolved { + var resolved: Resolved = .{ .data = null, .dir = null }; + errdefer resolved.deinit(); + + switch (emit) { + .no => {}, + .yes_default_path => { + resolved.data = Compilation.EmitLoc{ + .directory = .{ .path = null, .handle = fs.cwd() }, + .basename = default_basename, + }; + }, + .yes => |full_path| { + const basename = fs.path.basename(full_path); + if (fs.path.dirname(full_path)) |dirname| { + const handle = try fs.cwd().openDir(dirname, .{}); + resolved = .{ + .dir = handle, + .data = Compilation.EmitLoc{ + .basename = basename, + .directory = .{ + .path = dirname, + .handle = handle, + }, + }, + }; + } else { + resolved.data = Compilation.EmitLoc{ + .basename = basename, + .directory = .{ .path = null, .handle = fs.cwd() }, + }; + } + }, + } + return resolved; + } +}; + +fn buildOutputType( + gpa: *Allocator, + arena: *Allocator, + all_args: []const []const u8, + arg_mode: union(enum) { + build: std.builtin.OutputMode, + cc, + cpp, + translate_c, + zig_test, + run, + }, +) !void { + var color: Color = .Auto; + var optimize_mode: std.builtin.Mode = .Debug; + var provided_name: ?[]const u8 = null; + var link_mode: ?std.builtin.LinkMode = null; + var dll_export_fns: ?bool = null; + var root_src_file: ?[]const u8 = null; + var version: std.builtin.Version = .{ .major = 0, .minor = 0, .patch = 0 }; + var have_version = false; + var strip = false; + var single_threaded = false; + var function_sections = false; + var watch = false; + var verbose_link = false; + var verbose_cc = false; + var verbose_tokenize = false; + var verbose_ast = false; + var verbose_ir = false; + var verbose_llvm_ir = false; + var verbose_cimport = false; + var verbose_llvm_cpu_features = false; + var time_report = false; + var stack_report = false; + var show_builtin = false; + var emit_bin: Emit = .yes_default_path; + var emit_asm: Emit = .no; + var emit_llvm_ir: Emit = .no; + var emit_zir: Emit = .no; + var emit_docs: Emit = .no; + var emit_analysis: Emit = .no; + var target_arch_os_abi: []const u8 = "native"; + var target_mcpu: ?[]const u8 = null; + var target_dynamic_linker: ?[]const u8 = null; + var target_ofmt: ?[]const u8 = null; + var output_mode: std.builtin.OutputMode = undefined; + var emit_h: Emit = undefined; + var ensure_libc_on_non_freestanding = false; + var ensure_libcpp_on_non_freestanding = false; + var link_libc = false; + var link_libcpp = false; + var want_native_include_dirs = false; + var enable_cache: ?bool = null; + var want_pic: ?bool = null; + var want_sanitize_c: ?bool = null; + var want_stack_check: ?bool = null; + var want_valgrind: ?bool = null; + var rdynamic: bool = false; + var linker_script: ?[]const u8 = null; + var version_script: ?[]const u8 = null; + var disable_c_depfile = false; + var override_soname: ?[]const u8 = null; + var linker_gc_sections: ?bool = null; + var linker_allow_shlib_undefined: ?bool = null; + var linker_bind_global_refs_locally: ?bool = null; + var linker_z_nodelete = false; + var linker_z_defs = false; + var test_evented_io = false; + var stack_size_override: ?u64 = null; + var use_llvm: ?bool = null; + var use_lld: ?bool = null; + var use_clang: ?bool = null; + var link_eh_frame_hdr = false; + var each_lib_rpath = false; + var libc_paths_file: ?[]const u8 = null; + var machine_code_model: std.builtin.CodeModel = .default; + var runtime_args_start: ?usize = null; + var test_filter: ?[]const u8 = null; + var test_name_prefix: ?[]const u8 = null; + var override_local_cache_dir: ?[]const u8 = null; + var override_global_cache_dir: ?[]const u8 = null; + var override_lib_dir: ?[]const u8 = null; + var main_pkg_path: ?[]const u8 = null; + var clang_preprocessor_mode: Compilation.ClangPreprocessorMode = .no; + var subsystem: ?std.Target.SubSystem = null; + + var system_libs = std.ArrayList([]const u8).init(gpa); + defer system_libs.deinit(); + + var clang_argv = std.ArrayList([]const u8).init(gpa); + defer clang_argv.deinit(); + + var extra_cflags = std.ArrayList([]const u8).init(gpa); + defer extra_cflags.deinit(); + + var lld_argv = std.ArrayList([]const u8).init(gpa); + defer lld_argv.deinit(); + + var lib_dirs = std.ArrayList([]const u8).init(gpa); + defer lib_dirs.deinit(); + + var rpath_list = std.ArrayList([]const u8).init(gpa); + defer rpath_list.deinit(); + + var c_source_files = std.ArrayList(Compilation.CSourceFile).init(gpa); + defer c_source_files.deinit(); + + var link_objects = std.ArrayList([]const u8).init(gpa); + defer link_objects.deinit(); + + var framework_dirs = std.ArrayList([]const u8).init(gpa); + defer framework_dirs.deinit(); + + var frameworks = std.ArrayList([]const u8).init(gpa); + defer frameworks.deinit(); + + // null means replace with the test executable binary + var test_exec_args = std.ArrayList(?[]const u8).init(gpa); + defer test_exec_args.deinit(); + + var root_pkg_memory: Package = .{ + .root_src_directory = undefined, + .root_src_path = undefined, + }; + defer root_pkg_memory.table.deinit(gpa); + var cur_pkg: *Package = &root_pkg_memory; + + switch (arg_mode) { + .build, .translate_c, .zig_test, .run => { + var optimize_mode_string: ?[]const u8 = null; + switch (arg_mode) { + .build => |m| { + output_mode = m; + }, + .translate_c => { + emit_bin = .no; + output_mode = .Obj; + }, + .zig_test, .run => { + output_mode = .Exe; + }, + else => unreachable, + } + // TODO finish self-hosted and add support for emitting C header files + emit_h = .no; + //switch (arg_mode) { + // .build => switch (output_mode) { + // .Exe => emit_h = .no, + // .Obj, .Lib => emit_h = .yes_default_path, + // }, + // .translate_c, .zig_test, .run => emit_h = .no, + // else => unreachable, + //} + const args = all_args[2..]; + var i: usize = 0; + while (i < args.len) : (i += 1) { + const arg = args[i]; + if (mem.startsWith(u8, arg, "-")) { + if (mem.eql(u8, arg, "-h") or mem.eql(u8, arg, "--help")) { + try io.getStdOut().writeAll(usage_build_generic); + return cleanExit(); + } else if (mem.eql(u8, arg, "--")) { + if (arg_mode == .run) { + runtime_args_start = i + 1; + } else { + fatal("unexpected end-of-parameter mark: --", .{}); + } + } else if (mem.eql(u8, arg, "--pkg-begin")) { + if (i + 2 >= args.len) fatal("Expected 2 arguments after {}", .{arg}); + i += 1; + const pkg_name = args[i]; + i += 1; + const pkg_path = args[i]; + + const new_cur_pkg = try arena.create(Package); + new_cur_pkg.* = .{ + .root_src_directory = if (fs.path.dirname(pkg_path)) |dirname| + .{ + .path = dirname, + .handle = try fs.cwd().openDir(dirname, .{}), // TODO close this fd + } + else + .{ + .path = null, + .handle = fs.cwd(), + }, + .root_src_path = fs.path.basename(pkg_path), + .parent = cur_pkg, + }; + try cur_pkg.table.put(gpa, pkg_name, new_cur_pkg); + cur_pkg = new_cur_pkg; + } else if (mem.eql(u8, arg, "--pkg-end")) { + cur_pkg = cur_pkg.parent orelse + fatal("encountered --pkg-end with no matching --pkg-begin", .{}); + } else if (mem.eql(u8, arg, "--main-pkg-path")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + main_pkg_path = args[i]; + } else if (mem.eql(u8, arg, "-cflags")) { + extra_cflags.shrinkRetainingCapacity(0); + while (true) { + i += 1; + if (i + 1 >= args.len) fatal("expected -- after -cflags", .{}); + if (mem.eql(u8, args[i], "--")) break; + try extra_cflags.append(args[i]); + } + } else if (mem.eql(u8, arg, "--color")) { + if (i + 1 >= args.len) { + fatal("expected [auto|on|off] after --color", .{}); + } + i += 1; + const next_arg = args[i]; + if (mem.eql(u8, next_arg, "auto")) { + color = .Auto; + } else if (mem.eql(u8, next_arg, "on")) { + color = .On; + } else if (mem.eql(u8, next_arg, "off")) { + color = .Off; + } else { + fatal("expected [auto|on|off] after --color, found '{}'", .{next_arg}); + } + } else if (mem.eql(u8, arg, "--subsystem")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + if (mem.eql(u8, args[i], "console")) { + subsystem = .Console; + } else if (mem.eql(u8, args[i], "windows")) { + subsystem = .Windows; + } else if (mem.eql(u8, args[i], "posix")) { + subsystem = .Posix; + } else if (mem.eql(u8, args[i], "native")) { + subsystem = .Native; + } else if (mem.eql(u8, args[i], "efi_application")) { + subsystem = .EfiApplication; + } else if (mem.eql(u8, args[i], "efi_boot_service_driver")) { + subsystem = .EfiBootServiceDriver; + } else if (mem.eql(u8, args[i], "efi_rom")) { + subsystem = .EfiRom; + } else if (mem.eql(u8, args[i], "efi_runtime_driver")) { + subsystem = .EfiRuntimeDriver; + } else { + fatal("invalid: --subsystem: '{s}'. Options are:\n{s}", .{ + args[i], + \\ console + \\ windows + \\ posix + \\ native + \\ efi_application + \\ efi_boot_service_driver + \\ efi_rom + \\ efi_runtime_driver + \\ + }); + } + } else if (mem.eql(u8, arg, "-O")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + optimize_mode_string = args[i]; + } else if (mem.eql(u8, arg, "--stack")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + stack_size_override = std.fmt.parseInt(u64, args[i], 10) catch |err| { + fatal("unable to parse '{}': {}", .{ arg, @errorName(err) }); + }; + } else if (mem.eql(u8, arg, "--name")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + provided_name = args[i]; + } else if (mem.eql(u8, arg, "-rpath")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + try rpath_list.append(args[i]); + } else if (mem.eql(u8, arg, "--library-directory") or mem.eql(u8, arg, "-L")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + try lib_dirs.append(args[i]); + } else if (mem.eql(u8, arg, "-F")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + try framework_dirs.append(args[i]); + } else if (mem.eql(u8, arg, "-framework")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + try frameworks.append(args[i]); + } else if (mem.eql(u8, arg, "-T") or mem.eql(u8, arg, "--script")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + linker_script = args[i]; + } else if (mem.eql(u8, arg, "--version-script")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + version_script = args[i]; + } else if (mem.eql(u8, arg, "--library") or mem.eql(u8, arg, "-l")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + // We don't know whether this library is part of libc or libc++ until we resolve the target. + // So we simply append to the list for now. + i += 1; + try system_libs.append(args[i]); + } else if (mem.eql(u8, arg, "-D") or + mem.eql(u8, arg, "-isystem") or + mem.eql(u8, arg, "-I") or + mem.eql(u8, arg, "-dirafter")) + { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + try clang_argv.append(arg); + try clang_argv.append(args[i]); + } else if (mem.eql(u8, arg, "--version")) { + if (i + 1 >= args.len) { + fatal("expected parameter after --version", .{}); + } + i += 1; + version = std.builtin.Version.parse(args[i]) catch |err| { + fatal("unable to parse --version '{}': {}", .{ args[i], @errorName(err) }); + }; + have_version = true; + } else if (mem.eql(u8, arg, "-target")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + target_arch_os_abi = args[i]; + } else if (mem.eql(u8, arg, "-mcpu")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + target_mcpu = args[i]; + } else if (mem.eql(u8, arg, "-mcmodel")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + machine_code_model = parseCodeModel(args[i]); + } else if (mem.startsWith(u8, arg, "-ofmt=")) { + target_ofmt = arg["-ofmt=".len..]; + } else if (mem.startsWith(u8, arg, "-mcpu=")) { + target_mcpu = arg["-mcpu=".len..]; + } else if (mem.startsWith(u8, arg, "-mcmodel=")) { + machine_code_model = parseCodeModel(arg["-mcmodel=".len..]); + } else if (mem.startsWith(u8, arg, "-O")) { + optimize_mode_string = arg["-O".len..]; + } else if (mem.eql(u8, arg, "--dynamic-linker")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + target_dynamic_linker = args[i]; + } else if (mem.eql(u8, arg, "--libc")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + libc_paths_file = args[i]; + } else if (mem.eql(u8, arg, "--test-filter")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + test_filter = args[i]; + } else if (mem.eql(u8, arg, "--test-name-prefix")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + test_name_prefix = args[i]; + } else if (mem.eql(u8, arg, "--test-cmd")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + try test_exec_args.append(args[i]); + } else if (mem.eql(u8, arg, "--cache-dir")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + override_local_cache_dir = args[i]; + } else if (mem.eql(u8, arg, "--global-cache-dir")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + override_global_cache_dir = args[i]; + } else if (mem.eql(u8, arg, "--override-lib-dir")) { + if (i + 1 >= args.len) fatal("expected parameter after {}", .{arg}); + i += 1; + override_lib_dir = args[i]; + } else if (mem.eql(u8, arg, "--each-lib-rpath")) { + each_lib_rpath = true; + } else if (mem.eql(u8, arg, "--enable-cache")) { + enable_cache = true; + } else if (mem.eql(u8, arg, "--test-cmd-bin")) { + try test_exec_args.append(null); + } else if (mem.eql(u8, arg, "--test-evented-io")) { + test_evented_io = true; + } else if (mem.eql(u8, arg, "--watch")) { + watch = true; + } else if (mem.eql(u8, arg, "-ftime-report")) { + time_report = true; + } else if (mem.eql(u8, arg, "-fstack-report")) { + stack_report = true; + } else if (mem.eql(u8, arg, "-fPIC")) { + want_pic = true; + } else if (mem.eql(u8, arg, "-fno-PIC")) { + want_pic = false; + } else if (mem.eql(u8, arg, "-fstack-check")) { + want_stack_check = true; + } else if (mem.eql(u8, arg, "-fno-stack-check")) { + want_stack_check = false; + } else if (mem.eql(u8, arg, "-fsanitize-c")) { + want_sanitize_c = true; + } else if (mem.eql(u8, arg, "-fno-sanitize-c")) { + want_sanitize_c = false; + } else if (mem.eql(u8, arg, "-fvalgrind")) { + want_valgrind = true; + } else if (mem.eql(u8, arg, "-fno-valgrind")) { + want_valgrind = false; + } else if (mem.eql(u8, arg, "-fLLVM")) { + use_llvm = true; + } else if (mem.eql(u8, arg, "-fno-LLVM")) { + use_llvm = false; + } else if (mem.eql(u8, arg, "-fLLD")) { + use_lld = true; + } else if (mem.eql(u8, arg, "-fno-LLD")) { + use_lld = false; + } else if (mem.eql(u8, arg, "-fClang")) { + use_clang = true; + } else if (mem.eql(u8, arg, "-fno-Clang")) { + use_clang = false; + } else if (mem.eql(u8, arg, "-rdynamic")) { + rdynamic = true; + } else if (mem.eql(u8, arg, "-femit-bin")) { + emit_bin = .yes_default_path; + } else if (mem.startsWith(u8, arg, "-femit-bin=")) { + emit_bin = .{ .yes = arg["-femit-bin=".len..] }; + } else if (mem.eql(u8, arg, "-fno-emit-bin")) { + emit_bin = .no; + } else if (mem.eql(u8, arg, "-femit-zir")) { + emit_zir = .yes_default_path; + } else if (mem.startsWith(u8, arg, "-femit-zir=")) { + emit_zir = .{ .yes = arg["-femit-zir=".len..] }; + } else if (mem.eql(u8, arg, "-fno-emit-zir")) { + emit_zir = .no; + } else if (mem.eql(u8, arg, "-femit-h")) { + emit_h = .yes_default_path; + } else if (mem.startsWith(u8, arg, "-femit-h=")) { + emit_h = .{ .yes = arg["-femit-h=".len..] }; + } else if (mem.eql(u8, arg, "-fno-emit-h")) { + emit_h = .no; + } else if (mem.eql(u8, arg, "-femit-asm")) { + emit_asm = .yes_default_path; + } else if (mem.startsWith(u8, arg, "-femit-asm=")) { + emit_asm = .{ .yes = arg["-femit-asm=".len..] }; + } else if (mem.eql(u8, arg, "-fno-emit-asm")) { + emit_asm = .no; + } else if (mem.eql(u8, arg, "-femit-llvm-ir")) { + emit_llvm_ir = .yes_default_path; + } else if (mem.startsWith(u8, arg, "-femit-llvm-ir=")) { + emit_llvm_ir = .{ .yes = arg["-femit-llvm-ir=".len..] }; + } else if (mem.eql(u8, arg, "-fno-emit-llvm-ir")) { + emit_llvm_ir = .no; + } else if (mem.eql(u8, arg, "-femit-docs")) { + emit_docs = .yes_default_path; + } else if (mem.startsWith(u8, arg, "-femit-docs=")) { + emit_docs = .{ .yes = arg["-femit-docs=".len..] }; + } else if (mem.eql(u8, arg, "-fno-emit-docs")) { + emit_docs = .no; + } else if (mem.eql(u8, arg, "-femit-analysis")) { + emit_analysis = .yes_default_path; + } else if (mem.startsWith(u8, arg, "-femit-analysis=")) { + emit_analysis = .{ .yes = arg["-femit-analysis=".len..] }; + } else if (mem.eql(u8, arg, "-fno-emit-analysis")) { + emit_analysis = .no; + } else if (mem.eql(u8, arg, "-dynamic")) { + link_mode = .Dynamic; + } else if (mem.eql(u8, arg, "-static")) { + link_mode = .Static; + } else if (mem.eql(u8, arg, "-fdll-export-fns")) { + dll_export_fns = true; + } else if (mem.eql(u8, arg, "-fno-dll-export-fns")) { + dll_export_fns = false; + } else if (mem.eql(u8, arg, "--show-builtin")) { + show_builtin = true; + emit_bin = .no; + } else if (mem.eql(u8, arg, "--strip")) { + strip = true; + } else if (mem.eql(u8, arg, "--single-threaded")) { + single_threaded = true; + } else if (mem.eql(u8, arg, "-ffunction-sections")) { + function_sections = true; + } else if (mem.eql(u8, arg, "--eh-frame-hdr")) { + link_eh_frame_hdr = true; + } else if (mem.eql(u8, arg, "-Bsymbolic")) { + linker_bind_global_refs_locally = true; + } else if (mem.eql(u8, arg, "--verbose-link")) { + verbose_link = true; + } else if (mem.eql(u8, arg, "--verbose-cc")) { + verbose_cc = true; + } else if (mem.eql(u8, arg, "--verbose-tokenize")) { + verbose_tokenize = true; + } else if (mem.eql(u8, arg, "--verbose-ast")) { + verbose_ast = true; + } else if (mem.eql(u8, arg, "--verbose-ir")) { + verbose_ir = true; + } else if (mem.eql(u8, arg, "--verbose-llvm-ir")) { + verbose_llvm_ir = true; + } else if (mem.eql(u8, arg, "--verbose-cimport")) { + verbose_cimport = true; + } else if (mem.eql(u8, arg, "--verbose-llvm-cpu-features")) { + verbose_llvm_cpu_features = true; + } else if (mem.startsWith(u8, arg, "-T")) { + linker_script = arg[2..]; + } else if (mem.startsWith(u8, arg, "-L")) { + try lib_dirs.append(arg[2..]); + } else if (mem.startsWith(u8, arg, "-F")) { + try framework_dirs.append(arg[2..]); + } else if (mem.startsWith(u8, arg, "-l")) { + // We don't know whether this library is part of libc or libc++ until we resolve the target. + // So we simply append to the list for now. + try system_libs.append(arg[2..]); + } else if (mem.startsWith(u8, arg, "-D") or + mem.startsWith(u8, arg, "-I")) + { + try clang_argv.append(arg); + } else { + fatal("unrecognized parameter: '{}'", .{arg}); + } + } else switch (Compilation.classifyFileExt(arg)) { + .object, .static_library => { + try link_objects.append(arg); + }, + .assembly, .c, .cpp, .h, .ll, .bc => { + try c_source_files.append(.{ + .src_path = arg, + .extra_flags = try arena.dupe([]const u8, extra_cflags.items), + }); + }, + .shared_library => { + fatal("linking against dynamic libraries not yet supported", .{}); + }, + .zig, .zir => { + if (root_src_file) |other| { + fatal("found another zig file '{}' after root source file '{}'", .{ arg, other }); + } else { + root_src_file = arg; + } + }, + .unknown => { + fatal("unrecognized file extension of parameter '{}'", .{arg}); + }, + } + } + if (optimize_mode_string) |s| { + optimize_mode = std.meta.stringToEnum(std.builtin.Mode, s) orelse + fatal("unrecognized optimization mode: '{}'", .{s}); + } + }, + .cc, .cpp => { + emit_h = .no; + strip = true; + ensure_libc_on_non_freestanding = true; + ensure_libcpp_on_non_freestanding = arg_mode == .cpp; + want_native_include_dirs = true; + + const COutMode = enum { + link, + object, + assembly, + preprocessor, + }; + var c_out_mode: COutMode = .link; + var out_path: ?[]const u8 = null; + var is_shared_lib = false; + var linker_args = std.ArrayList([]const u8).init(arena); + var it = ClangArgIterator.init(arena, all_args); + while (it.has_next) { + it.next() catch |err| { + fatal("unable to parse command line parameters: {}", .{@errorName(err)}); + }; + switch (it.zig_equivalent) { + .target => target_arch_os_abi = it.only_arg, // example: -target riscv64-linux-unknown + .o => out_path = it.only_arg, // -o + .c => c_out_mode = .object, // -c + .asm_only => c_out_mode = .assembly, // -S + .preprocess_only => c_out_mode = .preprocessor, // -E + .other => { + try clang_argv.appendSlice(it.other_args); + }, + .positional => { + const file_ext = Compilation.classifyFileExt(mem.spanZ(it.only_arg)); + switch (file_ext) { + .assembly, .c, .cpp, .ll, .bc, .h => try c_source_files.append(.{ .src_path = it.only_arg }), + .unknown, .shared_library, .object, .static_library => { + try link_objects.append(it.only_arg); + }, + .zig, .zir => { + if (root_src_file) |other| { + fatal("found another zig file '{}' after root source file '{}'", .{ it.only_arg, other }); + } else { + root_src_file = it.only_arg; + } + }, + } + }, + .l => { + // -l + // We don't know whether this library is part of libc or libc++ until we resolve the target. + // So we simply append to the list for now. + try system_libs.append(it.only_arg); + }, + .ignore => {}, + .driver_punt => { + // Never mind what we're doing, just pass the args directly. For example --help. + return punt_to_clang(arena, all_args); + }, + .pic => want_pic = true, + .no_pic => want_pic = false, + .nostdlib => ensure_libc_on_non_freestanding = false, + .nostdlib_cpp => ensure_libcpp_on_non_freestanding = false, + .shared => { + link_mode = .Dynamic; + is_shared_lib = true; + }, + .rdynamic => rdynamic = true, + .wl => { + var split_it = mem.split(it.only_arg, ","); + while (split_it.next()) |linker_arg| { + try linker_args.append(linker_arg); + } + }, + .optimize => { + // Alright, what release mode do they want? + if (mem.eql(u8, it.only_arg, "Os")) { + optimize_mode = .ReleaseSmall; + } else if (mem.eql(u8, it.only_arg, "O2") or + mem.eql(u8, it.only_arg, "O3") or + mem.eql(u8, it.only_arg, "O4")) + { + optimize_mode = .ReleaseFast; + } else if (mem.eql(u8, it.only_arg, "Og") or + mem.eql(u8, it.only_arg, "O0")) + { + optimize_mode = .Debug; + } else { + try clang_argv.appendSlice(it.other_args); + } + }, + .debug => { + strip = false; + if (mem.eql(u8, it.only_arg, "-g")) { + // We handled with strip = false above. + } else { + try clang_argv.appendSlice(it.other_args); + } + }, + .sanitize => { + if (mem.eql(u8, it.only_arg, "undefined")) { + want_sanitize_c = true; + } else { + try clang_argv.appendSlice(it.other_args); + } + }, + .linker_script => linker_script = it.only_arg, + .verbose_cmds => { + verbose_cc = true; + verbose_link = true; + }, + .for_linker => try linker_args.append(it.only_arg), + .linker_input_z => { + try linker_args.append("-z"); + try linker_args.append(it.only_arg); + }, + .lib_dir => try lib_dirs.append(it.only_arg), + .mcpu => target_mcpu = it.only_arg, + .dep_file => { + disable_c_depfile = true; + try clang_argv.appendSlice(it.other_args); + }, + .framework_dir => try framework_dirs.append(it.only_arg), + .framework => try frameworks.append(it.only_arg), + .nostdlibinc => want_native_include_dirs = false, + } + } + // Parse linker args. + var i: usize = 0; + while (i < linker_args.items.len) : (i += 1) { + const arg = linker_args.items[i]; + if (mem.eql(u8, arg, "-soname")) { + i += 1; + if (i >= linker_args.items.len) { + fatal("expected linker arg after '{}'", .{arg}); + } + const soname = linker_args.items[i]; + override_soname = soname; + // Use it as --name. + // Example: libsoundio.so.2 + var prefix: usize = 0; + if (mem.startsWith(u8, soname, "lib")) { + prefix = 3; + } + var end: usize = soname.len; + if (mem.endsWith(u8, soname, ".so")) { + end -= 3; + } else { + var found_digit = false; + while (end > 0 and std.ascii.isDigit(soname[end - 1])) { + found_digit = true; + end -= 1; + } + if (found_digit and end > 0 and soname[end - 1] == '.') { + end -= 1; + } else { + end = soname.len; + } + if (mem.endsWith(u8, soname[prefix..end], ".so")) { + end -= 3; + } + } + provided_name = soname[prefix..end]; + } else if (mem.eql(u8, arg, "-rpath")) { + i += 1; + if (i >= linker_args.items.len) { + fatal("expected linker arg after '{}'", .{arg}); + } + try rpath_list.append(linker_args.items[i]); + } else if (mem.eql(u8, arg, "-I") or + mem.eql(u8, arg, "--dynamic-linker") or + mem.eql(u8, arg, "-dynamic-linker")) + { + i += 1; + if (i >= linker_args.items.len) { + fatal("expected linker arg after '{}'", .{arg}); + } + target_dynamic_linker = linker_args.items[i]; + } else if (mem.eql(u8, arg, "-E") or + mem.eql(u8, arg, "--export-dynamic") or + mem.eql(u8, arg, "-export-dynamic")) + { + rdynamic = true; + } else if (mem.eql(u8, arg, "--version-script")) { + i += 1; + if (i >= linker_args.items.len) { + fatal("expected linker arg after '{}'", .{arg}); + } + version_script = linker_args.items[i]; + } else if (mem.startsWith(u8, arg, "-O")) { + try lld_argv.append(arg); + } else if (mem.eql(u8, arg, "--gc-sections")) { + linker_gc_sections = true; + } else if (mem.eql(u8, arg, "--no-gc-sections")) { + linker_gc_sections = false; + } else if (mem.eql(u8, arg, "--allow-shlib-undefined") or + mem.eql(u8, arg, "-allow-shlib-undefined")) + { + linker_allow_shlib_undefined = true; + } else if (mem.eql(u8, arg, "--no-allow-shlib-undefined") or + mem.eql(u8, arg, "-no-allow-shlib-undefined")) + { + linker_allow_shlib_undefined = false; + } else if (mem.eql(u8, arg, "-Bsymbolic")) { + linker_bind_global_refs_locally = true; + } else if (mem.eql(u8, arg, "-z")) { + i += 1; + if (i >= linker_args.items.len) { + fatal("expected linker arg after '{}'", .{arg}); + } + const z_arg = linker_args.items[i]; + if (mem.eql(u8, z_arg, "nodelete")) { + linker_z_nodelete = true; + } else if (mem.eql(u8, z_arg, "defs")) { + linker_z_defs = true; + } else { + warn("unsupported linker arg: -z {}", .{z_arg}); + } + } else if (mem.eql(u8, arg, "--major-image-version")) { + i += 1; + if (i >= linker_args.items.len) { + fatal("expected linker arg after '{}'", .{arg}); + } + version.major = std.fmt.parseInt(u32, linker_args.items[i], 10) catch |err| { + fatal("unable to parse '{}': {}", .{ arg, @errorName(err) }); + }; + have_version = true; + } else if (mem.eql(u8, arg, "--minor-image-version")) { + i += 1; + if (i >= linker_args.items.len) { + fatal("expected linker arg after '{}'", .{arg}); + } + version.minor = std.fmt.parseInt(u32, linker_args.items[i], 10) catch |err| { + fatal("unable to parse '{}': {}", .{ arg, @errorName(err) }); + }; + have_version = true; + } else if (mem.eql(u8, arg, "--stack")) { + i += 1; + if (i >= linker_args.items.len) { + fatal("expected linker arg after '{}'", .{arg}); + } + stack_size_override = std.fmt.parseInt(u64, linker_args.items[i], 10) catch |err| { + fatal("unable to parse '{}': {}", .{ arg, @errorName(err) }); + }; + } else { + warn("unsupported linker arg: {}", .{arg}); + } + } + + if (want_sanitize_c) |wsc| { + if (wsc and optimize_mode == .ReleaseFast) { + optimize_mode = .ReleaseSafe; + } + } + + switch (c_out_mode) { + .link => { + output_mode = if (is_shared_lib) .Lib else .Exe; + emit_bin = .{ .yes = out_path orelse "a.out" }; + enable_cache = true; + }, + .object => { + output_mode = .Obj; + if (out_path) |p| { + emit_bin = .{ .yes = p }; + } else { + emit_bin = .yes_default_path; + } + }, + .assembly => { + output_mode = .Obj; + emit_bin = .no; + if (out_path) |p| { + emit_asm = .{ .yes = p }; + } else { + emit_asm = .yes_default_path; + } + }, + .preprocessor => { + output_mode = .Obj; + // An error message is generated when there is more than 1 C source file. + if (c_source_files.items.len != 1) { + // For example `zig cc` and no args should print the "no input files" message. + return punt_to_clang(arena, all_args); + } + if (out_path) |p| { + emit_bin = .{ .yes = p }; + clang_preprocessor_mode = .yes; + } else { + clang_preprocessor_mode = .stdout; + } + }, + } + if (c_source_files.items.len == 0 and link_objects.items.len == 0) { + // For example `zig cc` and no args should print the "no input files" message. + return punt_to_clang(arena, all_args); + } + }, + } + + if (arg_mode == .translate_c and c_source_files.items.len != 1) { + fatal("translate-c expects exactly 1 source file (found {})", .{c_source_files.items.len}); + } + + const root_name = if (provided_name) |n| n else blk: { + if (arg_mode == .zig_test) { + break :blk "test"; + } else if (root_src_file) |file| { + const basename = fs.path.basename(file); + break :blk mem.split(basename, ".").next().?; + } else if (c_source_files.items.len == 1) { + const basename = fs.path.basename(c_source_files.items[0].src_path); + break :blk mem.split(basename, ".").next().?; + } else if (link_objects.items.len == 1) { + const basename = fs.path.basename(link_objects.items[0]); + break :blk mem.split(basename, ".").next().?; + } else if (emit_bin == .yes) { + const basename = fs.path.basename(emit_bin.yes); + break :blk mem.split(basename, ".").next().?; + } else if (show_builtin) { + break :blk "builtin"; + } else if (arg_mode == .run) { + break :blk "run"; + } else { + fatal("--name [name] not provided and unable to infer", .{}); + } + }; + + var diags: std.zig.CrossTarget.ParseOptions.Diagnostics = .{}; + const cross_target = std.zig.CrossTarget.parse(.{ + .arch_os_abi = target_arch_os_abi, + .cpu_features = target_mcpu, + .dynamic_linker = target_dynamic_linker, + .diagnostics = &diags, + }) catch |err| switch (err) { + error.UnknownCpuModel => { + help: { + var help_text = std.ArrayList(u8).init(arena); + for (diags.arch.?.allCpuModels()) |cpu| { + help_text.writer().print(" {}\n", .{cpu.name}) catch break :help; + } + std.log.info("Available CPUs for architecture '{}': {}", .{ + @tagName(diags.arch.?), help_text.items, + }); + } + fatal("Unknown CPU: '{}'", .{diags.cpu_name.?}); + }, + error.UnknownCpuFeature => { + help: { + var help_text = std.ArrayList(u8).init(arena); + for (diags.arch.?.allFeaturesList()) |feature| { + help_text.writer().print(" {}: {}\n", .{ feature.name, feature.description }) catch break :help; + } + std.log.info("Available CPU features for architecture '{}': {}", .{ + @tagName(diags.arch.?), help_text.items, + }); + } + fatal("Unknown CPU feature: '{}'", .{diags.unknown_feature_name}); + }, + else => |e| return e, + }; + + const target_info = try detectNativeTargetInfo(gpa, cross_target); + + if (target_info.target.os.tag != .freestanding) { + if (ensure_libc_on_non_freestanding) + link_libc = true; + if (ensure_libcpp_on_non_freestanding) + link_libcpp = true; + } + + // Now that we have target info, we can find out if any of the system libraries + // are part of libc or libc++. We remove them from the list and communicate their + // existence via flags instead. + { + var i: usize = 0; + while (i < system_libs.items.len) { + const lib_name = system_libs.items[i]; + if (target_util.is_libc_lib_name(target_info.target, lib_name)) { + link_libc = true; + _ = system_libs.orderedRemove(i); + continue; + } + if (target_util.is_libcpp_lib_name(target_info.target, lib_name)) { + link_libcpp = true; + _ = system_libs.orderedRemove(i); + continue; + } + i += 1; + } + } + + if (cross_target.isNativeOs() and (system_libs.items.len != 0 or want_native_include_dirs)) { + const paths = std.zig.system.NativePaths.detect(arena) catch |err| { + fatal("unable to detect native system paths: {}", .{@errorName(err)}); + }; + for (paths.warnings.items) |warning| { + warn("{}", .{warning}); + } + try clang_argv.ensureCapacity(clang_argv.items.len + paths.include_dirs.items.len * 2); + for (paths.include_dirs.items) |include_dir| { + clang_argv.appendAssumeCapacity("-isystem"); + clang_argv.appendAssumeCapacity(include_dir); + } + for (paths.lib_dirs.items) |lib_dir| { + try lib_dirs.append(lib_dir); + } + for (paths.rpaths.items) |rpath| { + try rpath_list.append(rpath); + } + } + + const object_format: std.Target.ObjectFormat = blk: { + const ofmt = target_ofmt orelse break :blk target_info.target.getObjectFormat(); + if (mem.eql(u8, ofmt, "elf")) { + break :blk .elf; + } else if (mem.eql(u8, ofmt, "c")) { + break :blk .c; + } else if (mem.eql(u8, ofmt, "coff")) { + break :blk .coff; + } else if (mem.eql(u8, ofmt, "pe")) { + break :blk .pe; + } else if (mem.eql(u8, ofmt, "macho")) { + break :blk .macho; + } else if (mem.eql(u8, ofmt, "wasm")) { + break :blk .wasm; + } else if (mem.eql(u8, ofmt, "hex")) { + break :blk .hex; + } else if (mem.eql(u8, ofmt, "raw")) { + break :blk .raw; + } else { + fatal("unsupported object format: {}", .{ofmt}); + } + }; + + if (output_mode == .Obj and (object_format == .coff or object_format == .macho)) { + const total_obj_count = c_source_files.items.len + + @boolToInt(root_src_file != null) + + link_objects.items.len; + if (total_obj_count > 1) { + fatal("{s} does not support linking multiple objects into one", .{@tagName(object_format)}); + } + } + + var cleanup_emit_bin_dir: ?fs.Dir = null; + defer if (cleanup_emit_bin_dir) |*dir| dir.close(); + + const have_enable_cache = enable_cache orelse false; + const optional_version = if (have_version) version else null; + + const emit_bin_loc: ?Compilation.EmitLoc = switch (emit_bin) { + .no => null, + .yes_default_path => Compilation.EmitLoc{ + .directory = blk: { + switch (arg_mode) { + .run, .zig_test => break :blk null, + else => { + if (have_enable_cache) { + break :blk null; + } else { + break :blk .{ .path = null, .handle = fs.cwd() }; + } + }, + } + }, + .basename = try std.zig.binNameAlloc(arena, .{ + .root_name = root_name, + .target = target_info.target, + .output_mode = output_mode, + .link_mode = link_mode, + .object_format = object_format, + .version = optional_version, + }), + }, + .yes => |full_path| b: { + const basename = fs.path.basename(full_path); + if (have_enable_cache) { + break :b Compilation.EmitLoc{ + .basename = basename, + .directory = null, + }; + } + if (fs.path.dirname(full_path)) |dirname| { + const handle = fs.cwd().openDir(dirname, .{}) catch |err| { + fatal("unable to open output directory '{}': {}", .{ dirname, @errorName(err) }); + }; + cleanup_emit_bin_dir = handle; + break :b Compilation.EmitLoc{ + .basename = basename, + .directory = .{ + .path = dirname, + .handle = handle, + }, + }; + } else { + break :b Compilation.EmitLoc{ + .basename = basename, + .directory = .{ .path = null, .handle = fs.cwd() }, + }; + } + }, + }; + + const default_h_basename = try std.fmt.allocPrint(arena, "{}.h", .{root_name}); + var emit_h_resolved = try emit_h.resolve(default_h_basename); + defer emit_h_resolved.deinit(); + + const default_asm_basename = try std.fmt.allocPrint(arena, "{}.s", .{root_name}); + var emit_asm_resolved = try emit_asm.resolve(default_asm_basename); + defer emit_asm_resolved.deinit(); + + const default_llvm_ir_basename = try std.fmt.allocPrint(arena, "{}.ll", .{root_name}); + var emit_llvm_ir_resolved = try emit_llvm_ir.resolve(default_llvm_ir_basename); + defer emit_llvm_ir_resolved.deinit(); + + const default_analysis_basename = try std.fmt.allocPrint(arena, "{}-analysis.json", .{root_name}); + var emit_analysis_resolved = try emit_analysis.resolve(default_analysis_basename); + defer emit_analysis_resolved.deinit(); + + var emit_docs_resolved = try emit_docs.resolve("docs"); + defer emit_docs_resolved.deinit(); + + const zir_out_path: ?[]const u8 = switch (emit_zir) { + .no => null, + .yes_default_path => blk: { + if (root_src_file) |rsf| { + if (mem.endsWith(u8, rsf, ".zir")) { + break :blk try std.fmt.allocPrint(arena, "{}.out.zir", .{root_name}); + } + } + break :blk try std.fmt.allocPrint(arena, "{}.zir", .{root_name}); + }, + .yes => |p| p, + }; + + var cleanup_root_dir: ?fs.Dir = null; + defer if (cleanup_root_dir) |*dir| dir.close(); + + const root_pkg: ?*Package = if (root_src_file) |src_path| blk: { + if (main_pkg_path) |p| { + const dir = try fs.cwd().openDir(p, .{}); + cleanup_root_dir = dir; + root_pkg_memory.root_src_directory = .{ .path = p, .handle = dir }; + root_pkg_memory.root_src_path = try fs.path.relative(arena, p, src_path); + } else { + root_pkg_memory.root_src_directory = .{ .path = null, .handle = fs.cwd() }; + root_pkg_memory.root_src_path = src_path; + } + break :blk &root_pkg_memory; + } else null; + + const self_exe_path = try fs.selfExePathAlloc(arena); + var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |lib_dir| + .{ + .path = lib_dir, + .handle = try fs.cwd().openDir(lib_dir, .{}), + } + else + introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| { + fatal("unable to find zig installation directory: {}", .{@errorName(err)}); + }; + defer zig_lib_directory.handle.close(); + + const random_seed = blk: { + var random_seed: u64 = undefined; + try std.crypto.randomBytes(mem.asBytes(&random_seed)); + break :blk random_seed; + }; + var default_prng = std.rand.DefaultPrng.init(random_seed); + + var libc_installation: ?LibCInstallation = null; + defer if (libc_installation) |*l| l.deinit(gpa); + + if (libc_paths_file) |paths_file| { + libc_installation = LibCInstallation.parse(gpa, paths_file) catch |err| { + fatal("unable to parse libc paths file: {}", .{@errorName(err)}); + }; + } + + var global_cache_directory: Compilation.Directory = l: { + const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena); + break :l .{ + .handle = try fs.cwd().makeOpenPath(p, .{}), + .path = p, + }; + }; + defer global_cache_directory.handle.close(); + + var cleanup_local_cache_dir: ?fs.Dir = null; + defer if (cleanup_local_cache_dir) |*dir| dir.close(); + + var local_cache_directory: Compilation.Directory = l: { + if (override_local_cache_dir) |local_cache_dir_path| { + const dir = try fs.cwd().makeOpenPath(local_cache_dir_path, .{}); + cleanup_local_cache_dir = dir; + break :l .{ + .handle = dir, + .path = local_cache_dir_path, + }; + } + if (arg_mode == .run) { + break :l global_cache_directory; + } + const cache_dir_path = blk: { + if (root_pkg) |pkg| { + if (pkg.root_src_directory.path) |p| { + break :blk try fs.path.join(arena, &[_][]const u8{ p, "zig-cache" }); + } + } + break :blk "zig-cache"; + }; + const cache_parent_dir = if (root_pkg) |pkg| pkg.root_src_directory.handle else fs.cwd(); + const dir = try cache_parent_dir.makeOpenPath("zig-cache", .{}); + cleanup_local_cache_dir = dir; + break :l .{ + .handle = dir, + .path = cache_dir_path, + }; + }; + + if (build_options.have_llvm and emit_asm != .no) { + // LLVM has no way to set this non-globally. + const argv = [_][*:0]const u8{ "zig (LLVM option parsing)", "--x86-asm-syntax=intel" }; + @import("llvm.zig").ParseCommandLineOptions(argv.len, &argv); + } + + gimmeMoreOfThoseSweetSweetFileDescriptors(); + + const comp = Compilation.create(gpa, .{ + .zig_lib_directory = zig_lib_directory, + .local_cache_directory = local_cache_directory, + .global_cache_directory = global_cache_directory, + .root_name = root_name, + .target = target_info.target, + .is_native_os = cross_target.isNativeOs(), + .dynamic_linker = target_info.dynamic_linker.get(), + .output_mode = output_mode, + .root_pkg = root_pkg, + .emit_bin = emit_bin_loc, + .emit_h = emit_h_resolved.data, + .emit_asm = emit_asm_resolved.data, + .emit_llvm_ir = emit_llvm_ir_resolved.data, + .emit_docs = emit_docs_resolved.data, + .emit_analysis = emit_analysis_resolved.data, + .link_mode = link_mode, + .dll_export_fns = dll_export_fns, + .object_format = object_format, + .optimize_mode = optimize_mode, + .keep_source_files_loaded = zir_out_path != null, + .clang_argv = clang_argv.items, + .lld_argv = lld_argv.items, + .lib_dirs = lib_dirs.items, + .rpath_list = rpath_list.items, + .c_source_files = c_source_files.items, + .link_objects = link_objects.items, + .framework_dirs = framework_dirs.items, + .frameworks = frameworks.items, + .system_libs = system_libs.items, + .link_libc = link_libc, + .link_libcpp = link_libcpp, + .want_pic = want_pic, + .want_sanitize_c = want_sanitize_c, + .want_stack_check = want_stack_check, + .want_valgrind = want_valgrind, + .use_llvm = use_llvm, + .use_lld = use_lld, + .use_clang = use_clang, + .rdynamic = rdynamic, + .linker_script = linker_script, + .version_script = version_script, + .disable_c_depfile = disable_c_depfile, + .override_soname = override_soname, + .linker_gc_sections = linker_gc_sections, + .linker_allow_shlib_undefined = linker_allow_shlib_undefined, + .linker_bind_global_refs_locally = linker_bind_global_refs_locally, + .linker_z_nodelete = linker_z_nodelete, + .linker_z_defs = linker_z_defs, + .link_eh_frame_hdr = link_eh_frame_hdr, + .stack_size_override = stack_size_override, + .strip = strip, + .single_threaded = single_threaded, + .function_sections = function_sections, + .self_exe_path = self_exe_path, + .rand = &default_prng.random, + .clang_passthrough_mode = arg_mode != .build, + .clang_preprocessor_mode = clang_preprocessor_mode, + .version = optional_version, + .libc_installation = if (libc_installation) |*lci| lci else null, + .verbose_cc = verbose_cc, + .verbose_link = verbose_link, + .verbose_tokenize = verbose_tokenize, + .verbose_ast = verbose_ast, + .verbose_ir = verbose_ir, + .verbose_llvm_ir = verbose_llvm_ir, + .verbose_cimport = verbose_cimport, + .verbose_llvm_cpu_features = verbose_llvm_cpu_features, + .machine_code_model = machine_code_model, + .color = color, + .time_report = time_report, + .stack_report = stack_report, + .is_test = arg_mode == .zig_test, + .each_lib_rpath = each_lib_rpath, + .test_evented_io = test_evented_io, + .test_filter = test_filter, + .test_name_prefix = test_name_prefix, + .disable_lld_caching = !have_enable_cache, + .subsystem = subsystem, + }) catch |err| { + fatal("unable to create compilation: {}", .{@errorName(err)}); + }; + defer comp.destroy(); + + if (show_builtin) { + return std.io.getStdOut().writeAll(try comp.generateBuiltinZigSource(arena)); + } + if (arg_mode == .translate_c) { + return cmdTranslateC(comp, arena, have_enable_cache); + } + + const hook: AfterUpdateHook = blk: { + if (!have_enable_cache) + break :blk .none; + + switch (emit_bin) { + .no => break :blk .none, + .yes_default_path => break :blk .{ + .print = comp.bin_file.options.emit.?.directory.path orelse ".", + }, + .yes => |full_path| break :blk .{ .update = full_path }, + } + }; + + try updateModule(gpa, comp, zir_out_path, hook); + + if (build_options.is_stage1 and comp.stage1_lock != null and watch) { + warn("--watch is not recommended with the stage1 backend; it leaks memory and is not capable of incremental compilation", .{}); + } + + switch (arg_mode) { + .run, .zig_test => run: { + const exe_loc = emit_bin_loc orelse break :run; + const exe_directory = exe_loc.directory orelse comp.bin_file.options.emit.?.directory; + const exe_path = try fs.path.join(arena, &[_][]const u8{ + exe_directory.path orelse ".", exe_loc.basename, + }); + + var argv = std.ArrayList([]const u8).init(gpa); + defer argv.deinit(); + + if (test_exec_args.items.len == 0) { + if (!std.Target.current.canExecBinariesOf(target_info.target)) { + switch (arg_mode) { + .zig_test => { + warn("created {s} but skipping execution because it is non-native", .{exe_path}); + if (!watch) return cleanExit(); + break :run; + }, + .run => fatal("unable to execute {s}: non-native", .{exe_path}), + else => unreachable, + } + } + try argv.append(exe_path); + } else { + for (test_exec_args.items) |arg| { + try argv.append(arg orelse exe_path); + } + } + if (runtime_args_start) |i| { + try argv.appendSlice(all_args[i..]); + } + // TODO On operating systems that support it, do an execve here rather than child process, + // when watch=false and arg_mode == .run + const child = try std.ChildProcess.init(argv.items, gpa); + defer child.deinit(); + + child.stdin_behavior = .Inherit; + child.stdout_behavior = .Inherit; + child.stderr_behavior = .Inherit; + + const term = try child.spawnAndWait(); + switch (arg_mode) { + .run => { + switch (term) { + .Exited => |code| { + if (code == 0) { + if (!watch) return cleanExit(); + } else { + // TODO https://github.com/ziglang/zig/issues/6342 + process.exit(1); + } + }, + else => process.exit(1), + } + }, + .zig_test => { + switch (term) { + .Exited => |code| { + if (code == 0) { + if (!watch) return cleanExit(); + } else { + const cmd = try argvCmd(arena, argv.items); + fatal("the following test command failed with exit code {}:\n{}", .{ code, cmd }); + } + }, + else => { + const cmd = try argvCmd(arena, argv.items); + fatal("the following test command crashed:\n{}", .{cmd}); + }, + } + }, + else => unreachable, + } + }, + else => {}, + } + + const stdin = std.io.getStdIn().inStream(); + const stderr = std.io.getStdErr().outStream(); + var repl_buf: [1024]u8 = undefined; + + while (watch) { + try stderr.print("(zig) ", .{}); + if (output_mode == .Exe) { + try comp.makeBinFileExecutable(); + } + if (stdin.readUntilDelimiterOrEof(&repl_buf, '\n') catch |err| { + try stderr.print("\nUnable to parse command: {}\n", .{@errorName(err)}); + continue; + }) |line| { + const actual_line = mem.trimRight(u8, line, "\r\n "); + + if (mem.eql(u8, actual_line, "update")) { + if (output_mode == .Exe) { + try comp.makeBinFileWritable(); + } + try updateModule(gpa, comp, zir_out_path, hook); + } else if (mem.eql(u8, actual_line, "exit")) { + break; + } else if (mem.eql(u8, actual_line, "help")) { + try stderr.writeAll(repl_help); + } else { + try stderr.print("unknown command: {}\n", .{actual_line}); + } + } else { + break; + } + } +} + +const AfterUpdateHook = union(enum) { + none, + print: []const u8, + update: []const u8, +}; + +fn updateModule(gpa: *Allocator, comp: *Compilation, zir_out_path: ?[]const u8, hook: AfterUpdateHook) !void { + try comp.update(); + + var errors = try comp.getAllErrorsAlloc(); + defer errors.deinit(comp.gpa); + + if (errors.list.len != 0) { + for (errors.list) |full_err_msg| { + full_err_msg.renderToStdErr(); + } + } else switch (hook) { + .none => {}, + .print => |bin_path| try io.getStdOut().writer().print("{s}\n", .{bin_path}), + .update => |full_path| _ = try comp.bin_file.options.emit.?.directory.handle.updateFile( + comp.bin_file.options.emit.?.sub_path, + fs.cwd(), + full_path, + .{}, + ), + } + + if (zir_out_path) |zop| { + const module = comp.bin_file.options.module orelse + fatal("-femit-zir with no zig source code", .{}); + var new_zir_module = try zir.emit(gpa, module); + defer new_zir_module.deinit(gpa); + + const baf = try io.BufferedAtomicFile.create(gpa, fs.cwd(), zop, .{}); + defer baf.destroy(); + + try new_zir_module.writeToStream(gpa, baf.stream()); + + try baf.finish(); + } +} + +fn cmdTranslateC(comp: *Compilation, arena: *Allocator, enable_cache: bool) !void { + if (!build_options.have_llvm) + fatal("cannot translate-c: compiler built without LLVM extensions", .{}); + + assert(comp.c_source_files.len == 1); + const c_source_file = comp.c_source_files[0]; + + const translated_zig_basename = try std.fmt.allocPrint(arena, "{}.zig", .{comp.bin_file.options.root_name}); + + var man: Cache.Manifest = comp.obtainCObjectCacheManifest(); + defer if (enable_cache) man.deinit(); + + man.hash.add(@as(u16, 0xb945)); // Random number to distinguish translate-c from compiling C objects + _ = man.addFile(c_source_file.src_path, null) catch |err| { + fatal("unable to process '{}': {}", .{ c_source_file.src_path, @errorName(err) }); + }; + + const digest = if (try man.hit()) man.final() else digest: { + var argv = std.ArrayList([]const u8).init(arena); + + var zig_cache_tmp_dir = try comp.local_cache_directory.handle.makeOpenPath("tmp", .{}); + defer zig_cache_tmp_dir.close(); + + const ext = Compilation.classifyFileExt(c_source_file.src_path); + const out_dep_path: ?[]const u8 = blk: { + if (comp.disable_c_depfile or !ext.clangSupportsDepFile()) + break :blk null; + + const c_src_basename = fs.path.basename(c_source_file.src_path); + const dep_basename = try std.fmt.allocPrint(arena, "{}.d", .{c_src_basename}); + const out_dep_path = try comp.tmpFilePath(arena, dep_basename); + break :blk out_dep_path; + }; + + try comp.addTranslateCCArgs(arena, &argv, ext, out_dep_path); + try argv.append(c_source_file.src_path); + + if (comp.verbose_cc) { + std.debug.print("clang ", .{}); + Compilation.dump_argv(argv.items); + } + + // Convert to null terminated args. + const new_argv_with_sentinel = try arena.alloc(?[*:0]const u8, argv.items.len + 1); + new_argv_with_sentinel[argv.items.len] = null; + const new_argv = new_argv_with_sentinel[0..argv.items.len :null]; + for (argv.items) |arg, i| { + new_argv[i] = try arena.dupeZ(u8, arg); + } + + const c_headers_dir_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{"include"}); + const c_headers_dir_path_z = try arena.dupeZ(u8, c_headers_dir_path); + var clang_errors: []translate_c.ClangErrMsg = &[0]translate_c.ClangErrMsg{}; + const tree = translate_c.translate( + comp.gpa, + new_argv.ptr, + new_argv.ptr + new_argv.len, + &clang_errors, + c_headers_dir_path_z, + ) catch |err| switch (err) { + error.OutOfMemory => return error.OutOfMemory, + error.ASTUnitFailure => fatal("clang API returned errors but due to a clang bug, it is not exposing the errors for zig to see. For more details: https://github.com/ziglang/zig/issues/4455", .{}), + error.SemanticAnalyzeFail => { + for (clang_errors) |clang_err| { + std.debug.print("{}:{}:{}: {}\n", .{ + if (clang_err.filename_ptr) |p| p[0..clang_err.filename_len] else "(no file)", + clang_err.line + 1, + clang_err.column + 1, + clang_err.msg_ptr[0..clang_err.msg_len], + }); + } + process.exit(1); + }, + }; + defer tree.deinit(); + + if (out_dep_path) |dep_file_path| { + const dep_basename = std.fs.path.basename(dep_file_path); + // Add the files depended on to the cache system. + try man.addDepFilePost(zig_cache_tmp_dir, dep_basename); + // Just to save disk space, we delete the file because it is never needed again. + zig_cache_tmp_dir.deleteFile(dep_basename) catch |err| { + warn("failed to delete '{}': {}", .{ dep_file_path, @errorName(err) }); + }; + } + + const digest = man.final(); + const o_sub_path = try fs.path.join(arena, &[_][]const u8{ "o", &digest }); + var o_dir = try comp.local_cache_directory.handle.makeOpenPath(o_sub_path, .{}); + defer o_dir.close(); + var zig_file = try o_dir.createFile(translated_zig_basename, .{}); + defer zig_file.close(); + + var bos = io.bufferedOutStream(zig_file.writer()); + _ = try std.zig.render(comp.gpa, bos.writer(), tree); + try bos.flush(); + + man.writeManifest() catch |err| warn("failed to write cache manifest: {}", .{@errorName(err)}); + + break :digest digest; + }; + + if (enable_cache) { + const full_zig_path = try comp.local_cache_directory.join(arena, &[_][]const u8{ + "o", &digest, translated_zig_basename, + }); + try io.getStdOut().writer().print("{}\n", .{full_zig_path}); + return cleanExit(); + } else { + const out_zig_path = try fs.path.join(arena, &[_][]const u8{ "o", &digest, translated_zig_basename }); + const zig_file = try comp.local_cache_directory.handle.openFile(out_zig_path, .{}); + defer zig_file.close(); + try io.getStdOut().writeFileAll(zig_file, .{}); + return cleanExit(); + } +} + +pub const usage_libc = + \\Usage: zig libc + \\ + \\ Detect the native libc installation and print the resulting + \\ paths to stdout. You can save this into a file and then edit + \\ the paths to create a cross compilation libc kit. Then you + \\ can pass `--libc [file]` for Zig to use it. + \\ + \\Usage: zig libc [paths_file] + \\ + \\ Parse a libc installation text file and validate it. + \\ +; + +pub fn cmdLibC(gpa: *Allocator, args: []const []const u8) !void { + var input_file: ?[]const u8 = null; + { + var i: usize = 0; + while (i < args.len) : (i += 1) { + const arg = args[i]; + if (mem.startsWith(u8, arg, "-")) { + if (mem.eql(u8, arg, "--help")) { + const stdout = io.getStdOut().writer(); + try stdout.writeAll(usage_libc); + return cleanExit(); + } else { + fatal("unrecognized parameter: '{}'", .{arg}); + } + } else if (input_file != null) { + fatal("unexpected extra parameter: '{}'", .{arg}); + } else { + input_file = arg; + } + } + } + if (input_file) |libc_file| { + var libc = LibCInstallation.parse(gpa, libc_file) catch |err| { + fatal("unable to parse libc file: {}", .{@errorName(err)}); + }; + defer libc.deinit(gpa); + } else { + var libc = LibCInstallation.findNative(.{ + .allocator = gpa, + .verbose = true, + }) catch |err| { + fatal("unable to detect native libc: {}", .{@errorName(err)}); + }; + defer libc.deinit(gpa); + + var bos = io.bufferedOutStream(io.getStdOut().writer()); + try libc.render(bos.writer()); + try bos.flush(); + } +} + +pub const usage_init = + \\Usage: zig init-exe + \\ zig init-lib + \\ + \\ Initializes a `zig build` project in the current working + \\ directory. + \\ + \\Options: + \\ --help Print this help and exit + \\ + \\ +; + +pub fn cmdInit( + gpa: *Allocator, + arena: *Allocator, + args: []const []const u8, + output_mode: std.builtin.OutputMode, +) !void { + { + var i: usize = 0; + while (i < args.len) : (i += 1) { + const arg = args[i]; + if (mem.startsWith(u8, arg, "-")) { + if (mem.eql(u8, arg, "--help")) { + try io.getStdOut().writeAll(usage_init); + return cleanExit(); + } else { + fatal("unrecognized parameter: '{}'", .{arg}); + } + } else { + fatal("unexpected extra parameter: '{}'", .{arg}); + } + } + } + const self_exe_path = try fs.selfExePathAlloc(arena); + var zig_lib_directory = introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| { + fatal("unable to find zig installation directory: {}\n", .{@errorName(err)}); + }; + defer zig_lib_directory.handle.close(); + + const s = fs.path.sep_str; + const template_sub_path = switch (output_mode) { + .Obj => unreachable, + .Lib => "std" ++ s ++ "special" ++ s ++ "init-lib", + .Exe => "std" ++ s ++ "special" ++ s ++ "init-exe", + }; + var template_dir = try zig_lib_directory.handle.openDir(template_sub_path, .{}); + defer template_dir.close(); + + const cwd_path = try process.getCwdAlloc(arena); + const cwd_basename = fs.path.basename(cwd_path); + + const max_bytes = 10 * 1024 * 1024; + const build_zig_contents = template_dir.readFileAlloc(arena, "build.zig", max_bytes) catch |err| { + fatal("unable to read template file 'build.zig': {}", .{@errorName(err)}); + }; + var modified_build_zig_contents = std.ArrayList(u8).init(arena); + try modified_build_zig_contents.ensureCapacity(build_zig_contents.len); + for (build_zig_contents) |c| { + if (c == '$') { + try modified_build_zig_contents.appendSlice(cwd_basename); + } else { + try modified_build_zig_contents.append(c); + } + } + const main_zig_contents = template_dir.readFileAlloc(arena, "src" ++ s ++ "main.zig", max_bytes) catch |err| { + fatal("unable to read template file 'main.zig': {}", .{@errorName(err)}); + }; + if (fs.cwd().access("build.zig", .{})) |_| { + fatal("existing build.zig file would be overwritten", .{}); + } else |err| switch (err) { + error.FileNotFound => {}, + else => fatal("unable to test existence of build.zig: {}\n", .{@errorName(err)}), + } + var src_dir = try fs.cwd().makeOpenPath("src", .{}); + defer src_dir.close(); + + try src_dir.writeFile("main.zig", main_zig_contents); + try fs.cwd().writeFile("build.zig", modified_build_zig_contents.items); + + std.log.info("Created build.zig", .{}); + std.log.info("Created src" ++ s ++ "main.zig", .{}); + + switch (output_mode) { + .Lib => std.log.info("Next, try `zig build --help` or `zig build test`", .{}), + .Exe => std.log.info("Next, try `zig build --help` or `zig build run`", .{}), + .Obj => unreachable, + } +} + +pub const usage_build = + \\Usage: zig build [steps] [options] + \\ + \\ Build a project from build.zig. + \\ + \\Options: + \\ --help Print this help and exit + \\ + \\ +; + +pub fn cmdBuild(gpa: *Allocator, arena: *Allocator, args: []const []const u8) !void { + // We want to release all the locks before executing the child process, so we make a nice + // big block here to ensure the cleanup gets run when we extract out our argv. + const lock_and_argv = lock_and_argv: { + const self_exe_path = try fs.selfExePathAlloc(arena); + + var build_file: ?[]const u8 = null; + var override_lib_dir: ?[]const u8 = null; + var override_global_cache_dir: ?[]const u8 = null; + var override_local_cache_dir: ?[]const u8 = null; + var child_argv = std.ArrayList([]const u8).init(arena); + + const argv_index_exe = child_argv.items.len; + _ = try child_argv.addOne(); + + try child_argv.append(self_exe_path); + + const argv_index_build_file = child_argv.items.len; + _ = try child_argv.addOne(); + + const argv_index_cache_dir = child_argv.items.len; + _ = try child_argv.addOne(); + + { + var i: usize = 0; + while (i < args.len) : (i += 1) { + const arg = args[i]; + if (mem.startsWith(u8, arg, "-")) { + if (mem.eql(u8, arg, "--build-file")) { + if (i + 1 >= args.len) fatal("expected argument after '{}'", .{arg}); + i += 1; + build_file = args[i]; + continue; + } else if (mem.eql(u8, arg, "--override-lib-dir")) { + if (i + 1 >= args.len) fatal("expected argument after '{}'", .{arg}); + i += 1; + override_lib_dir = args[i]; + try child_argv.appendSlice(&[_][]const u8{ arg, args[i] }); + continue; + } else if (mem.eql(u8, arg, "--cache-dir")) { + if (i + 1 >= args.len) fatal("expected argument after '{}'", .{arg}); + i += 1; + override_local_cache_dir = args[i]; + try child_argv.appendSlice(&[_][]const u8{ arg, args[i] }); + continue; + } else if (mem.eql(u8, arg, "--global-cache-dir")) { + if (i + 1 >= args.len) fatal("expected argument after '{}'", .{arg}); + i += 1; + override_global_cache_dir = args[i]; + try child_argv.appendSlice(&[_][]const u8{ arg, args[i] }); + continue; + } + } + try child_argv.append(arg); + } + } + + var zig_lib_directory: Compilation.Directory = if (override_lib_dir) |lib_dir| + .{ + .path = lib_dir, + .handle = try fs.cwd().openDir(lib_dir, .{}), + } + else + introspect.findZigLibDirFromSelfExe(arena, self_exe_path) catch |err| { + fatal("unable to find zig installation directory: {}", .{@errorName(err)}); + }; + defer zig_lib_directory.handle.close(); + + const std_special = "std" ++ fs.path.sep_str ++ "special"; + const special_dir_path = try zig_lib_directory.join(arena, &[_][]const u8{std_special}); + + var root_pkg: Package = .{ + .root_src_directory = .{ + .path = special_dir_path, + .handle = try zig_lib_directory.handle.openDir(std_special, .{}), + }, + .root_src_path = "build_runner.zig", + }; + defer root_pkg.root_src_directory.handle.close(); + + var cleanup_build_dir: ?fs.Dir = null; + defer if (cleanup_build_dir) |*dir| dir.close(); + + const cwd_path = try process.getCwdAlloc(arena); + const build_zig_basename = if (build_file) |bf| fs.path.basename(bf) else "build.zig"; + const build_directory: Compilation.Directory = blk: { + if (build_file) |bf| { + if (fs.path.dirname(bf)) |dirname| { + const dir = try fs.cwd().openDir(dirname, .{}); + cleanup_build_dir = dir; + break :blk .{ .path = dirname, .handle = dir }; + } + + break :blk .{ .path = null, .handle = fs.cwd() }; + } + // Search up parent directories until we find build.zig. + var dirname: []const u8 = cwd_path; + while (true) { + const joined_path = try fs.path.join(arena, &[_][]const u8{ dirname, build_zig_basename }); + if (fs.cwd().access(joined_path, .{})) |_| { + const dir = try fs.cwd().openDir(dirname, .{}); + break :blk .{ .path = dirname, .handle = dir }; + } else |err| switch (err) { + error.FileNotFound => { + dirname = fs.path.dirname(dirname) orelse { + std.log.info("{}", .{ + \\Initialize a 'build.zig' template file with `zig init-lib` or `zig init-exe`, + \\or see `zig --help` for more options. + }); + fatal("No 'build.zig' file found, in the current directory or any parent directories.", .{}); + }; + continue; + }, + else => |e| return e, + } + } + }; + child_argv.items[argv_index_build_file] = build_directory.path orelse cwd_path; + + var build_pkg: Package = .{ + .root_src_directory = build_directory, + .root_src_path = build_zig_basename, + }; + try root_pkg.table.put(arena, "@build", &build_pkg); + + var global_cache_directory: Compilation.Directory = l: { + const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena); + break :l .{ + .handle = try fs.cwd().makeOpenPath(p, .{}), + .path = p, + }; + }; + defer global_cache_directory.handle.close(); + + var local_cache_directory: Compilation.Directory = l: { + if (override_local_cache_dir) |local_cache_dir_path| { + break :l .{ + .handle = try fs.cwd().makeOpenPath(local_cache_dir_path, .{}), + .path = local_cache_dir_path, + }; + } + const cache_dir_path = try build_directory.join(arena, &[_][]const u8{"zig-cache"}); + break :l .{ + .handle = try build_directory.handle.makeOpenPath("zig-cache", .{}), + .path = cache_dir_path, + }; + }; + defer local_cache_directory.handle.close(); + + child_argv.items[argv_index_cache_dir] = local_cache_directory.path orelse cwd_path; + + gimmeMoreOfThoseSweetSweetFileDescriptors(); + + const cross_target: std.zig.CrossTarget = .{}; + const target_info = try detectNativeTargetInfo(gpa, cross_target); + + const exe_basename = try std.zig.binNameAlloc(arena, .{ + .root_name = "build", + .target = target_info.target, + .output_mode = .Exe, + }); + const emit_bin: Compilation.EmitLoc = .{ + .directory = null, // Use the local zig-cache. + .basename = exe_basename, + }; + const random_seed = blk: { + var random_seed: u64 = undefined; + try std.crypto.randomBytes(mem.asBytes(&random_seed)); + break :blk random_seed; + }; + var default_prng = std.rand.DefaultPrng.init(random_seed); + const comp = Compilation.create(gpa, .{ + .zig_lib_directory = zig_lib_directory, + .local_cache_directory = local_cache_directory, + .global_cache_directory = global_cache_directory, + .root_name = "build", + .target = target_info.target, + .is_native_os = cross_target.isNativeOs(), + .dynamic_linker = target_info.dynamic_linker.get(), + .output_mode = .Exe, + .root_pkg = &root_pkg, + .emit_bin = emit_bin, + .emit_h = null, + .optimize_mode = .Debug, + .self_exe_path = self_exe_path, + .rand = &default_prng.random, + }) catch |err| { + fatal("unable to create compilation: {}", .{@errorName(err)}); + }; + defer comp.destroy(); + + try updateModule(gpa, comp, null, .none); + + child_argv.items[argv_index_exe] = try comp.bin_file.options.emit.?.directory.join( + arena, + &[_][]const u8{exe_basename}, + ); + + break :lock_and_argv .{ + .child_argv = child_argv.items, + .lock = comp.bin_file.toOwnedLock(), + }; + }; + const child_argv = lock_and_argv.child_argv; + var lock = lock_and_argv.lock; + defer lock.release(); + + const child = try std.ChildProcess.init(child_argv, gpa); + defer child.deinit(); + + child.stdin_behavior = .Inherit; + child.stdout_behavior = .Inherit; + child.stderr_behavior = .Inherit; + + const term = try child.spawnAndWait(); + switch (term) { + .Exited => |code| { + if (code == 0) return cleanExit(); + const cmd = try argvCmd(arena, child_argv); + fatal("the following build command failed with exit code {}:\n{}", .{ code, cmd }); + }, + else => { + const cmd = try argvCmd(arena, child_argv); + fatal("the following build command crashed:\n{}", .{cmd}); + }, + } +} + +fn argvCmd(allocator: *Allocator, argv: []const []const u8) ![]u8 { + var cmd = std.ArrayList(u8).init(allocator); + defer cmd.deinit(); + for (argv[0 .. argv.len - 1]) |arg| { + try cmd.appendSlice(arg); + try cmd.append(' '); + } + try cmd.appendSlice(argv[argv.len - 1]); + return cmd.toOwnedSlice(); +} + +pub const usage_fmt = + \\Usage: zig fmt [file]... + \\ + \\ Formats the input files and modifies them in-place. + \\ Arguments can be files or directories, which are searched + \\ recursively. + \\ + \\Options: + \\ --help Print this help and exit + \\ --color [auto|off|on] Enable or disable colored error messages + \\ --stdin Format code from stdin; output to stdout + \\ --check List non-conforming files and exit with an error + \\ if the list is non-empty + \\ + \\ +; + +const Fmt = struct { + seen: SeenMap, + any_error: bool, + color: Color, + gpa: *Allocator, + out_buffer: std.ArrayList(u8), + + const SeenMap = std.AutoHashMap(fs.File.INode, void); +}; + +pub fn cmdFmt(gpa: *Allocator, args: []const []const u8) !void { + const stderr_file = io.getStdErr(); + var color: Color = .Auto; + var stdin_flag: bool = false; + var check_flag: bool = false; + var input_files = ArrayList([]const u8).init(gpa); + + { + var i: usize = 0; + while (i < args.len) : (i += 1) { + const arg = args[i]; + if (mem.startsWith(u8, arg, "-")) { + if (mem.eql(u8, arg, "--help")) { + const stdout = io.getStdOut().outStream(); + try stdout.writeAll(usage_fmt); + return cleanExit(); + } else if (mem.eql(u8, arg, "--color")) { + if (i + 1 >= args.len) { + fatal("expected [auto|on|off] after --color", .{}); + } + i += 1; + const next_arg = args[i]; + if (mem.eql(u8, next_arg, "auto")) { + color = .Auto; + } else if (mem.eql(u8, next_arg, "on")) { + color = .On; + } else if (mem.eql(u8, next_arg, "off")) { + color = .Off; + } else { + fatal("expected [auto|on|off] after --color, found '{}'", .{next_arg}); + } + } else if (mem.eql(u8, arg, "--stdin")) { + stdin_flag = true; + } else if (mem.eql(u8, arg, "--check")) { + check_flag = true; + } else { + fatal("unrecognized parameter: '{}'", .{arg}); + } + } else { + try input_files.append(arg); + } + } + } + + if (stdin_flag) { + if (input_files.items.len != 0) { + fatal("cannot use --stdin with positional arguments", .{}); + } + + const stdin = io.getStdIn().inStream(); + + const source_code = try stdin.readAllAlloc(gpa, max_src_size); + defer gpa.free(source_code); + + const tree = std.zig.parse(gpa, source_code) catch |err| { + fatal("error parsing stdin: {}", .{err}); + }; + defer tree.deinit(); + + for (tree.errors) |parse_error| { + try printErrMsgToFile(gpa, parse_error, tree, "", stderr_file, color); + } + if (tree.errors.len != 0) { + process.exit(1); + } + if (check_flag) { + const anything_changed = try std.zig.render(gpa, io.null_out_stream, tree); + const code = if (anything_changed) @as(u8, 1) else @as(u8, 0); + process.exit(code); + } + + var bos = io.bufferedOutStream(io.getStdOut().writer()); + _ = try std.zig.render(gpa, bos.writer(), tree); + try bos.flush(); + return; + } + + if (input_files.items.len == 0) { + fatal("expected at least one source file argument", .{}); + } + + var fmt = Fmt{ + .gpa = gpa, + .seen = Fmt.SeenMap.init(gpa), + .any_error = false, + .color = color, + .out_buffer = std.ArrayList(u8).init(gpa), + }; + defer fmt.seen.deinit(); + defer fmt.out_buffer.deinit(); + + for (input_files.span()) |file_path| { + // Get the real path here to avoid Windows failing on relative file paths with . or .. in them. + const real_path = fs.realpathAlloc(gpa, file_path) catch |err| { + fatal("unable to open '{}': {}", .{ file_path, err }); + }; + defer gpa.free(real_path); + + try fmtPath(&fmt, file_path, check_flag, fs.cwd(), real_path); + } + if (fmt.any_error) { + process.exit(1); + } +} + +const FmtError = error{ + SystemResources, + OperationAborted, + IoPending, + BrokenPipe, + Unexpected, + WouldBlock, + FileClosed, + DestinationAddressRequired, + DiskQuota, + FileTooBig, + InputOutput, + NoSpaceLeft, + AccessDenied, + OutOfMemory, + RenameAcrossMountPoints, + ReadOnlyFileSystem, + LinkQuotaExceeded, + FileBusy, + EndOfStream, + Unseekable, + NotOpenForWriting, +} || fs.File.OpenError; + +fn fmtPath(fmt: *Fmt, file_path: []const u8, check_mode: bool, dir: fs.Dir, sub_path: []const u8) FmtError!void { + fmtPathFile(fmt, file_path, check_mode, dir, sub_path) catch |err| switch (err) { + error.IsDir, error.AccessDenied => return fmtPathDir(fmt, file_path, check_mode, dir, sub_path), + else => { + warn("unable to format '{}': {}", .{ file_path, err }); + fmt.any_error = true; + return; + }, + }; +} + +fn fmtPathDir( + fmt: *Fmt, + file_path: []const u8, + check_mode: bool, + parent_dir: fs.Dir, + parent_sub_path: []const u8, +) FmtError!void { + var dir = try parent_dir.openDir(parent_sub_path, .{ .iterate = true }); + defer dir.close(); + + const stat = try dir.stat(); + if (try fmt.seen.fetchPut(stat.inode, {})) |_| return; + + var dir_it = dir.iterate(); + while (try dir_it.next()) |entry| { + const is_dir = entry.kind == .Directory; + if (is_dir or mem.endsWith(u8, entry.name, ".zig")) { + const full_path = try fs.path.join(fmt.gpa, &[_][]const u8{ file_path, entry.name }); + defer fmt.gpa.free(full_path); + + if (is_dir) { + try fmtPathDir(fmt, full_path, check_mode, dir, entry.name); + } else { + fmtPathFile(fmt, full_path, check_mode, dir, entry.name) catch |err| { + warn("unable to format '{}': {}", .{ full_path, err }); + fmt.any_error = true; + return; + }; + } + } + } +} + +fn fmtPathFile( + fmt: *Fmt, + file_path: []const u8, + check_mode: bool, + dir: fs.Dir, + sub_path: []const u8, +) FmtError!void { + const source_file = try dir.openFile(sub_path, .{}); + var file_closed = false; + errdefer if (!file_closed) source_file.close(); + + const stat = try source_file.stat(); + + if (stat.kind == .Directory) + return error.IsDir; + + const source_code = source_file.readToEndAllocOptions( + fmt.gpa, + max_src_size, + stat.size, + @alignOf(u8), + null, + ) catch |err| switch (err) { + error.ConnectionResetByPeer => unreachable, + error.ConnectionTimedOut => unreachable, + error.NotOpenForReading => unreachable, + else => |e| return e, + }; + source_file.close(); + file_closed = true; + defer fmt.gpa.free(source_code); + + // Add to set after no longer possible to get error.IsDir. + if (try fmt.seen.fetchPut(stat.inode, {})) |_| return; + + const tree = try std.zig.parse(fmt.gpa, source_code); + defer tree.deinit(); + + for (tree.errors) |parse_error| { + try printErrMsgToFile(fmt.gpa, parse_error, tree, file_path, std.io.getStdErr(), fmt.color); + } + if (tree.errors.len != 0) { + fmt.any_error = true; + return; + } + + if (check_mode) { + const anything_changed = try std.zig.render(fmt.gpa, io.null_out_stream, tree); + if (anything_changed) { + // TODO this should output to stdout instead of stderr. + std.debug.print("{}\n", .{file_path}); + fmt.any_error = true; + } + } else { + // As a heuristic, we make enough capacity for the same as the input source. + try fmt.out_buffer.ensureCapacity(source_code.len); + fmt.out_buffer.items.len = 0; + const writer = fmt.out_buffer.writer(); + const anything_changed = try std.zig.render(fmt.gpa, writer, tree); + if (!anything_changed) + return; // Good thing we didn't waste any file system access on this. + + var af = try dir.atomicFile(sub_path, .{ .mode = stat.mode }); + defer af.deinit(); + + try af.file.writeAll(fmt.out_buffer.items); + try af.finish(); + // TODO this should output to stdout instead of stderr. + std.debug.print("{}\n", .{file_path}); + } +} + +fn printErrMsgToFile( + gpa: *mem.Allocator, + parse_error: ast.Error, + tree: *ast.Tree, + path: []const u8, + file: fs.File, + color: Color, +) !void { + const color_on = switch (color) { + .Auto => file.isTty(), + .On => true, + .Off => false, + }; + const lok_token = parse_error.loc(); + const span_first = lok_token; + const span_last = lok_token; + + const first_token = tree.token_locs[span_first]; + const last_token = tree.token_locs[span_last]; + const start_loc = tree.tokenLocationLoc(0, first_token); + const end_loc = tree.tokenLocationLoc(first_token.end, last_token); + + var text_buf = std.ArrayList(u8).init(gpa); + defer text_buf.deinit(); + const out_stream = text_buf.outStream(); + try parse_error.render(tree.token_ids, out_stream); + const text = text_buf.span(); + + const stream = file.outStream(); + try stream.print("{}:{}:{}: error: {}\n", .{ path, start_loc.line + 1, start_loc.column + 1, text }); + + if (!color_on) return; + + // Print \r and \t as one space each so that column counts line up + for (tree.source[start_loc.line_start..start_loc.line_end]) |byte| { + try stream.writeByte(switch (byte) { + '\r', '\t' => ' ', + else => byte, + }); + } + try stream.writeByte('\n'); + try stream.writeByteNTimes(' ', start_loc.column); + try stream.writeByteNTimes('~', last_token.end - first_token.start); + try stream.writeByte('\n'); +} + +pub const info_zen = + \\ + \\ * Communicate intent precisely. + \\ * Edge cases matter. + \\ * Favor reading code over writing code. + \\ * Only one obvious way to do things. + \\ * Runtime crashes are better than bugs. + \\ * Compile errors are better than runtime crashes. + \\ * Incremental improvements. + \\ * Avoid local maximums. + \\ * Reduce the amount one must remember. + \\ * Focus on code rather than style. + \\ * Resource allocation may fail; resource deallocation must succeed. + \\ * Memory is a resource. + \\ * Together we serve the users. + \\ + \\ +; + +extern "c" fn ZigClang_main(argc: c_int, argv: [*:null]?[*:0]u8) c_int; + +/// TODO https://github.com/ziglang/zig/issues/3257 +fn punt_to_clang(arena: *Allocator, args: []const []const u8) error{OutOfMemory} { + if (!build_options.have_llvm) + fatal("`zig cc` and `zig c++` unavailable: compiler built without LLVM extensions", .{}); + // Convert the args to the format Clang expects. + const argv = try arena.alloc(?[*:0]u8, args.len + 1); + for (args) |arg, i| { + argv[i] = try arena.dupeZ(u8, arg); // TODO If there was an argsAllocZ we could avoid this allocation. + } + argv[args.len] = null; + const exit_code = ZigClang_main(@intCast(c_int, args.len), argv[0..args.len :null].ptr); + process.exit(@bitCast(u8, @truncate(i8, exit_code))); +} + +const clang_args = @import("clang_options.zig").list; + +pub const ClangArgIterator = struct { + has_next: bool, + zig_equivalent: ZigEquivalent, + only_arg: []const u8, + second_arg: []const u8, + other_args: []const []const u8, + argv: []const []const u8, + next_index: usize, + root_args: ?*Args, + allocator: *Allocator, + + pub const ZigEquivalent = enum { + target, + o, + c, + other, + positional, + l, + ignore, + driver_punt, + pic, + no_pic, + nostdlib, + nostdlib_cpp, + shared, + rdynamic, + wl, + preprocess_only, + asm_only, + optimize, + debug, + sanitize, + linker_script, + verbose_cmds, + for_linker, + linker_input_z, + lib_dir, + mcpu, + dep_file, + framework_dir, + framework, + nostdlibinc, + }; + + const Args = struct { + next_index: usize, + argv: []const []const u8, + }; + + fn init(allocator: *Allocator, argv: []const []const u8) ClangArgIterator { + return .{ + .next_index = 2, // `zig cc foo` this points to `foo` + .has_next = argv.len > 2, + .zig_equivalent = undefined, + .only_arg = undefined, + .second_arg = undefined, + .other_args = undefined, + .argv = argv, + .root_args = null, + .allocator = allocator, + }; + } + + fn next(self: *ClangArgIterator) !void { + assert(self.has_next); + assert(self.next_index < self.argv.len); + // In this state we know that the parameter we are looking at is a root parameter + // rather than an argument to a parameter. + // We adjust the len below when necessary. + self.other_args = (self.argv.ptr + self.next_index)[0..1]; + var arg = mem.span(self.argv[self.next_index]); + self.incrementArgIndex(); + + if (mem.startsWith(u8, arg, "@")) { + if (self.root_args != null) return error.NestedResponseFile; + + // This is a "compiler response file". We must parse the file and treat its + // contents as command line parameters. + const allocator = self.allocator; + const max_bytes = 10 * 1024 * 1024; // 10 MiB of command line arguments is a reasonable limit + const resp_file_path = arg[1..]; + const resp_contents = fs.cwd().readFileAlloc(allocator, resp_file_path, max_bytes) catch |err| { + fatal("unable to read response file '{}': {}", .{ resp_file_path, @errorName(err) }); + }; + defer allocator.free(resp_contents); + // TODO is there a specification for this file format? Let's find it and make this parsing more robust + // at the very least I'm guessing this needs to handle quotes and `#` comments. + var it = mem.tokenize(resp_contents, " \t\r\n"); + var resp_arg_list = std.ArrayList([]const u8).init(allocator); + defer resp_arg_list.deinit(); + { + errdefer { + for (resp_arg_list.span()) |item| { + allocator.free(mem.span(item)); + } + } + while (it.next()) |token| { + const dupe_token = try mem.dupeZ(allocator, u8, token); + errdefer allocator.free(dupe_token); + try resp_arg_list.append(dupe_token); + } + const args = try allocator.create(Args); + errdefer allocator.destroy(args); + args.* = .{ + .next_index = self.next_index, + .argv = self.argv, + }; + self.root_args = args; + } + const resp_arg_slice = resp_arg_list.toOwnedSlice(); + self.next_index = 0; + self.argv = resp_arg_slice; + + if (resp_arg_slice.len == 0) { + self.resolveRespFileArgs(); + return; + } + + self.has_next = true; + self.other_args = (self.argv.ptr + self.next_index)[0..1]; // We adjust len below when necessary. + arg = mem.span(self.argv[self.next_index]); + self.incrementArgIndex(); + } + if (!mem.startsWith(u8, arg, "-")) { + self.zig_equivalent = .positional; + self.only_arg = arg; + return; + } + + find_clang_arg: for (clang_args) |clang_arg| switch (clang_arg.syntax) { + .flag => { + const prefix_len = clang_arg.matchEql(arg); + if (prefix_len > 0) { + self.zig_equivalent = clang_arg.zig_equivalent; + self.only_arg = arg[prefix_len..]; + + break :find_clang_arg; + } + }, + .joined, .comma_joined => { + // joined example: --target=foo + // comma_joined example: -Wl,-soname,libsoundio.so.2 + const prefix_len = clang_arg.matchStartsWith(arg); + if (prefix_len != 0) { + self.zig_equivalent = clang_arg.zig_equivalent; + self.only_arg = arg[prefix_len..]; // This will skip over the "--target=" part. + + break :find_clang_arg; + } + }, + .joined_or_separate => { + // Examples: `-lfoo`, `-l foo` + const prefix_len = clang_arg.matchStartsWith(arg); + if (prefix_len == arg.len) { + if (self.next_index >= self.argv.len) { + fatal("Expected parameter after '{}'", .{arg}); + } + self.only_arg = self.argv[self.next_index]; + self.incrementArgIndex(); + self.other_args.len += 1; + self.zig_equivalent = clang_arg.zig_equivalent; + + break :find_clang_arg; + } else if (prefix_len != 0) { + self.zig_equivalent = clang_arg.zig_equivalent; + self.only_arg = arg[prefix_len..]; + + break :find_clang_arg; + } + }, + .joined_and_separate => { + // Example: `-Xopenmp-target=riscv64-linux-unknown foo` + const prefix_len = clang_arg.matchStartsWith(arg); + if (prefix_len != 0) { + self.only_arg = arg[prefix_len..]; + if (self.next_index >= self.argv.len) { + fatal("Expected parameter after '{}'", .{arg}); + } + self.second_arg = self.argv[self.next_index]; + self.incrementArgIndex(); + self.other_args.len += 1; + self.zig_equivalent = clang_arg.zig_equivalent; + break :find_clang_arg; + } + }, + .separate => if (clang_arg.matchEql(arg) > 0) { + if (self.next_index >= self.argv.len) { + fatal("Expected parameter after '{}'", .{arg}); + } + self.only_arg = self.argv[self.next_index]; + self.incrementArgIndex(); + self.other_args.len += 1; + self.zig_equivalent = clang_arg.zig_equivalent; + break :find_clang_arg; + }, + .remaining_args_joined => { + const prefix_len = clang_arg.matchStartsWith(arg); + if (prefix_len != 0) { + @panic("TODO"); + } + }, + .multi_arg => if (clang_arg.matchEql(arg) > 0) { + @panic("TODO"); + }, + } + else { + fatal("Unknown Clang option: '{}'", .{arg}); + } + } + + fn incrementArgIndex(self: *ClangArgIterator) void { + self.next_index += 1; + self.resolveRespFileArgs(); + } + + fn resolveRespFileArgs(self: *ClangArgIterator) void { + const allocator = self.allocator; + if (self.next_index >= self.argv.len) { + if (self.root_args) |root_args| { + self.next_index = root_args.next_index; + self.argv = root_args.argv; + + allocator.destroy(root_args); + self.root_args = null; + } + if (self.next_index >= self.argv.len) { + self.has_next = false; + } + } + } +}; + +fn parseCodeModel(arg: []const u8) std.builtin.CodeModel { + return std.meta.stringToEnum(std.builtin.CodeModel, arg) orelse + fatal("unsupported machine code model: '{}'", .{arg}); +} + +/// Raise the open file descriptor limit. Ask and ye shall receive. +/// For one example of why this is handy, consider the case of building musl libc. +/// We keep a lock open for each of the object files in the form of a file descriptor +/// until they are finally put into an archive file. This is to allow a zig-cache +/// garbage collector to run concurrently to zig processes, and to allow multiple +/// zig processes to run concurrently with each other, without clobbering each other. +fn gimmeMoreOfThoseSweetSweetFileDescriptors() void { + switch (std.Target.current.os.tag) { + .windows, .wasi, .uefi, .other, .freestanding => return, + // std lib is missing getrlimit/setrlimit. + // https://github.com/ziglang/zig/issues/6361 + //else => {}, + else => return, + } + const posix = std.os; + var lim = posix.getrlimit(posix.RLIMIT_NOFILE, &lim) catch return; // Oh well; we tried. + if (lim.cur == lim.max) return; + while (true) { + // Do a binary search for the limit. + var min: posix.rlim_t = lim.cur; + var max: posix.rlim_t = 1 << 20; + // But if there's a defined upper bound, don't search, just set it. + if (lim.max != posix.RLIM_INFINITY) { + min = lim.max; + max = lim.max; + } + while (true) { + lim.cur = min + (max - min) / 2; + if (posix.setrlimit(posix.RLIMIT_NOFILE, lim)) |_| { + min = lim.cur; + } else |_| { + max = lim.cur; + } + if (min + 1 < max) continue; + return; + } + } +} + +test "fds" { + gimmeMoreOfThoseSweetSweetFileDescriptors(); +} + +fn detectNativeCpuWithLLVM( + arch: std.Target.Cpu.Arch, + llvm_cpu_name_z: ?[*:0]const u8, + llvm_cpu_features_opt: ?[*:0]const u8, +) !std.Target.Cpu { + var result = std.Target.Cpu.baseline(arch); + + if (llvm_cpu_name_z) |cpu_name_z| { + const llvm_cpu_name = mem.spanZ(cpu_name_z); + + for (arch.allCpuModels()) |model| { + const this_llvm_name = model.llvm_name orelse continue; + if (mem.eql(u8, this_llvm_name, llvm_cpu_name)) { + // Here we use the non-dependencies-populated set, + // so that subtracting features later in this function + // affect the prepopulated set. + result = std.Target.Cpu{ + .arch = arch, + .model = model, + .features = model.features, + }; + break; + } + } + } + + const all_features = arch.allFeaturesList(); + + if (llvm_cpu_features_opt) |llvm_cpu_features| { + var it = mem.tokenize(mem.spanZ(llvm_cpu_features), ","); + while (it.next()) |decorated_llvm_feat| { + var op: enum { + add, + sub, + } = undefined; + var llvm_feat: []const u8 = undefined; + if (mem.startsWith(u8, decorated_llvm_feat, "+")) { + op = .add; + llvm_feat = decorated_llvm_feat[1..]; + } else if (mem.startsWith(u8, decorated_llvm_feat, "-")) { + op = .sub; + llvm_feat = decorated_llvm_feat[1..]; + } else { + return error.InvalidLlvmCpuFeaturesFormat; + } + for (all_features) |feature, index_usize| { + const this_llvm_name = feature.llvm_name orelse continue; + if (mem.eql(u8, llvm_feat, this_llvm_name)) { + const index = @intCast(std.Target.Cpu.Feature.Set.Index, index_usize); + switch (op) { + .add => result.features.addFeature(index), + .sub => result.features.removeFeature(index), + } + break; + } + } + } + } + + result.features.populateDependencies(all_features); + return result; +} + +fn detectNativeTargetInfo(gpa: *Allocator, cross_target: std.zig.CrossTarget) !std.zig.system.NativeTargetInfo { + var info = try std.zig.system.NativeTargetInfo.detect(gpa, cross_target); + if (info.cpu_detection_unimplemented) { + const arch = std.Target.current.cpu.arch; + + // We want to just use detected_info.target but implementing + // CPU model & feature detection is todo so here we rely on LLVM. + // https://github.com/ziglang/zig/issues/4591 + if (!build_options.have_llvm) + fatal("CPU features detection is not yet available for {} without LLVM extensions", .{@tagName(arch)}); + + const llvm = @import("llvm.zig"); + const llvm_cpu_name = llvm.GetHostCPUName(); + const llvm_cpu_features = llvm.GetNativeFeatures(); + info.target.cpu = try detectNativeCpuWithLLVM(arch, llvm_cpu_name, llvm_cpu_features); + cross_target.updateCpuFeatures(&info.target.cpu.features); + info.target.cpu.arch = cross_target.getCpuArch(); + } + return info; +} + +/// Indicate that we are now terminating with a successful exit code. +/// In debug builds, this is a no-op, so that the calling code's +/// cleanup mechanisms are tested and so that external tools that +/// check for resource leaks can be accurate. In release builds, this +/// calls exit(0), and does not return. +pub fn cleanExit() void { + if (std.builtin.mode == .Debug) { + return; + } else { + process.exit(0); + } +} diff --git a/src/mem.cpp b/src/mem.cpp deleted file mode 100644 index 51ee9a27eecf8c82266d47d1ccee7352bcb12b8a..0000000000000000000000000000000000000000 --- a/src/mem.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2020 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#include "config.h" -#include "mem.hpp" -#include "mem_profile.hpp" -#include "heap.hpp" - -namespace mem { - -void init() { - heap::bootstrap_allocator_state.init("heap::bootstrap_allocator"); - heap::c_allocator_state.init("heap::c_allocator"); -} - -void deinit() { - heap::c_allocator_state.deinit(); - heap::bootstrap_allocator_state.deinit(); -} - -#ifdef ZIG_ENABLE_MEM_PROFILE -void print_report(FILE *file) { - heap::c_allocator_state.print_report(file); - intern_counters.print_report(file); -} -#endif - -#ifdef ZIG_ENABLE_MEM_PROFILE -bool report_print = false; -FILE *report_file{nullptr}; -#endif - -} // namespace mem diff --git a/src/mem.hpp b/src/mem.hpp deleted file mode 100644 index 9e262b7d53399a29b235896df8c274b6cd49cd1f..0000000000000000000000000000000000000000 --- a/src/mem.hpp +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright (c) 2020 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#ifndef ZIG_MEM_HPP -#define ZIG_MEM_HPP - -#include -#include -#include - -#include "config.h" -#include "util_base.hpp" -#include "mem_type_info.hpp" - -// -// -- Memory Allocation General Notes -- -// -// `heap::c_allocator` is the preferred general allocator. -// -// `heap::bootstrap_allocator` is an implementation detail for use -// by allocators themselves when incidental heap may be required for -// profiling and statistics. It breaks the infinite recursion cycle. -// -// `mem::os` contains a raw wrapper for system malloc API used in -// preference to calling ::{malloc, free, calloc, realloc} directly. -// This isolates usage and helps with audits: -// -// mem::os::malloc -// mem::os::free -// mem::os::calloc -// mem::os::realloc -// -namespace mem { - -// initialize mem module before any use -void init(); - -// deinitialize mem module to free memory and print report -void deinit(); - -// isolate system/libc allocators -namespace os { - -ATTRIBUTE_RETURNS_NOALIAS -inline void *malloc(size_t size) { -#ifndef NDEBUG - // make behavior when size == 0 portable - if (size == 0) - return nullptr; -#endif - auto ptr = ::malloc(size); - if (ptr == nullptr) - zig_panic("allocation failed"); - return ptr; -} - -inline void free(void *ptr) { - ::free(ptr); -} - -ATTRIBUTE_RETURNS_NOALIAS -inline void *calloc(size_t count, size_t size) { -#ifndef NDEBUG - // make behavior when size == 0 portable - if (count == 0 || size == 0) - return nullptr; -#endif - auto ptr = ::calloc(count, size); - if (ptr == nullptr) - zig_panic("allocation failed"); - return ptr; -} - -inline void *realloc(void *old_ptr, size_t size) { -#ifndef NDEBUG - // make behavior when size == 0 portable - if (old_ptr == nullptr && size == 0) - return nullptr; -#endif - auto ptr = ::realloc(old_ptr, size); - if (ptr == nullptr) - zig_panic("allocation failed"); - return ptr; -} - -} // namespace os - -struct Allocator { - virtual void destruct(Allocator *allocator) = 0; - - template ATTRIBUTE_RETURNS_NOALIAS - T *allocate(size_t count) { - return reinterpret_cast(this->internal_allocate(TypeInfo::make(), count)); - } - - template ATTRIBUTE_RETURNS_NOALIAS - T *allocate_nonzero(size_t count) { - return reinterpret_cast(this->internal_allocate_nonzero(TypeInfo::make(), count)); - } - - template - T *reallocate(T *old_ptr, size_t old_count, size_t new_count) { - return reinterpret_cast(this->internal_reallocate(TypeInfo::make(), old_ptr, old_count, new_count)); - } - - template - T *reallocate_nonzero(T *old_ptr, size_t old_count, size_t new_count) { - return reinterpret_cast(this->internal_reallocate_nonzero(TypeInfo::make(), old_ptr, old_count, new_count)); - } - - template - void deallocate(T *ptr, size_t count) { - this->internal_deallocate(TypeInfo::make(), ptr, count); - } - - template - T *create() { - return reinterpret_cast(this->internal_allocate(TypeInfo::make(), 1)); - } - - template - void destroy(T *ptr) { - this->internal_deallocate(TypeInfo::make(), ptr, 1); - } - -protected: - ATTRIBUTE_RETURNS_NOALIAS virtual void *internal_allocate(const TypeInfo &info, size_t count) = 0; - ATTRIBUTE_RETURNS_NOALIAS virtual void *internal_allocate_nonzero(const TypeInfo &info, size_t count) = 0; - virtual void *internal_reallocate(const TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) = 0; - virtual void *internal_reallocate_nonzero(const TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) = 0; - virtual void internal_deallocate(const TypeInfo &info, void *ptr, size_t count) = 0; -}; - -#ifdef ZIG_ENABLE_MEM_PROFILE -void print_report(FILE *file = nullptr); - -// global memory report flag -extern bool report_print; -// global memory report default destination -extern FILE *report_file; -#endif - -} // namespace mem - -#endif diff --git a/src/mem_hash_map.hpp b/src/mem_hash_map.hpp deleted file mode 100644 index 6abbbf665003035a1152de69477bfadffe851873..0000000000000000000000000000000000000000 --- a/src/mem_hash_map.hpp +++ /dev/null @@ -1,244 +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_MEM_HASH_MAP_HPP -#define ZIG_MEM_HASH_MAP_HPP - -#include "mem.hpp" - -namespace mem { - -template -class HashMap { -public: - void init(Allocator& allocator, int capacity) { - init_capacity(allocator, capacity); - } - void deinit(Allocator& allocator) { - allocator.deallocate(_entries, _capacity); - } - - struct Entry { - K key; - V value; - bool used; - int distance_from_start_index; - }; - - void clear() { - for (int i = 0; i < _capacity; i += 1) { - _entries[i].used = false; - } - _size = 0; - _max_distance_from_start_index = 0; - _modification_count += 1; - } - - int size() const { - return _size; - } - - void put(Allocator& allocator, const K &key, const V &value) { - _modification_count += 1; - internal_put(key, value); - - // if we get too full (60%), double the capacity - if (_size * 5 >= _capacity * 3) { - Entry *old_entries = _entries; - int old_capacity = _capacity; - init_capacity(allocator, _capacity * 2); - // dump all of the old elements into the new table - for (int i = 0; i < old_capacity; i += 1) { - Entry *old_entry = &old_entries[i]; - if (old_entry->used) - internal_put(old_entry->key, old_entry->value); - } - allocator.deallocate(old_entries, old_capacity); - } - } - - Entry *put_unique(Allocator& allocator, const K &key, const V &value) { - // TODO make this more efficient - Entry *entry = internal_get(key); - if (entry) - return entry; - put(allocator, key, value); - return nullptr; - } - - const V &get(const K &key) const { - Entry *entry = internal_get(key); - if (!entry) - zig_panic("key not found"); - return entry->value; - } - - Entry *maybe_get(const K &key) const { - return internal_get(key); - } - - void maybe_remove(const K &key) { - if (maybe_get(key)) { - remove(key); - } - } - - void remove(const K &key) { - _modification_count += 1; - int start_index = key_to_index(key); - for (int roll_over = 0; roll_over <= _max_distance_from_start_index; roll_over += 1) { - int index = (start_index + roll_over) % _capacity; - Entry *entry = &_entries[index]; - - if (!entry->used) - zig_panic("key not found"); - - if (!EqualFn(entry->key, key)) - continue; - - for (; roll_over < _capacity; roll_over += 1) { - int next_index = (start_index + roll_over + 1) % _capacity; - Entry *next_entry = &_entries[next_index]; - if (!next_entry->used || next_entry->distance_from_start_index == 0) { - entry->used = false; - _size -= 1; - return; - } - *entry = *next_entry; - entry->distance_from_start_index -= 1; - entry = next_entry; - } - zig_panic("shifting everything in the table"); - } - zig_panic("key not found"); - } - - class Iterator { - public: - Entry *next() { - if (_inital_modification_count != _table->_modification_count) - zig_panic("concurrent modification"); - if (_count >= _table->size()) - return NULL; - for (; _index < _table->_capacity; _index += 1) { - Entry *entry = &_table->_entries[_index]; - if (entry->used) { - _index += 1; - _count += 1; - return entry; - } - } - zig_panic("no next item"); - } - - private: - const HashMap * _table; - // how many items have we returned - int _count = 0; - // iterator through the entry array - int _index = 0; - // used to detect concurrent modification - uint32_t _inital_modification_count; - Iterator(const HashMap * table) : - _table(table), _inital_modification_count(table->_modification_count) { - } - friend HashMap; - }; - - // you must not modify the underlying HashMap while this iterator is still in use - Iterator entry_iterator() const { - return Iterator(this); - } - -private: - Entry *_entries; - int _capacity; - int _size; - int _max_distance_from_start_index; - // this is used to detect bugs where a hashtable is edited while an iterator is running. - uint32_t _modification_count; - - void init_capacity(Allocator& allocator, int capacity) { - _capacity = capacity; - _entries = allocator.allocate(_capacity); - _size = 0; - _max_distance_from_start_index = 0; - for (int i = 0; i < _capacity; i += 1) { - _entries[i].used = false; - } - } - - void internal_put(K key, V value) { - int start_index = key_to_index(key); - for (int roll_over = 0, distance_from_start_index = 0; - roll_over < _capacity; roll_over += 1, distance_from_start_index += 1) - { - int index = (start_index + roll_over) % _capacity; - Entry *entry = &_entries[index]; - - if (entry->used && !EqualFn(entry->key, key)) { - if (entry->distance_from_start_index < distance_from_start_index) { - // robin hood to the rescue - Entry tmp = *entry; - if (distance_from_start_index > _max_distance_from_start_index) - _max_distance_from_start_index = distance_from_start_index; - *entry = { - key, - value, - true, - distance_from_start_index, - }; - key = tmp.key; - value = tmp.value; - distance_from_start_index = tmp.distance_from_start_index; - } - continue; - } - - if (!entry->used) { - // adding an entry. otherwise overwriting old value with - // same key - _size += 1; - } - - if (distance_from_start_index > _max_distance_from_start_index) - _max_distance_from_start_index = distance_from_start_index; - *entry = { - key, - value, - true, - distance_from_start_index, - }; - return; - } - zig_panic("put into a full HashMap"); - } - - - Entry *internal_get(const K &key) const { - int start_index = key_to_index(key); - for (int roll_over = 0; roll_over <= _max_distance_from_start_index; roll_over += 1) { - int index = (start_index + roll_over) % _capacity; - Entry *entry = &_entries[index]; - - if (!entry->used) - return NULL; - - if (EqualFn(entry->key, key)) - return entry; - } - return NULL; - } - - int key_to_index(const K &key) const { - return (int)(HashFunction(key) % ((uint32_t)_capacity)); - } -}; - -} // namespace mem - -#endif diff --git a/src/mem_list.hpp b/src/mem_list.hpp deleted file mode 100644 index df82358ea9542174cd652ec1ee97ee4d4d5b7e4c..0000000000000000000000000000000000000000 --- a/src/mem_list.hpp +++ /dev/null @@ -1,104 +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_MEM_LIST_HPP -#define ZIG_MEM_LIST_HPP - -#include "mem.hpp" - -namespace mem { - -template -struct List { - void deinit(Allocator *allocator) { - allocator->deallocate(items, capacity); - items = nullptr; - length = 0; - capacity = 0; - } - - void append(Allocator *allocator, const T& item) { - ensure_capacity(allocator, length + 1); - items[length++] = item; - } - - // remember that the pointer to this item is invalid after you - // modify the length of the list - const T & at(size_t index) const { - assert(index != SIZE_MAX); - assert(index < length); - return items[index]; - } - - T & at(size_t index) { - assert(index != SIZE_MAX); - assert(index < length); - return items[index]; - } - - T pop() { - assert(length >= 1); - return items[--length]; - } - - T *add_one() { - resize(length + 1); - return &last(); - } - - const T & last() const { - assert(length >= 1); - return items[length - 1]; - } - - T & last() { - assert(length >= 1); - return items[length - 1]; - } - - void resize(Allocator *allocator, size_t new_length) { - assert(new_length != SIZE_MAX); - ensure_capacity(allocator, new_length); - length = new_length; - } - - void clear() { - length = 0; - } - - void ensure_capacity(Allocator *allocator, size_t new_capacity) { - if (capacity >= new_capacity) - return; - - size_t better_capacity = capacity; - do { - better_capacity = better_capacity * 5 / 2 + 8; - } while (better_capacity < new_capacity); - - items = allocator->reallocate_nonzero(items, capacity, better_capacity); - capacity = better_capacity; - } - - T swap_remove(size_t index) { - if (length - 1 == index) return pop(); - - assert(index != SIZE_MAX); - assert(index < length); - - T old_item = items[index]; - items[index] = pop(); - return old_item; - } - - T *items{nullptr}; - size_t length{0}; - size_t capacity{0}; -}; - -} // namespace mem - -#endif diff --git a/src/mem_profile.cpp b/src/mem_profile.cpp deleted file mode 100644 index 13ba57f913879813fa832dd6a4f07a791aa671e3..0000000000000000000000000000000000000000 --- a/src/mem_profile.cpp +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright (c) 2020 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#include "config.h" - -#ifdef ZIG_ENABLE_MEM_PROFILE - -#include "mem.hpp" -#include "mem_list.hpp" -#include "mem_profile.hpp" -#include "heap.hpp" - -namespace mem { - -void Profile::init(const char *name, const char *kind) { - this->name = name; - this->kind = kind; - this->usage_table.init(heap::bootstrap_allocator, 1024); -} - -void Profile::deinit() { - assert(this->name != nullptr); - if (mem::report_print) - this->print_report(); - this->usage_table.deinit(heap::bootstrap_allocator); - this->name = nullptr; -} - -void Profile::record_alloc(const TypeInfo &info, size_t count) { - if (count == 0) return; - auto existing_entry = this->usage_table.put_unique( - heap::bootstrap_allocator, - UsageKey{info.name_ptr, info.name_len}, - Entry{info, 1, count, 0, 0} ); - if (existing_entry != nullptr) { - assert(existing_entry->value.info.size == info.size); // allocated name does not match type - existing_entry->value.alloc.calls += 1; - existing_entry->value.alloc.objects += count; - } -} - -void Profile::record_dealloc(const TypeInfo &info, size_t count) { - if (count == 0) return; - auto existing_entry = this->usage_table.maybe_get(UsageKey{info.name_ptr, info.name_len}); - if (existing_entry == nullptr) { - fprintf(stderr, "deallocated name '"); - for (size_t i = 0; i < info.name_len; ++i) - fputc(info.name_ptr[i], stderr); - zig_panic("' (size %zu) not found in allocated table; compromised memory usage stats", info.size); - } - if (existing_entry->value.info.size != info.size) { - fprintf(stderr, "deallocated name '"); - for (size_t i = 0; i < info.name_len; ++i) - fputc(info.name_ptr[i], stderr); - zig_panic("' does not match expected type size %zu", info.size); - } - assert(existing_entry->value.alloc.calls - existing_entry->value.dealloc.calls > 0); - assert(existing_entry->value.alloc.objects - existing_entry->value.dealloc.objects >= count); - existing_entry->value.dealloc.calls += 1; - existing_entry->value.dealloc.objects += count; -} - -static size_t entry_remain_total_bytes(const Profile::Entry *entry) { - return (entry->alloc.objects - entry->dealloc.objects) * entry->info.size; -} - -static int entry_compare(const void *a, const void *b) { - size_t total_a = entry_remain_total_bytes(*reinterpret_cast(a)); - size_t total_b = entry_remain_total_bytes(*reinterpret_cast(b)); - if (total_a > total_b) - return -1; - if (total_a < total_b) - return 1; - return 0; -}; - -void Profile::print_report(FILE *file) { - if (!file) { - file = report_file; - if (!file) - file = stderr; - } - fprintf(file, "\n--- MEMORY PROFILE REPORT [%s]: %s ---\n", this->kind, this->name); - - List list; - auto it = this->usage_table.entry_iterator(); - for (;;) { - auto entry = it.next(); - if (!entry) - break; - list.append(&heap::bootstrap_allocator, &entry->value); - } - - qsort(list.items, list.length, sizeof(const Entry *), entry_compare); - - size_t total_bytes_alloc = 0; - size_t total_bytes_dealloc = 0; - - size_t total_calls_alloc = 0; - size_t total_calls_dealloc = 0; - - for (size_t i = 0; i < list.length; i += 1) { - const Entry *entry = list.at(i); - fprintf(file, " "); - for (size_t j = 0; j < entry->info.name_len; ++j) - fputc(entry->info.name_ptr[j], file); - fprintf(file, ": %zu bytes each", entry->info.size); - - fprintf(file, ", alloc{ %zu calls, %zu objects, total ", entry->alloc.calls, entry->alloc.objects); - const auto alloc_num_bytes = entry->alloc.objects * entry->info.size; - zig_pretty_print_bytes(file, alloc_num_bytes); - - fprintf(file, " }, dealloc{ %zu calls, %zu objects, total ", entry->dealloc.calls, entry->dealloc.objects); - const auto dealloc_num_bytes = entry->dealloc.objects * entry->info.size; - zig_pretty_print_bytes(file, dealloc_num_bytes); - - fprintf(file, " }, remain{ %zu calls, %zu objects, total ", - entry->alloc.calls - entry->dealloc.calls, - entry->alloc.objects - entry->dealloc.objects ); - const auto remain_num_bytes = alloc_num_bytes - dealloc_num_bytes; - zig_pretty_print_bytes(file, remain_num_bytes); - - fprintf(file, " }\n"); - - total_bytes_alloc += alloc_num_bytes; - total_bytes_dealloc += dealloc_num_bytes; - - total_calls_alloc += entry->alloc.calls; - total_calls_dealloc += entry->dealloc.calls; - } - - fprintf(file, "\n Total bytes allocated: "); - zig_pretty_print_bytes(file, total_bytes_alloc); - fprintf(file, ", deallocated: "); - zig_pretty_print_bytes(file, total_bytes_dealloc); - fprintf(file, ", remaining: "); - zig_pretty_print_bytes(file, total_bytes_alloc - total_bytes_dealloc); - - fprintf(file, "\n Total calls alloc: %zu, dealloc: %zu, remain: %zu\n", - total_calls_alloc, total_calls_dealloc, (total_calls_alloc - total_calls_dealloc)); - - list.deinit(&heap::bootstrap_allocator); -} - -uint32_t Profile::usage_hash(UsageKey key) { - // FNV 32-bit hash - uint32_t h = 2166136261; - for (size_t i = 0; i < key.name_len; ++i) { - h = h ^ key.name_ptr[i]; - h = h * 16777619; - } - return h; -} - -bool Profile::usage_equal(UsageKey a, UsageKey b) { - return memcmp(a.name_ptr, b.name_ptr, a.name_len > b.name_len ? a.name_len : b.name_len) == 0; -} - -void InternCounters::print_report(FILE *file) { - if (!file) { - file = report_file; - if (!file) - file = stderr; - } - fprintf(file, "\n--- IR INTERNING REPORT ---\n"); - fprintf(file, " undefined: interned %zu times\n", intern_counters.x_undefined); - fprintf(file, " void: interned %zu times\n", intern_counters.x_void); - fprintf(file, " null: interned %zu times\n", intern_counters.x_null); - fprintf(file, " unreachable: interned %zu times\n", intern_counters.x_unreachable); - fprintf(file, " zero_byte: interned %zu times\n", intern_counters.zero_byte); -} - -InternCounters intern_counters; - -} // namespace mem - -#endif diff --git a/src/mem_profile.hpp b/src/mem_profile.hpp deleted file mode 100644 index 3b13b7680b653f401cd7da59b441f426ae60c7f2..0000000000000000000000000000000000000000 --- a/src/mem_profile.hpp +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2020 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#ifndef ZIG_MEM_PROFILE_HPP -#define ZIG_MEM_PROFILE_HPP - -#include "config.h" - -#ifdef ZIG_ENABLE_MEM_PROFILE - -#include - -#include "mem.hpp" -#include "mem_hash_map.hpp" -#include "util.hpp" - -namespace mem { - -struct Profile { - void init(const char *name, const char *kind); - void deinit(); - - void record_alloc(const TypeInfo &info, size_t count); - void record_dealloc(const TypeInfo &info, size_t count); - - void print_report(FILE *file = nullptr); - - struct Entry { - TypeInfo info; - - struct Use { - size_t calls; - size_t objects; - } alloc, dealloc; - }; - -private: - const char *name; - const char *kind; - - struct UsageKey { - const char *name_ptr; - size_t name_len; - }; - - static uint32_t usage_hash(UsageKey key); - static bool usage_equal(UsageKey a, UsageKey b); - - HashMap usage_table; -}; - -struct InternCounters { - size_t x_undefined; - size_t x_void; - size_t x_null; - size_t x_unreachable; - size_t zero_byte; - - void print_report(FILE *file = nullptr); -}; - -extern InternCounters intern_counters; - -} // namespace mem - -#endif -#endif diff --git a/src/mem_type_info.hpp b/src/mem_type_info.hpp deleted file mode 100644 index 8698992ca0b2a02843aeca2af30da23664037903..0000000000000000000000000000000000000000 --- a/src/mem_type_info.hpp +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright (c) 2020 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#ifndef ZIG_MEM_TYPE_INFO_HPP -#define ZIG_MEM_TYPE_INFO_HPP - -#include "config.h" - -#ifndef ZIG_TYPE_INFO_IMPLEMENTATION -# ifdef ZIG_ENABLE_MEM_PROFILE -# define ZIG_TYPE_INFO_IMPLEMENTATION 1 -# else -# define ZIG_TYPE_INFO_IMPLEMENTATION 0 -# endif -#endif - -namespace mem { - -#if ZIG_TYPE_INFO_IMPLEMENTATION == 0 - -struct TypeInfo { - size_t size; - size_t alignment; - - template - static constexpr TypeInfo make() { - return {sizeof(T), alignof(T)}; - } -}; - -#elif ZIG_TYPE_INFO_IMPLEMENTATION == 1 - -// -// A non-portable way to get a human-readable type-name compatible with -// non-RTTI C++ compiler mode; eg. `-fno-rtti`. -// -// Minimum requirements are c++11 and a compiler that has a constant for the -// current function's decorated name whereby a template-type name can be -// computed. eg. `__PRETTY_FUNCTION__` or `__FUNCSIG__`. -// -// given the following snippet: -// -// | #include -// | -// | struct Top {}; -// | namespace mynamespace { -// | using custom = unsigned int; -// | struct Foo { -// | struct Bar {}; -// | }; -// | }; -// | -// | template -// | void foobar() { -// | #ifdef _MSC_VER -// | fprintf(stderr, "--> %s\n", __FUNCSIG__); -// | #else -// | fprintf(stderr, "--> %s\n", __PRETTY_FUNCTION__); -// | #endif -// | } -// | -// | int main() { -// | foobar(); -// | foobar(); -// | foobar(); -// | foobar(); -// | foobar(); -// | } -// -// gcc 9.2.0 produces: -// --> void foobar() [with T = Top] -// --> void foobar() [with T = unsigned int] -// --> void foobar() [with T = unsigned int] -// --> void foobar() [with T = mynamespace::Foo*] -// --> void foobar() [with T = mynamespace::Foo::Bar*] -// -// xcode 11.3.1/clang produces: -// --> void foobar() [T = Top] -// --> void foobar() [T = unsigned int] -// --> void foobar() [T = unsigned int] -// --> void foobar() [T = mynamespace::Foo *] -// --> void foobar() [T = mynamespace::Foo::Bar *] -// -// VStudio 2019 16.5.0/msvc produces: -// --> void __cdecl foobar(void) -// --> void __cdecl foobar(void) -// --> void __cdecl foobar(void) -// --> void __cdecl foobar(void) -// --> void __cdecl foobar(void) -// -struct TypeInfo { - const char *name_ptr; - size_t name_len; - size_t size; - size_t alignment; - - static constexpr TypeInfo to_type_info(const char *str, size_t start, size_t end, size_t size, size_t alignment) { - return TypeInfo{str + start, end - start, size, alignment}; - } - - static constexpr size_t index_of(const char *str, char c) { - return *str == c ? 0 : 1 + index_of(str + 1, c); - } - - template - static constexpr const char *decorated_name() { -#ifdef _MSC_VER - return __FUNCSIG__; -#else - return __PRETTY_FUNCTION__; -#endif - } - - static constexpr TypeInfo extract(const char *decorated, size_t size, size_t alignment) { -#ifdef _MSC_VER - return to_type_info(decorated, index_of(decorated, '<') + 1, index_of(decorated, '>'), size, alignment); -#else - return to_type_info(decorated, index_of(decorated, '=') + 2, index_of(decorated, ']'), size, alignment); -#endif - } - - template - static constexpr TypeInfo make() { - return TypeInfo::extract(TypeInfo::decorated_name(), sizeof(T), alignof(T)); - } -}; - -#endif // ZIG_TYPE_INFO_IMPLEMENTATION - -} // namespace mem - -#endif diff --git a/src/mingw.zig b/src/mingw.zig new file mode 100644 index 0000000000000000000000000000000000000000..b6c8591ea4235de909a3a29916f2a111e661f0cf --- /dev/null +++ b/src/mingw.zig @@ -0,0 +1,1092 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; +const mem = std.mem; +const path = std.fs.path; +const assert = std.debug.assert; +const log = std.log.scoped(.mingw); + +const target_util = @import("target.zig"); +const Compilation = @import("Compilation.zig"); +const build_options = @import("build_options"); +const Cache = @import("Cache.zig"); + +pub const CRTFile = enum { + crt2_o, + dllcrt2_o, + mingw32_lib, + msvcrt_os_lib, + mingwex_lib, + uuid_lib, +}; + +pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { + if (!build_options.have_llvm) { + return error.ZigCompilerNotBuiltWithLLVMExtensions; + } + var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); + defer arena_allocator.deinit(); + const arena = &arena_allocator.allocator; + + switch (crt_file) { + .crt2_o => { + var args = std.ArrayList([]const u8).init(arena); + try add_cc_args(comp, arena, &args); + try args.appendSlice(&[_][]const u8{ + "-U__CRTDLL__", + "-D__MSVCRT__", + // Uncomment these 3 things for crtu + //"-DUNICODE", + //"-D_UNICODE", + //"-DWPRFLAG=1", + }); + return comp.build_crt_file("crt2", .Obj, &[1]Compilation.CSourceFile{ + .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ + "libc", "mingw", "crt", "crtexe.c", + }), + .extra_flags = args.items, + }, + }); + }, + + .dllcrt2_o => { + var args = std.ArrayList([]const u8).init(arena); + try add_cc_args(comp, arena, &args); + try args.appendSlice(&[_][]const u8{ + "-U__CRTDLL__", + "-D__MSVCRT__", + }); + return comp.build_crt_file("dllcrt2", .Obj, &[1]Compilation.CSourceFile{ + .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ + "libc", "mingw", "crt", "crtdll.c", + }), + .extra_flags = args.items, + }, + }); + }, + + .mingw32_lib => { + var c_source_files: [mingw32_lib_deps.len]Compilation.CSourceFile = undefined; + for (mingw32_lib_deps) |dep, i| { + var args = std.ArrayList([]const u8).init(arena); + try args.appendSlice(&[_][]const u8{ + "-DHAVE_CONFIG_H", + "-D_SYSCRT=1", + "-DCRTDLL=1", + + "-isystem", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ + "libc", "include", "any-windows-any", + }), + + "-isystem", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", "include" }), + + "-std=gnu99", + "-D_CRTBLD", + "-D_WIN32_WINNT=0x0f00", + "-D__MSVCRT_VERSION__=0x700", + "-g", + "-O2", + }); + c_source_files[i] = .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ + "libc", "mingw", "crt", dep, + }), + .extra_flags = args.items, + }; + } + return comp.build_crt_file("mingw32", .Lib, &c_source_files); + }, + + .msvcrt_os_lib => { + const extra_flags = try arena.dupe([]const u8, &[_][]const u8{ + "-DHAVE_CONFIG_H", + "-D__LIBMSVCRT__", + + "-I", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", "include" }), + + "-std=gnu99", + "-D_CRTBLD", + "-D_WIN32_WINNT=0x0f00", + "-D__MSVCRT_VERSION__=0x700", + + "-isystem", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "include", "any-windows-any" }), + + "-g", + "-O2", + }); + var c_source_files = std.ArrayList(Compilation.CSourceFile).init(arena); + + for (msvcrt_common_src) |dep| { + (try c_source_files.addOne()).* = .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", dep }), + .extra_flags = extra_flags, + }; + } + if (comp.getTarget().cpu.arch == .i386) { + for (msvcrt_i386_src) |dep| { + (try c_source_files.addOne()).* = .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ + "libc", "mingw", dep, + }), + .extra_flags = extra_flags, + }; + } + } else { + for (msvcrt_other_src) |dep| { + (try c_source_files.addOne()).* = .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ + "libc", "mingw", dep, + }), + .extra_flags = extra_flags, + }; + } + } + return comp.build_crt_file("msvcrt-os", .Lib, c_source_files.items); + }, + + .mingwex_lib => { + const extra_flags = try arena.dupe([]const u8, &[_][]const u8{ + "-DHAVE_CONFIG_H", + + "-I", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw" }), + + "-I", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", "include" }), + + "-std=gnu99", + "-D_CRTBLD", + "-D_WIN32_WINNT=0x0f00", + "-D__MSVCRT_VERSION__=0x700", + "-g", + "-O2", + + "-isystem", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "include", "any-windows-any" }), + }); + var c_source_files = std.ArrayList(Compilation.CSourceFile).init(arena); + + for (mingwex_generic_src) |dep| { + (try c_source_files.addOne()).* = .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ + "libc", "mingw", dep, + }), + .extra_flags = extra_flags, + }; + } + const target = comp.getTarget(); + if (target.cpu.arch == .i386 or target.cpu.arch == .x86_64) { + for (mingwex_x86_src) |dep| { + (try c_source_files.addOne()).* = .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ + "libc", "mingw", dep, + }), + .extra_flags = extra_flags, + }; + } + } else if (target.cpu.arch.isARM()) { + if (target.cpu.arch.ptrBitWidth() == 32) { + for (mingwex_arm32_src) |dep| { + (try c_source_files.addOne()).* = .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ + "libc", "mingw", dep, + }), + .extra_flags = extra_flags, + }; + } + } else { + for (mingwex_arm64_src) |dep| { + (try c_source_files.addOne()).* = .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ + "libc", "mingw", dep, + }), + .extra_flags = extra_flags, + }; + } + } + } else { + unreachable; + } + return comp.build_crt_file("mingwex", .Lib, c_source_files.items); + }, + + .uuid_lib => { + const extra_flags = try arena.dupe([]const u8, &[_][]const u8{ + "-DHAVE_CONFIG_H", + + "-I", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw" }), + + "-I", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", "include" }), + + "-std=gnu99", + "-D_CRTBLD", + "-D_WIN32_WINNT=0x0f00", + "-D__MSVCRT_VERSION__=0x700", + "-g", + "-O2", + + "-isystem", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ + "libc", "include", "any-windows-any", + }), + }); + var c_source_files: [uuid_src.len]Compilation.CSourceFile = undefined; + for (uuid_src) |dep, i| { + c_source_files[i] = .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ + "libc", "mingw", "libsrc", dep, + }), + .extra_flags = extra_flags, + }; + } + return comp.build_crt_file("uuid", .Lib, &c_source_files); + }, + } +} + +fn add_cc_args( + comp: *Compilation, + arena: *Allocator, + args: *std.ArrayList([]const u8), +) error{OutOfMemory}!void { + try args.appendSlice(&[_][]const u8{ + "-DHAVE_CONFIG_H", + + "-I", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", "include" }), + + "-isystem", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "include", "any-windows-any" }), + }); + + const target = comp.getTarget(); + if (target.cpu.arch.isARM() and target.cpu.arch.ptrBitWidth() == 32) { + try args.append("-mfpu=vfp"); + } + + try args.appendSlice(&[_][]const u8{ + "-std=gnu11", + "-D_CRTBLD", + "-D_WIN32_WINNT=0x0f00", + "-D__MSVCRT_VERSION__=0x700", + }); +} + +pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { + var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); + defer arena_allocator.deinit(); + const arena = &arena_allocator.allocator; + + const def_file_path = findDef(comp, arena, lib_name) catch |err| switch (err) { + error.FileNotFound => { + log.debug("no {s}.def file available to make a DLL import {s}.lib", .{ lib_name, lib_name }); + // In this case we will end up putting foo.lib onto the linker line and letting the linker + // use its library paths to look for libraries and report any problems. + return; + }, + else => |e| return e, + }; + + // We need to invoke `zig clang` to use the preprocessor. + if (!build_options.have_llvm) return error.ZigCompilerNotBuiltWithLLVMExtensions; + const self_exe_path = comp.self_exe_path orelse return error.PreprocessorDisabled; + + const target = comp.getTarget(); + + var cache: Cache = .{ + .gpa = comp.gpa, + .manifest_dir = comp.cache_parent.manifest_dir, + }; + cache.hash.addBytes(build_options.version); + cache.hash.addOptionalBytes(comp.zig_lib_directory.path); + cache.hash.add(target.cpu.arch); + + var man = cache.obtain(); + defer man.deinit(); + + _ = try man.addFile(def_file_path, null); + + const final_lib_basename = try std.fmt.allocPrint(comp.gpa, "{s}.lib", .{lib_name}); + errdefer comp.gpa.free(final_lib_basename); + + if (try man.hit()) { + const digest = man.final(); + + try comp.crt_files.ensureCapacity(comp.gpa, comp.crt_files.count() + 1); + comp.crt_files.putAssumeCapacityNoClobber(final_lib_basename, .{ + .full_object_path = try comp.global_cache_directory.join(comp.gpa, &[_][]const u8{ + "o", &digest, final_lib_basename, + }), + .lock = man.toOwnedLock(), + }); + return; + } + + const digest = man.final(); + const o_sub_path = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest }); + var o_dir = try comp.global_cache_directory.handle.makeOpenPath(o_sub_path, .{}); + defer o_dir.close(); + + const final_def_basename = try std.fmt.allocPrint(arena, "{s}.def", .{lib_name}); + const def_final_path = try comp.global_cache_directory.join(arena, &[_][]const u8{ + "o", &digest, final_def_basename, + }); + + const target_def_arg = switch (target.cpu.arch) { + .i386 => "-DDEF_I386", + .x86_64 => "-DDEF_X64", + .arm, .armeb, .thumb, .thumbeb, .aarch64_32 => "-DDEF_ARM32", + .aarch64, .aarch64_be => "-DDEF_ARM64", + else => unreachable, + }; + + const args = [_][]const u8{ + self_exe_path, + "clang", + "-x", + "c", + def_file_path, + "-Wp,-w", + "-undef", + "-P", + "-I", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", "def-include" }), + target_def_arg, + "-E", + "-o", + def_final_path, + }; + + if (comp.verbose_cc) { + Compilation.dump_argv(&args); + } + + const child = try std.ChildProcess.init(&args, arena); + defer child.deinit(); + + child.stdin_behavior = .Ignore; + child.stdout_behavior = .Pipe; + child.stderr_behavior = .Pipe; + + try child.spawn(); + + const stdout_reader = child.stdout.?.reader(); + const stderr_reader = child.stderr.?.reader(); + + // TODO https://github.com/ziglang/zig/issues/6343 + const stdout = try stdout_reader.readAllAlloc(arena, std.math.maxInt(u32)); + const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024); + + const term = child.wait() catch |err| { + // TODO surface a proper error here + log.err("unable to spawn {}: {}", .{ args[0], @errorName(err) }); + return error.ClangPreprocessorFailed; + }; + + switch (term) { + .Exited => |code| { + if (code != 0) { + // TODO surface a proper error here + log.err("clang exited with code {d} and stderr: {s}", .{ code, stderr }); + return error.ClangPreprocessorFailed; + } + }, + else => { + // TODO surface a proper error here + log.err("clang terminated unexpectedly with stderr: {}", .{stderr}); + return error.ClangPreprocessorFailed; + }, + } + + const lib_final_path = try comp.global_cache_directory.join(comp.gpa, &[_][]const u8{ + "o", &digest, final_lib_basename, + }); + errdefer comp.gpa.free(lib_final_path); + + const llvm = @import("llvm.zig"); + const arch_type = @import("target.zig").archToLLVM(target.cpu.arch); + const def_final_path_z = try arena.dupeZ(u8, def_final_path); + const lib_final_path_z = try arena.dupeZ(u8, lib_final_path); + if (llvm.WriteImportLibrary(def_final_path_z.ptr, arch_type, lib_final_path_z.ptr, true)) { + // TODO surface a proper error here + log.err("unable to turn {s}.def into {s}.lib", .{ lib_name, lib_name }); + return error.WritingImportLibFailed; + } + + man.writeManifest() catch |err| { + log.warn("failed to write cache manifest for DLL import {s}.lib: {s}", .{ lib_name, @errorName(err) }); + }; + + try comp.crt_files.putNoClobber(comp.gpa, final_lib_basename, .{ + .full_object_path = lib_final_path, + .lock = man.toOwnedLock(), + }); +} + +/// This function body is verbose but all it does is test 3 different paths and see if a .def file exists. +fn findDef(comp: *Compilation, allocator: *Allocator, lib_name: []const u8) ![]u8 { + const target = comp.getTarget(); + + const lib_path = switch (target.cpu.arch) { + .i386 => "lib32", + .x86_64 => "lib64", + .arm, .armeb => switch (target.cpu.arch.ptrBitWidth()) { + 32 => "libarm32", + 64 => "libarm64", + else => unreachable, + }, + else => unreachable, + }; + + var override_path = std.ArrayList(u8).init(allocator); + defer override_path.deinit(); + + const s = path.sep_str; + + { + // Try the archtecture-specific path first. + const fmt_path = "libc" ++ s ++ "mingw" ++ s ++ "{s}" ++ s ++ "{s}.def"; + if (comp.zig_lib_directory.path) |p| { + try override_path.writer().print("{s}" ++ s ++ fmt_path, .{ p, lib_path, lib_name }); + } else { + try override_path.writer().print(fmt_path, .{ lib_path, lib_name }); + } + if (std.fs.cwd().access(override_path.items, .{})) |_| { + return override_path.toOwnedSlice(); + } else |err| switch (err) { + error.FileNotFound => {}, + else => |e| return e, + } + } + + { + // Try the generic version. + override_path.shrinkRetainingCapacity(0); + const fmt_path = "libc" ++ s ++ "mingw" ++ s ++ "lib-common" ++ s ++ "{s}.def"; + if (comp.zig_lib_directory.path) |p| { + try override_path.writer().print("{s}" ++ s ++ fmt_path, .{ p, lib_name }); + } else { + try override_path.writer().print(fmt_path, .{lib_name}); + } + if (std.fs.cwd().access(override_path.items, .{})) |_| { + return override_path.toOwnedSlice(); + } else |err| switch (err) { + error.FileNotFound => {}, + else => |e| return e, + } + } + + { + // Try the generic version and preprocess it. + override_path.shrinkRetainingCapacity(0); + const fmt_path = "libc" ++ s ++ "mingw" ++ s ++ "lib-common" ++ s ++ "{s}.def.in"; + if (comp.zig_lib_directory.path) |p| { + try override_path.writer().print("{s}" ++ s ++ fmt_path, .{ p, lib_name }); + } else { + try override_path.writer().print(fmt_path, .{lib_name}); + } + if (std.fs.cwd().access(override_path.items, .{})) |_| { + return override_path.toOwnedSlice(); + } else |err| switch (err) { + error.FileNotFound => {}, + else => |e| return e, + } + } + + return error.FileNotFound; +} + +const mingw32_lib_deps = [_][]const u8{ + "crt0_c.c", + "dll_argv.c", + "gccmain.c", + "natstart.c", + "pseudo-reloc-list.c", + "wildcard.c", + "charmax.c", + "crt0_w.c", + "dllargv.c", + "gs_support.c", + "_newmode.c", + "tlssup.c", + "xncommod.c", + "cinitexe.c", + "merr.c", + "usermatherr.c", + "pesect.c", + "udllargc.c", + "xthdloc.c", + "CRT_fp10.c", + "mingw_helpers.c", + "pseudo-reloc.c", + "udll_argv.c", + "xtxtmode.c", + "crt_handler.c", + "tlsthrd.c", + "tlsmthread.c", + "tlsmcrt.c", + "cxa_atexit.c", +}; +const msvcrt_common_src = [_][]const u8{ + "misc" ++ path.sep_str ++ "_create_locale.c", + "misc" ++ path.sep_str ++ "_free_locale.c", + "misc" ++ path.sep_str ++ "onexit_table.c", + "misc" ++ path.sep_str ++ "register_tls_atexit.c", + "stdio" ++ path.sep_str ++ "acrt_iob_func.c", + "misc" ++ path.sep_str ++ "_configthreadlocale.c", + "misc" ++ path.sep_str ++ "_get_current_locale.c", + "misc" ++ path.sep_str ++ "invalid_parameter_handler.c", + "misc" ++ path.sep_str ++ "output_format.c", + "misc" ++ path.sep_str ++ "purecall.c", + "secapi" ++ path.sep_str ++ "_access_s.c", + "secapi" ++ path.sep_str ++ "_cgets_s.c", + "secapi" ++ path.sep_str ++ "_cgetws_s.c", + "secapi" ++ path.sep_str ++ "_chsize_s.c", + "secapi" ++ path.sep_str ++ "_controlfp_s.c", + "secapi" ++ path.sep_str ++ "_cprintf_s.c", + "secapi" ++ path.sep_str ++ "_cprintf_s_l.c", + "secapi" ++ path.sep_str ++ "_ctime32_s.c", + "secapi" ++ path.sep_str ++ "_ctime64_s.c", + "secapi" ++ path.sep_str ++ "_cwprintf_s.c", + "secapi" ++ path.sep_str ++ "_cwprintf_s_l.c", + "secapi" ++ path.sep_str ++ "_gmtime32_s.c", + "secapi" ++ path.sep_str ++ "_gmtime64_s.c", + "secapi" ++ path.sep_str ++ "_localtime32_s.c", + "secapi" ++ path.sep_str ++ "_localtime64_s.c", + "secapi" ++ path.sep_str ++ "_mktemp_s.c", + "secapi" ++ path.sep_str ++ "_sopen_s.c", + "secapi" ++ path.sep_str ++ "_strdate_s.c", + "secapi" ++ path.sep_str ++ "_strtime_s.c", + "secapi" ++ path.sep_str ++ "_umask_s.c", + "secapi" ++ path.sep_str ++ "_vcprintf_s.c", + "secapi" ++ path.sep_str ++ "_vcprintf_s_l.c", + "secapi" ++ path.sep_str ++ "_vcwprintf_s.c", + "secapi" ++ path.sep_str ++ "_vcwprintf_s_l.c", + "secapi" ++ path.sep_str ++ "_vscprintf_p.c", + "secapi" ++ path.sep_str ++ "_vscwprintf_p.c", + "secapi" ++ path.sep_str ++ "_vswprintf_p.c", + "secapi" ++ path.sep_str ++ "_waccess_s.c", + "secapi" ++ path.sep_str ++ "_wasctime_s.c", + "secapi" ++ path.sep_str ++ "_wctime32_s.c", + "secapi" ++ path.sep_str ++ "_wctime64_s.c", + "secapi" ++ path.sep_str ++ "_wstrtime_s.c", + "secapi" ++ path.sep_str ++ "_wmktemp_s.c", + "secapi" ++ path.sep_str ++ "_wstrdate_s.c", + "secapi" ++ path.sep_str ++ "asctime_s.c", + "secapi" ++ path.sep_str ++ "memcpy_s.c", + "secapi" ++ path.sep_str ++ "memmove_s.c", + "secapi" ++ path.sep_str ++ "rand_s.c", + "secapi" ++ path.sep_str ++ "sprintf_s.c", + "secapi" ++ path.sep_str ++ "strerror_s.c", + "secapi" ++ path.sep_str ++ "vsprintf_s.c", + "secapi" ++ path.sep_str ++ "wmemcpy_s.c", + "secapi" ++ path.sep_str ++ "wmemmove_s.c", + "stdio" ++ path.sep_str ++ "mingw_lock.c", +}; +const msvcrt_i386_src = [_][]const u8{ + "misc" ++ path.sep_str ++ "lc_locale_func.c", + "misc" ++ path.sep_str ++ "___mb_cur_max_func.c", +}; + +const msvcrt_other_src = [_][]const u8{ + "misc" ++ path.sep_str ++ "__p___argv.c", + "misc" ++ path.sep_str ++ "__p__acmdln.c", + "misc" ++ path.sep_str ++ "__p__fmode.c", + "misc" ++ path.sep_str ++ "__p__wcmdln.c", +}; +const mingwex_generic_src = [_][]const u8{ + "complex" ++ path.sep_str ++ "_cabs.c", + "complex" ++ path.sep_str ++ "cabs.c", + "complex" ++ path.sep_str ++ "cabsf.c", + "complex" ++ path.sep_str ++ "cabsl.c", + "complex" ++ path.sep_str ++ "cacos.c", + "complex" ++ path.sep_str ++ "cacosf.c", + "complex" ++ path.sep_str ++ "cacosl.c", + "complex" ++ path.sep_str ++ "carg.c", + "complex" ++ path.sep_str ++ "cargf.c", + "complex" ++ path.sep_str ++ "cargl.c", + "complex" ++ path.sep_str ++ "casin.c", + "complex" ++ path.sep_str ++ "casinf.c", + "complex" ++ path.sep_str ++ "casinl.c", + "complex" ++ path.sep_str ++ "catan.c", + "complex" ++ path.sep_str ++ "catanf.c", + "complex" ++ path.sep_str ++ "catanl.c", + "complex" ++ path.sep_str ++ "ccos.c", + "complex" ++ path.sep_str ++ "ccosf.c", + "complex" ++ path.sep_str ++ "ccosl.c", + "complex" ++ path.sep_str ++ "cexp.c", + "complex" ++ path.sep_str ++ "cexpf.c", + "complex" ++ path.sep_str ++ "cexpl.c", + "complex" ++ path.sep_str ++ "cimag.c", + "complex" ++ path.sep_str ++ "cimagf.c", + "complex" ++ path.sep_str ++ "cimagl.c", + "complex" ++ path.sep_str ++ "clog.c", + "complex" ++ path.sep_str ++ "clog10.c", + "complex" ++ path.sep_str ++ "clog10f.c", + "complex" ++ path.sep_str ++ "clog10l.c", + "complex" ++ path.sep_str ++ "clogf.c", + "complex" ++ path.sep_str ++ "clogl.c", + "complex" ++ path.sep_str ++ "conj.c", + "complex" ++ path.sep_str ++ "conjf.c", + "complex" ++ path.sep_str ++ "conjl.c", + "complex" ++ path.sep_str ++ "cpow.c", + "complex" ++ path.sep_str ++ "cpowf.c", + "complex" ++ path.sep_str ++ "cpowl.c", + "complex" ++ path.sep_str ++ "cproj.c", + "complex" ++ path.sep_str ++ "cprojf.c", + "complex" ++ path.sep_str ++ "cprojl.c", + "complex" ++ path.sep_str ++ "creal.c", + "complex" ++ path.sep_str ++ "crealf.c", + "complex" ++ path.sep_str ++ "creall.c", + "complex" ++ path.sep_str ++ "csin.c", + "complex" ++ path.sep_str ++ "csinf.c", + "complex" ++ path.sep_str ++ "csinl.c", + "complex" ++ path.sep_str ++ "csqrt.c", + "complex" ++ path.sep_str ++ "csqrtf.c", + "complex" ++ path.sep_str ++ "csqrtl.c", + "complex" ++ path.sep_str ++ "ctan.c", + "complex" ++ path.sep_str ++ "ctanf.c", + "complex" ++ path.sep_str ++ "ctanl.c", + "crt" ++ path.sep_str ++ "dllentry.c", + "crt" ++ path.sep_str ++ "dllmain.c", + "gdtoa" ++ path.sep_str ++ "arithchk.c", + "gdtoa" ++ path.sep_str ++ "dmisc.c", + "gdtoa" ++ path.sep_str ++ "dtoa.c", + "gdtoa" ++ path.sep_str ++ "g__fmt.c", + "gdtoa" ++ path.sep_str ++ "g_dfmt.c", + "gdtoa" ++ path.sep_str ++ "g_ffmt.c", + "gdtoa" ++ path.sep_str ++ "g_xfmt.c", + "gdtoa" ++ path.sep_str ++ "gdtoa.c", + "gdtoa" ++ path.sep_str ++ "gethex.c", + "gdtoa" ++ path.sep_str ++ "gmisc.c", + "gdtoa" ++ path.sep_str ++ "hd_init.c", + "gdtoa" ++ path.sep_str ++ "hexnan.c", + "gdtoa" ++ path.sep_str ++ "misc.c", + "gdtoa" ++ path.sep_str ++ "qnan.c", + "gdtoa" ++ path.sep_str ++ "smisc.c", + "gdtoa" ++ path.sep_str ++ "strtodg.c", + "gdtoa" ++ path.sep_str ++ "strtodnrp.c", + "gdtoa" ++ path.sep_str ++ "strtof.c", + "gdtoa" ++ path.sep_str ++ "strtopx.c", + "gdtoa" ++ path.sep_str ++ "sum.c", + "gdtoa" ++ path.sep_str ++ "ulp.c", + "math" ++ path.sep_str ++ "abs64.c", + "math" ++ path.sep_str ++ "cbrt.c", + "math" ++ path.sep_str ++ "cbrtf.c", + "math" ++ path.sep_str ++ "cbrtl.c", + "math" ++ path.sep_str ++ "cephes_emath.c", + "math" ++ path.sep_str ++ "copysign.c", + "math" ++ path.sep_str ++ "copysignf.c", + "math" ++ path.sep_str ++ "coshf.c", + "math" ++ path.sep_str ++ "coshl.c", + "math" ++ path.sep_str ++ "erfl.c", + "math" ++ path.sep_str ++ "expf.c", + "math" ++ path.sep_str ++ "fabs.c", + "math" ++ path.sep_str ++ "fabsf.c", + "math" ++ path.sep_str ++ "fabsl.c", + "math" ++ path.sep_str ++ "fdim.c", + "math" ++ path.sep_str ++ "fdimf.c", + "math" ++ path.sep_str ++ "fdiml.c", + "math" ++ path.sep_str ++ "fma.c", + "math" ++ path.sep_str ++ "fmaf.c", + "math" ++ path.sep_str ++ "fmal.c", + "math" ++ path.sep_str ++ "fmax.c", + "math" ++ path.sep_str ++ "fmaxf.c", + "math" ++ path.sep_str ++ "fmaxl.c", + "math" ++ path.sep_str ++ "fmin.c", + "math" ++ path.sep_str ++ "fminf.c", + "math" ++ path.sep_str ++ "fminl.c", + "math" ++ path.sep_str ++ "fp_consts.c", + "math" ++ path.sep_str ++ "fp_constsf.c", + "math" ++ path.sep_str ++ "fp_constsl.c", + "math" ++ path.sep_str ++ "fpclassify.c", + "math" ++ path.sep_str ++ "fpclassifyf.c", + "math" ++ path.sep_str ++ "fpclassifyl.c", + "math" ++ path.sep_str ++ "frexpf.c", + "math" ++ path.sep_str ++ "hypot.c", + "math" ++ path.sep_str ++ "hypotf.c", + "math" ++ path.sep_str ++ "hypotl.c", + "math" ++ path.sep_str ++ "isnan.c", + "math" ++ path.sep_str ++ "isnanf.c", + "math" ++ path.sep_str ++ "isnanl.c", + "math" ++ path.sep_str ++ "ldexpf.c", + "math" ++ path.sep_str ++ "lgamma.c", + "math" ++ path.sep_str ++ "lgammaf.c", + "math" ++ path.sep_str ++ "lgammal.c", + "math" ++ path.sep_str ++ "llrint.c", + "math" ++ path.sep_str ++ "llrintf.c", + "math" ++ path.sep_str ++ "llrintl.c", + "math" ++ path.sep_str ++ "llround.c", + "math" ++ path.sep_str ++ "llroundf.c", + "math" ++ path.sep_str ++ "llroundl.c", + "math" ++ path.sep_str ++ "log10f.c", + "math" ++ path.sep_str ++ "logf.c", + "math" ++ path.sep_str ++ "lrint.c", + "math" ++ path.sep_str ++ "lrintf.c", + "math" ++ path.sep_str ++ "lrintl.c", + "math" ++ path.sep_str ++ "lround.c", + "math" ++ path.sep_str ++ "lroundf.c", + "math" ++ path.sep_str ++ "lroundl.c", + "math" ++ path.sep_str ++ "modf.c", + "math" ++ path.sep_str ++ "modff.c", + "math" ++ path.sep_str ++ "modfl.c", + "math" ++ path.sep_str ++ "nextafterf.c", + "math" ++ path.sep_str ++ "nextafterl.c", + "math" ++ path.sep_str ++ "nexttoward.c", + "math" ++ path.sep_str ++ "nexttowardf.c", + "math" ++ path.sep_str ++ "powf.c", + "math" ++ path.sep_str ++ "powi.c", + "math" ++ path.sep_str ++ "powif.c", + "math" ++ path.sep_str ++ "powil.c", + "math" ++ path.sep_str ++ "rint.c", + "math" ++ path.sep_str ++ "rintf.c", + "math" ++ path.sep_str ++ "rintl.c", + "math" ++ path.sep_str ++ "round.c", + "math" ++ path.sep_str ++ "roundf.c", + "math" ++ path.sep_str ++ "roundl.c", + "math" ++ path.sep_str ++ "s_erf.c", + "math" ++ path.sep_str ++ "sf_erf.c", + "math" ++ path.sep_str ++ "signbit.c", + "math" ++ path.sep_str ++ "signbitf.c", + "math" ++ path.sep_str ++ "signbitl.c", + "math" ++ path.sep_str ++ "signgam.c", + "math" ++ path.sep_str ++ "sinhf.c", + "math" ++ path.sep_str ++ "sinhl.c", + "math" ++ path.sep_str ++ "sqrt.c", + "math" ++ path.sep_str ++ "sqrtf.c", + "math" ++ path.sep_str ++ "sqrtl.c", + "math" ++ path.sep_str ++ "tanhf.c", + "math" ++ path.sep_str ++ "tanhl.c", + "math" ++ path.sep_str ++ "tgamma.c", + "math" ++ path.sep_str ++ "tgammaf.c", + "math" ++ path.sep_str ++ "tgammal.c", + "math" ++ path.sep_str ++ "truncl.c", + "misc" ++ path.sep_str ++ "alarm.c", + "misc" ++ path.sep_str ++ "basename.c", + "misc" ++ path.sep_str ++ "btowc.c", + "misc" ++ path.sep_str ++ "delay-f.c", + "misc" ++ path.sep_str ++ "delay-n.c", + "misc" ++ path.sep_str ++ "delayimp.c", + "misc" ++ path.sep_str ++ "dirent.c", + "misc" ++ path.sep_str ++ "dirname.c", + "misc" ++ path.sep_str ++ "feclearexcept.c", + "misc" ++ path.sep_str ++ "fegetenv.c", + "misc" ++ path.sep_str ++ "fegetexceptflag.c", + "misc" ++ path.sep_str ++ "fegetround.c", + "misc" ++ path.sep_str ++ "feholdexcept.c", + "misc" ++ path.sep_str ++ "feraiseexcept.c", + "misc" ++ path.sep_str ++ "fesetenv.c", + "misc" ++ path.sep_str ++ "fesetexceptflag.c", + "misc" ++ path.sep_str ++ "fesetround.c", + "misc" ++ path.sep_str ++ "fetestexcept.c", + "misc" ++ path.sep_str ++ "feupdateenv.c", + "misc" ++ path.sep_str ++ "ftruncate.c", + "misc" ++ path.sep_str ++ "ftw.c", + "misc" ++ path.sep_str ++ "ftw64.c", + "misc" ++ path.sep_str ++ "fwide.c", + "misc" ++ path.sep_str ++ "getlogin.c", + "misc" ++ path.sep_str ++ "getopt.c", + "misc" ++ path.sep_str ++ "gettimeofday.c", + "misc" ++ path.sep_str ++ "imaxabs.c", + "misc" ++ path.sep_str ++ "imaxdiv.c", + "misc" ++ path.sep_str ++ "isblank.c", + "misc" ++ path.sep_str ++ "iswblank.c", + "misc" ++ path.sep_str ++ "mbrtowc.c", + "misc" ++ path.sep_str ++ "mbsinit.c", + "misc" ++ path.sep_str ++ "mempcpy.c", + "misc" ++ path.sep_str ++ "mingw-aligned-malloc.c", + "misc" ++ path.sep_str ++ "mingw-fseek.c", + "misc" ++ path.sep_str ++ "mingw_getsp.S", + "misc" ++ path.sep_str ++ "mingw_matherr.c", + "misc" ++ path.sep_str ++ "mingw_mbwc_convert.c", + "misc" ++ path.sep_str ++ "mingw_usleep.c", + "misc" ++ path.sep_str ++ "mingw_wcstod.c", + "misc" ++ path.sep_str ++ "mingw_wcstof.c", + "misc" ++ path.sep_str ++ "mingw_wcstold.c", + "misc" ++ path.sep_str ++ "mkstemp.c", + "misc" ++ path.sep_str ++ "seterrno.c", + "misc" ++ path.sep_str ++ "sleep.c", + "misc" ++ path.sep_str ++ "strnlen.c", + "misc" ++ path.sep_str ++ "strsafe.c", + "misc" ++ path.sep_str ++ "strtoimax.c", + "misc" ++ path.sep_str ++ "strtold.c", + "misc" ++ path.sep_str ++ "strtoumax.c", + "misc" ++ path.sep_str ++ "tdelete.c", + "misc" ++ path.sep_str ++ "tfind.c", + "misc" ++ path.sep_str ++ "tsearch.c", + "misc" ++ path.sep_str ++ "twalk.c", + "misc" ++ path.sep_str ++ "uchar_c16rtomb.c", + "misc" ++ path.sep_str ++ "uchar_c32rtomb.c", + "misc" ++ path.sep_str ++ "uchar_mbrtoc16.c", + "misc" ++ path.sep_str ++ "uchar_mbrtoc32.c", + "misc" ++ path.sep_str ++ "wassert.c", + "misc" ++ path.sep_str ++ "wcrtomb.c", + "misc" ++ path.sep_str ++ "wcsnlen.c", + "misc" ++ path.sep_str ++ "wcstof.c", + "misc" ++ path.sep_str ++ "wcstoimax.c", + "misc" ++ path.sep_str ++ "wcstold.c", + "misc" ++ path.sep_str ++ "wcstoumax.c", + "misc" ++ path.sep_str ++ "wctob.c", + "misc" ++ path.sep_str ++ "wctrans.c", + "misc" ++ path.sep_str ++ "wctype.c", + "misc" ++ path.sep_str ++ "wdirent.c", + "misc" ++ path.sep_str ++ "winbs_uint64.c", + "misc" ++ path.sep_str ++ "winbs_ulong.c", + "misc" ++ path.sep_str ++ "winbs_ushort.c", + "misc" ++ path.sep_str ++ "wmemchr.c", + "misc" ++ path.sep_str ++ "wmemcmp.c", + "misc" ++ path.sep_str ++ "wmemcpy.c", + "misc" ++ path.sep_str ++ "wmemmove.c", + "misc" ++ path.sep_str ++ "wmempcpy.c", + "misc" ++ path.sep_str ++ "wmemset.c", + "stdio" ++ path.sep_str ++ "_Exit.c", + "stdio" ++ path.sep_str ++ "_findfirst64i32.c", + "stdio" ++ path.sep_str ++ "_findnext64i32.c", + "stdio" ++ path.sep_str ++ "_fstat.c", + "stdio" ++ path.sep_str ++ "_fstat64i32.c", + "stdio" ++ path.sep_str ++ "_ftime.c", + "stdio" ++ path.sep_str ++ "_getc_nolock.c", + "stdio" ++ path.sep_str ++ "_getwc_nolock.c", + "stdio" ++ path.sep_str ++ "_putc_nolock.c", + "stdio" ++ path.sep_str ++ "_putwc_nolock.c", + "stdio" ++ path.sep_str ++ "_stat.c", + "stdio" ++ path.sep_str ++ "_stat64i32.c", + "stdio" ++ path.sep_str ++ "_wfindfirst64i32.c", + "stdio" ++ path.sep_str ++ "_wfindnext64i32.c", + "stdio" ++ path.sep_str ++ "_wstat.c", + "stdio" ++ path.sep_str ++ "_wstat64i32.c", + "stdio" ++ path.sep_str ++ "asprintf.c", + "stdio" ++ path.sep_str ++ "atoll.c", + "stdio" ++ path.sep_str ++ "fgetpos64.c", + "stdio" ++ path.sep_str ++ "fopen64.c", + "stdio" ++ path.sep_str ++ "fseeko32.c", + "stdio" ++ path.sep_str ++ "fseeko64.c", + "stdio" ++ path.sep_str ++ "fsetpos64.c", + "stdio" ++ path.sep_str ++ "ftello.c", + "stdio" ++ path.sep_str ++ "ftello64.c", + "stdio" ++ path.sep_str ++ "ftruncate64.c", + "stdio" ++ path.sep_str ++ "lltoa.c", + "stdio" ++ path.sep_str ++ "lltow.c", + "stdio" ++ path.sep_str ++ "lseek64.c", + "stdio" ++ path.sep_str ++ "mingw_asprintf.c", + "stdio" ++ path.sep_str ++ "mingw_fprintf.c", + "stdio" ++ path.sep_str ++ "mingw_fprintfw.c", + "stdio" ++ path.sep_str ++ "mingw_fscanf.c", + "stdio" ++ path.sep_str ++ "mingw_fwscanf.c", + "stdio" ++ path.sep_str ++ "mingw_pformat.c", + "stdio" ++ path.sep_str ++ "mingw_pformatw.c", + "stdio" ++ path.sep_str ++ "mingw_printf.c", + "stdio" ++ path.sep_str ++ "mingw_printfw.c", + "stdio" ++ path.sep_str ++ "mingw_scanf.c", + "stdio" ++ path.sep_str ++ "mingw_snprintf.c", + "stdio" ++ path.sep_str ++ "mingw_snprintfw.c", + "stdio" ++ path.sep_str ++ "mingw_sprintf.c", + "stdio" ++ path.sep_str ++ "mingw_sprintfw.c", + "stdio" ++ path.sep_str ++ "mingw_sscanf.c", + "stdio" ++ path.sep_str ++ "mingw_swscanf.c", + "stdio" ++ path.sep_str ++ "mingw_vasprintf.c", + "stdio" ++ path.sep_str ++ "mingw_vfprintf.c", + "stdio" ++ path.sep_str ++ "mingw_vfprintfw.c", + "stdio" ++ path.sep_str ++ "mingw_vfscanf.c", + "stdio" ++ path.sep_str ++ "mingw_vprintf.c", + "stdio" ++ path.sep_str ++ "mingw_vprintfw.c", + "stdio" ++ path.sep_str ++ "mingw_vsnprintf.c", + "stdio" ++ path.sep_str ++ "mingw_vsnprintfw.c", + "stdio" ++ path.sep_str ++ "mingw_vsprintf.c", + "stdio" ++ path.sep_str ++ "mingw_vsprintfw.c", + "stdio" ++ path.sep_str ++ "mingw_wscanf.c", + "stdio" ++ path.sep_str ++ "mingw_wvfscanf.c", + "stdio" ++ path.sep_str ++ "scanf.S", + "stdio" ++ path.sep_str ++ "snprintf.c", + "stdio" ++ path.sep_str ++ "snwprintf.c", + "stdio" ++ path.sep_str ++ "strtof.c", + "stdio" ++ path.sep_str ++ "strtok_r.c", + "stdio" ++ path.sep_str ++ "truncate.c", + "stdio" ++ path.sep_str ++ "ulltoa.c", + "stdio" ++ path.sep_str ++ "ulltow.c", + "stdio" ++ path.sep_str ++ "vasprintf.c", + "stdio" ++ path.sep_str ++ "vfscanf.c", + "stdio" ++ path.sep_str ++ "vfscanf2.S", + "stdio" ++ path.sep_str ++ "vfwscanf.c", + "stdio" ++ path.sep_str ++ "vfwscanf2.S", + "stdio" ++ path.sep_str ++ "vscanf.c", + "stdio" ++ path.sep_str ++ "vscanf2.S", + "stdio" ++ path.sep_str ++ "vsnprintf.c", + "stdio" ++ path.sep_str ++ "vsnwprintf.c", + "stdio" ++ path.sep_str ++ "vsscanf.c", + "stdio" ++ path.sep_str ++ "vsscanf2.S", + "stdio" ++ path.sep_str ++ "vswscanf.c", + "stdio" ++ path.sep_str ++ "vswscanf2.S", + "stdio" ++ path.sep_str ++ "vwscanf.c", + "stdio" ++ path.sep_str ++ "vwscanf2.S", + "stdio" ++ path.sep_str ++ "wtoll.c", +}; + +const mingwex_x86_src = [_][]const u8{ + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "acosf.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "acosh.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "acoshf.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "acoshl.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "acosl.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "asinf.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "asinh.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "asinhf.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "asinhl.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "asinl.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "atan2.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "atan2f.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "atan2l.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "atanf.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "atanh.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "atanhf.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "atanhl.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "atanl.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "ceilf.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "ceill.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "ceil.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "_chgsignl.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "copysignl.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "cos.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "cosf.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "cosl.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "cosl_internal.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "cossin.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "exp2f.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "exp2l.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "exp2.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "exp.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "expl.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "expm1.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "expm1f.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "expm1l.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "floorf.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "floorl.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "floor.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "fmod.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "fmodf.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "fmodl.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "fucom.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "ilogbf.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "ilogbl.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "ilogb.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "internal_logl.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "ldexp.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "ldexpl.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "log10l.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "log1pf.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "log1pl.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "log1p.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "log2f.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "log2l.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "log2.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "logb.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "logbf.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "logbl.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "log.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "logl.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "nearbyintf.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "nearbyintl.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "nearbyint.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "pow.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "powl.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "remainderf.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "remainderl.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "remainder.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "remquof.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "remquol.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "remquo.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "scalbnf.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "scalbnl.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "scalbn.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "sin.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "sinf.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "sinl.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "sinl_internal.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "tanf.c", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "tanl.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "truncf.S", + "math" ++ path.sep_str ++ "x86" ++ path.sep_str ++ "trunc.S", +}; + +const mingwex_arm32_src = [_][]const u8{ + "math" ++ path.sep_str ++ "arm" ++ path.sep_str ++ "_chgsignl.S", + "math" ++ path.sep_str ++ "arm" ++ path.sep_str ++ "exp2.c", + "math" ++ path.sep_str ++ "arm" ++ path.sep_str ++ "nearbyint.S", + "math" ++ path.sep_str ++ "arm" ++ path.sep_str ++ "nearbyintf.S", + "math" ++ path.sep_str ++ "arm" ++ path.sep_str ++ "nearbyintl.S", + "math" ++ path.sep_str ++ "arm" ++ path.sep_str ++ "trunc.S", + "math" ++ path.sep_str ++ "arm" ++ path.sep_str ++ "truncf.S", +}; + +const mingwex_arm64_src = [_][]const u8{ + "math" ++ path.sep_str ++ "arm64" ++ path.sep_str ++ "_chgsignl.S", + "math" ++ path.sep_str ++ "arm64" ++ path.sep_str ++ "exp2f.S", + "math" ++ path.sep_str ++ "arm64" ++ path.sep_str ++ "exp2.S", + "math" ++ path.sep_str ++ "arm64" ++ path.sep_str ++ "nearbyintf.S", + "math" ++ path.sep_str ++ "arm64" ++ path.sep_str ++ "nearbyintl.S", + "math" ++ path.sep_str ++ "arm64" ++ path.sep_str ++ "nearbyint.S", + "math" ++ path.sep_str ++ "arm64" ++ path.sep_str ++ "truncf.S", + "math" ++ path.sep_str ++ "arm64" ++ path.sep_str ++ "trunc.S", +}; + +const uuid_src = [_][]const u8{ + "ativscp-uuid.c", + "atsmedia-uuid.c", + "bth-uuid.c", + "cguid-uuid.c", + "comcat-uuid.c", + "devguid.c", + "docobj-uuid.c", + "dxva-uuid.c", + "exdisp-uuid.c", + "extras-uuid.c", + "fwp-uuid.c", + "guid_nul.c", + "hlguids-uuid.c", + "hlink-uuid.c", + "mlang-uuid.c", + "msctf-uuid.c", + "mshtmhst-uuid.c", + "mshtml-uuid.c", + "msxml-uuid.c", + "netcon-uuid.c", + "ntddkbd-uuid.c", + "ntddmou-uuid.c", + "ntddpar-uuid.c", + "ntddscsi-uuid.c", + "ntddser-uuid.c", + "ntddstor-uuid.c", + "ntddvdeo-uuid.c", + "oaidl-uuid.c", + "objidl-uuid.c", + "objsafe-uuid.c", + "ocidl-uuid.c", + "oleacc-uuid.c", + "olectlid-uuid.c", + "oleidl-uuid.c", + "power-uuid.c", + "powrprof-uuid.c", + "uianimation-uuid.c", + "usbcamdi-uuid.c", + "usbiodef-uuid.c", + "uuid.c", + "vds-uuid.c", + "virtdisk-uuid.c", + "wia-uuid.c", +}; + +pub const always_link_libs = [_][]const u8{ + "advapi32", + "kernel32", + "msvcrt", + "ntdll", + "shell32", + "user32", +}; diff --git a/src/musl.zig b/src/musl.zig new file mode 100644 index 0000000000000000000000000000000000000000..ef4ea7236bc2f6e3a326f77b09d5e6cc1b82f831 --- /dev/null +++ b/src/musl.zig @@ -0,0 +1,2142 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; +const mem = std.mem; +const path = std.fs.path; +const assert = std.debug.assert; + +const target_util = @import("target.zig"); +const Compilation = @import("Compilation.zig"); +const build_options = @import("build_options"); + +pub const CRTFile = enum { + crti_o, + crtn_o, + crt1_o, + scrt1_o, + libc_a, +}; + +pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { + if (!build_options.have_llvm) { + return error.ZigCompilerNotBuiltWithLLVMExtensions; + } + const gpa = comp.gpa; + var arena_allocator = std.heap.ArenaAllocator.init(gpa); + defer arena_allocator.deinit(); + const arena = &arena_allocator.allocator; + + switch (crt_file) { + .crti_o => { + var args = std.ArrayList([]const u8).init(arena); + try add_cc_args(comp, arena, &args, false); + try args.appendSlice(&[_][]const u8{ + "-Qunused-arguments", + }); + return comp.build_crt_file("crti", .Obj, &[1]Compilation.CSourceFile{ + .{ + .src_path = try start_asm_path(comp, arena, "crti.s"), + .extra_flags = args.items, + }, + }); + }, + .crtn_o => { + var args = std.ArrayList([]const u8).init(arena); + try add_cc_args(comp, arena, &args, false); + try args.appendSlice(&[_][]const u8{ + "-Qunused-arguments", + }); + return comp.build_crt_file("crtn", .Obj, &[1]Compilation.CSourceFile{ + .{ + .src_path = try start_asm_path(comp, arena, "crtn.s"), + .extra_flags = args.items, + }, + }); + }, + .crt1_o => { + var args = std.ArrayList([]const u8).init(arena); + try add_cc_args(comp, arena, &args, false); + try args.appendSlice(&[_][]const u8{ + "-fno-stack-protector", + "-DCRT", + }); + return comp.build_crt_file("crt1", .Obj, &[1]Compilation.CSourceFile{ + .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ + "libc", "musl", "crt", "crt1.c", + }), + .extra_flags = args.items, + }, + }); + }, + .scrt1_o => { + var args = std.ArrayList([]const u8).init(arena); + try add_cc_args(comp, arena, &args, false); + try args.appendSlice(&[_][]const u8{ + "-fPIC", + "-fno-stack-protector", + "-DCRT", + }); + return comp.build_crt_file("Scrt1", .Obj, &[1]Compilation.CSourceFile{ + .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ + "libc", "musl", "crt", "Scrt1.c", + }), + .extra_flags = args.items, + }, + }); + }, + .libc_a => { + // When there is a src//foo.* then it should substitute for src/foo.* + // Even a .s file can substitute for a .c file. + const target = comp.getTarget(); + const arch_name = target_util.archMuslName(target.cpu.arch); + var source_table = std.StringArrayHashMap(Ext).init(comp.gpa); + defer source_table.deinit(); + + try source_table.ensureCapacity(compat_time32_files.len + src_files.len); + + for (src_files) |src_file| { + try addSrcFile(arena, &source_table, src_file); + } + + const time32_compat_arch_list = [_][]const u8{ "arm", "i386", "mips", "powerpc" }; + for (time32_compat_arch_list) |time32_compat_arch| { + if (mem.eql(u8, arch_name, time32_compat_arch)) { + for (compat_time32_files) |compat_time32_file| { + try addSrcFile(arena, &source_table, compat_time32_file); + } + } + } + + var c_source_files = std.ArrayList(Compilation.CSourceFile).init(comp.gpa); + defer c_source_files.deinit(); + + var override_path = std.ArrayList(u8).init(comp.gpa); + defer override_path.deinit(); + + const s = path.sep_str; + + for (source_table.items()) |entry| { + const src_file = entry.key; + const ext = entry.value; + + const dirname = path.dirname(src_file).?; + const basename = path.basename(src_file); + const noextbasename = mem.split(basename, ".").next().?; + const before_arch_dir = path.dirname(dirname).?; + const dirbasename = path.basename(dirname); + + var is_arch_specific = false; + // Architecture-specific implementations are under a / folder. + if (is_musl_arch_name(dirbasename)) { + if (!mem.eql(u8, dirbasename, arch_name)) + continue; // Not the architecture we're compiling for. + is_arch_specific = true; + } + if (!is_arch_specific) { + // Look for an arch specific override. + override_path.shrinkRetainingCapacity(0); + try override_path.writer().print("{}" ++ s ++ "{}" ++ s ++ "{}.s", .{ + dirname, arch_name, noextbasename, + }); + if (source_table.contains(override_path.items)) + continue; + + override_path.shrinkRetainingCapacity(0); + try override_path.writer().print("{}" ++ s ++ "{}" ++ s ++ "{}.S", .{ + dirname, arch_name, noextbasename, + }); + if (source_table.contains(override_path.items)) + continue; + + override_path.shrinkRetainingCapacity(0); + try override_path.writer().print("{}" ++ s ++ "{}" ++ s ++ "{}.c", .{ + dirname, arch_name, noextbasename, + }); + if (source_table.contains(override_path.items)) + continue; + } + + var args = std.ArrayList([]const u8).init(arena); + try add_cc_args(comp, arena, &args, ext == .o3); + try args.appendSlice(&[_][]const u8{ + "-Qunused-arguments", + "-w", // disable all warnings + }); + const c_source_file = try c_source_files.addOne(); + c_source_file.* = .{ + .src_path = try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", src_file }), + .extra_flags = args.items, + }; + } + return comp.build_crt_file("c", .Lib, c_source_files.items); + }, + } +} + +fn is_musl_arch_name(name: []const u8) bool { + const musl_arch_names = [_][]const u8{ + "aarch64", + "arm", + "generic", + "i386", + "m68k", + "microblaze", + "mips", + "mips64", + "mipsn32", + "or1k", + "powerpc", + "powerpc64", + "riscv64", + "s390x", + "sh", + "x32", + "x86_64", + }; + for (musl_arch_names) |musl_arch_name| { + if (mem.eql(u8, musl_arch_name, name)) { + return true; + } + } + return false; +} + +const Ext = enum { + assembly, + normal, + o3, +}; + +fn addSrcFile(arena: *Allocator, source_table: *std.StringArrayHashMap(Ext), file_path: []const u8) !void { + const ext: Ext = ext: { + if (mem.endsWith(u8, file_path, ".c")) { + if (mem.startsWith(u8, file_path, "musl/src/malloc/") or + mem.startsWith(u8, file_path, "musl/src/string/") or + mem.startsWith(u8, file_path, "musl/src/internal/")) + { + break :ext .o3; + } else { + break :ext .assembly; + } + } else if (mem.endsWith(u8, file_path, ".s") or mem.endsWith(u8, file_path, ".S")) { + break :ext .assembly; + } else { + unreachable; + } + }; + // TODO do this at comptime on the comptime data rather than at runtime + // probably best to wait until self-hosted is done and our comptime execution + // is faster and uses less memory. + const key = if (path.sep != '/') blk: { + const mutable_file_path = try arena.dupe(u8, file_path); + for (mutable_file_path) |*c| { + if (c.* == '/') { + c.* = path.sep; + } + } + break :blk mutable_file_path; + } else file_path; + source_table.putAssumeCapacityNoClobber(key, ext); +} + +fn add_cc_args( + comp: *Compilation, + arena: *Allocator, + args: *std.ArrayList([]const u8), + want_O3: bool, +) error{OutOfMemory}!void { + const target = comp.getTarget(); + const arch_name = target_util.archMuslName(target.cpu.arch); + const os_name = @tagName(target.os.tag); + const triple = try std.fmt.allocPrint(arena, "{}-{}-musl", .{ arch_name, os_name }); + const o_arg = if (want_O3) "-O3" else "-Os"; + + try args.appendSlice(&[_][]const u8{ + "-std=c99", + "-ffreestanding", + // Musl adds these args to builds with gcc but clang does not support them. + //"-fexcess-precision=standard", + //"-frounding-math", + "-Wa,--noexecstack", + "-D_XOPEN_SOURCE=700", + + "-I", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "musl", "arch", arch_name }), + + "-I", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "musl", "arch", "generic" }), + + "-I", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "musl", "src", "include" }), + + "-I", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "musl", "src", "internal" }), + + "-I", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "musl", "include" }), + + "-I", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "include", triple }), + + "-I", + try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "include", "generic-musl" }), + + o_arg, + + "-fomit-frame-pointer", + "-fno-unwind-tables", + "-fno-asynchronous-unwind-tables", + "-ffunction-sections", + "-fdata-sections", + }); +} + +fn start_asm_path(comp: *Compilation, arena: *Allocator, basename: []const u8) ![]const u8 { + const target = comp.getTarget(); + return comp.zig_lib_directory.join(arena, &[_][]const u8{ + "libc", "musl", "crt", target_util.archMuslName(target.cpu.arch), basename, + }); +} + +const src_files = [_][]const u8{ + "musl/src/aio/aio.c", + "musl/src/aio/aio_suspend.c", + "musl/src/aio/lio_listio.c", + "musl/src/complex/__cexp.c", + "musl/src/complex/__cexpf.c", + "musl/src/complex/cabs.c", + "musl/src/complex/cabsf.c", + "musl/src/complex/cabsl.c", + "musl/src/complex/cacos.c", + "musl/src/complex/cacosf.c", + "musl/src/complex/cacosh.c", + "musl/src/complex/cacoshf.c", + "musl/src/complex/cacoshl.c", + "musl/src/complex/cacosl.c", + "musl/src/complex/carg.c", + "musl/src/complex/cargf.c", + "musl/src/complex/cargl.c", + "musl/src/complex/casin.c", + "musl/src/complex/casinf.c", + "musl/src/complex/casinh.c", + "musl/src/complex/casinhf.c", + "musl/src/complex/casinhl.c", + "musl/src/complex/casinl.c", + "musl/src/complex/catan.c", + "musl/src/complex/catanf.c", + "musl/src/complex/catanh.c", + "musl/src/complex/catanhf.c", + "musl/src/complex/catanhl.c", + "musl/src/complex/catanl.c", + "musl/src/complex/ccos.c", + "musl/src/complex/ccosf.c", + "musl/src/complex/ccosh.c", + "musl/src/complex/ccoshf.c", + "musl/src/complex/ccoshl.c", + "musl/src/complex/ccosl.c", + "musl/src/complex/cexp.c", + "musl/src/complex/cexpf.c", + "musl/src/complex/cexpl.c", + "musl/src/complex/cimag.c", + "musl/src/complex/cimagf.c", + "musl/src/complex/cimagl.c", + "musl/src/complex/clog.c", + "musl/src/complex/clogf.c", + "musl/src/complex/clogl.c", + "musl/src/complex/conj.c", + "musl/src/complex/conjf.c", + "musl/src/complex/conjl.c", + "musl/src/complex/cpow.c", + "musl/src/complex/cpowf.c", + "musl/src/complex/cpowl.c", + "musl/src/complex/cproj.c", + "musl/src/complex/cprojf.c", + "musl/src/complex/cprojl.c", + "musl/src/complex/creal.c", + "musl/src/complex/crealf.c", + "musl/src/complex/creall.c", + "musl/src/complex/csin.c", + "musl/src/complex/csinf.c", + "musl/src/complex/csinh.c", + "musl/src/complex/csinhf.c", + "musl/src/complex/csinhl.c", + "musl/src/complex/csinl.c", + "musl/src/complex/csqrt.c", + "musl/src/complex/csqrtf.c", + "musl/src/complex/csqrtl.c", + "musl/src/complex/ctan.c", + "musl/src/complex/ctanf.c", + "musl/src/complex/ctanh.c", + "musl/src/complex/ctanhf.c", + "musl/src/complex/ctanhl.c", + "musl/src/complex/ctanl.c", + "musl/src/conf/confstr.c", + "musl/src/conf/fpathconf.c", + "musl/src/conf/legacy.c", + "musl/src/conf/pathconf.c", + "musl/src/conf/sysconf.c", + "musl/src/crypt/crypt.c", + "musl/src/crypt/crypt_blowfish.c", + "musl/src/crypt/crypt_des.c", + "musl/src/crypt/crypt_md5.c", + "musl/src/crypt/crypt_r.c", + "musl/src/crypt/crypt_sha256.c", + "musl/src/crypt/crypt_sha512.c", + "musl/src/crypt/encrypt.c", + "musl/src/ctype/__ctype_b_loc.c", + "musl/src/ctype/__ctype_get_mb_cur_max.c", + "musl/src/ctype/__ctype_tolower_loc.c", + "musl/src/ctype/__ctype_toupper_loc.c", + "musl/src/ctype/isalnum.c", + "musl/src/ctype/isalpha.c", + "musl/src/ctype/isascii.c", + "musl/src/ctype/isblank.c", + "musl/src/ctype/iscntrl.c", + "musl/src/ctype/isdigit.c", + "musl/src/ctype/isgraph.c", + "musl/src/ctype/islower.c", + "musl/src/ctype/isprint.c", + "musl/src/ctype/ispunct.c", + "musl/src/ctype/isspace.c", + "musl/src/ctype/isupper.c", + "musl/src/ctype/iswalnum.c", + "musl/src/ctype/iswalpha.c", + "musl/src/ctype/iswblank.c", + "musl/src/ctype/iswcntrl.c", + "musl/src/ctype/iswctype.c", + "musl/src/ctype/iswdigit.c", + "musl/src/ctype/iswgraph.c", + "musl/src/ctype/iswlower.c", + "musl/src/ctype/iswprint.c", + "musl/src/ctype/iswpunct.c", + "musl/src/ctype/iswspace.c", + "musl/src/ctype/iswupper.c", + "musl/src/ctype/iswxdigit.c", + "musl/src/ctype/isxdigit.c", + "musl/src/ctype/toascii.c", + "musl/src/ctype/tolower.c", + "musl/src/ctype/toupper.c", + "musl/src/ctype/towctrans.c", + "musl/src/ctype/wcswidth.c", + "musl/src/ctype/wctrans.c", + "musl/src/ctype/wcwidth.c", + "musl/src/dirent/alphasort.c", + "musl/src/dirent/closedir.c", + "musl/src/dirent/dirfd.c", + "musl/src/dirent/fdopendir.c", + "musl/src/dirent/opendir.c", + "musl/src/dirent/readdir.c", + "musl/src/dirent/readdir_r.c", + "musl/src/dirent/rewinddir.c", + "musl/src/dirent/scandir.c", + "musl/src/dirent/seekdir.c", + "musl/src/dirent/telldir.c", + "musl/src/dirent/versionsort.c", + "musl/src/env/__environ.c", + "musl/src/env/__init_tls.c", + "musl/src/env/__libc_start_main.c", + "musl/src/env/__reset_tls.c", + "musl/src/env/__stack_chk_fail.c", + "musl/src/env/clearenv.c", + "musl/src/env/getenv.c", + "musl/src/env/putenv.c", + "musl/src/env/secure_getenv.c", + "musl/src/env/setenv.c", + "musl/src/env/unsetenv.c", + "musl/src/errno/__errno_location.c", + "musl/src/errno/strerror.c", + "musl/src/exit/_Exit.c", + "musl/src/exit/abort.c", + "musl/src/exit/arm/__aeabi_atexit.c", + "musl/src/exit/assert.c", + "musl/src/exit/at_quick_exit.c", + "musl/src/exit/atexit.c", + "musl/src/exit/exit.c", + "musl/src/exit/quick_exit.c", + "musl/src/fcntl/creat.c", + "musl/src/fcntl/fcntl.c", + "musl/src/fcntl/open.c", + "musl/src/fcntl/openat.c", + "musl/src/fcntl/posix_fadvise.c", + "musl/src/fcntl/posix_fallocate.c", + "musl/src/fenv/__flt_rounds.c", + "musl/src/fenv/aarch64/fenv.s", + "musl/src/fenv/arm/fenv-hf.S", + "musl/src/fenv/arm/fenv.c", + "musl/src/fenv/fegetexceptflag.c", + "musl/src/fenv/feholdexcept.c", + "musl/src/fenv/fenv.c", + "musl/src/fenv/fesetexceptflag.c", + "musl/src/fenv/fesetround.c", + "musl/src/fenv/feupdateenv.c", + "musl/src/fenv/i386/fenv.s", + "musl/src/fenv/m68k/fenv.c", + "musl/src/fenv/mips/fenv-sf.c", + "musl/src/fenv/mips/fenv.S", + "musl/src/fenv/mips64/fenv-sf.c", + "musl/src/fenv/mips64/fenv.S", + "musl/src/fenv/mipsn32/fenv-sf.c", + "musl/src/fenv/mipsn32/fenv.S", + "musl/src/fenv/powerpc/fenv-sf.c", + "musl/src/fenv/powerpc/fenv.S", + "musl/src/fenv/powerpc64/fenv.c", + "musl/src/fenv/riscv64/fenv-sf.c", + "musl/src/fenv/riscv64/fenv.S", + "musl/src/fenv/s390x/fenv.c", + "musl/src/fenv/sh/fenv-nofpu.c", + "musl/src/fenv/sh/fenv.S", + "musl/src/fenv/x32/fenv.s", + "musl/src/fenv/x86_64/fenv.s", + "musl/src/internal/defsysinfo.c", + "musl/src/internal/floatscan.c", + "musl/src/internal/i386/defsysinfo.s", + "musl/src/internal/intscan.c", + "musl/src/internal/libc.c", + "musl/src/internal/procfdname.c", + "musl/src/internal/sh/__shcall.c", + "musl/src/internal/shgetc.c", + "musl/src/internal/syscall_ret.c", + "musl/src/internal/vdso.c", + "musl/src/internal/version.c", + "musl/src/ipc/ftok.c", + "musl/src/ipc/msgctl.c", + "musl/src/ipc/msgget.c", + "musl/src/ipc/msgrcv.c", + "musl/src/ipc/msgsnd.c", + "musl/src/ipc/semctl.c", + "musl/src/ipc/semget.c", + "musl/src/ipc/semop.c", + "musl/src/ipc/semtimedop.c", + "musl/src/ipc/shmat.c", + "musl/src/ipc/shmctl.c", + "musl/src/ipc/shmdt.c", + "musl/src/ipc/shmget.c", + "musl/src/ldso/__dlsym.c", + "musl/src/ldso/aarch64/dlsym.s", + "musl/src/ldso/aarch64/tlsdesc.s", + "musl/src/ldso/arm/dlsym.s", + "musl/src/ldso/arm/dlsym_time64.S", + "musl/src/ldso/arm/find_exidx.c", + "musl/src/ldso/arm/tlsdesc.S", + "musl/src/ldso/dl_iterate_phdr.c", + "musl/src/ldso/dladdr.c", + "musl/src/ldso/dlclose.c", + "musl/src/ldso/dlerror.c", + "musl/src/ldso/dlinfo.c", + "musl/src/ldso/dlopen.c", + "musl/src/ldso/dlsym.c", + "musl/src/ldso/i386/dlsym.s", + "musl/src/ldso/i386/dlsym_time64.S", + "musl/src/ldso/i386/tlsdesc.s", + "musl/src/ldso/m68k/dlsym.s", + "musl/src/ldso/m68k/dlsym_time64.S", + "musl/src/ldso/microblaze/dlsym.s", + "musl/src/ldso/microblaze/dlsym_time64.S", + "musl/src/ldso/mips/dlsym.s", + "musl/src/ldso/mips/dlsym_time64.S", + "musl/src/ldso/mips64/dlsym.s", + "musl/src/ldso/mipsn32/dlsym.s", + "musl/src/ldso/mipsn32/dlsym_time64.S", + "musl/src/ldso/or1k/dlsym.s", + "musl/src/ldso/or1k/dlsym_time64.S", + "musl/src/ldso/powerpc/dlsym.s", + "musl/src/ldso/powerpc/dlsym_time64.S", + "musl/src/ldso/powerpc64/dlsym.s", + "musl/src/ldso/riscv64/dlsym.s", + "musl/src/ldso/s390x/dlsym.s", + "musl/src/ldso/sh/dlsym.s", + "musl/src/ldso/sh/dlsym_time64.S", + "musl/src/ldso/tlsdesc.c", + "musl/src/ldso/x32/dlsym.s", + "musl/src/ldso/x86_64/dlsym.s", + "musl/src/ldso/x86_64/tlsdesc.s", + "musl/src/legacy/cuserid.c", + "musl/src/legacy/daemon.c", + "musl/src/legacy/err.c", + "musl/src/legacy/euidaccess.c", + "musl/src/legacy/ftw.c", + "musl/src/legacy/futimes.c", + "musl/src/legacy/getdtablesize.c", + "musl/src/legacy/getloadavg.c", + "musl/src/legacy/getpagesize.c", + "musl/src/legacy/getpass.c", + "musl/src/legacy/getusershell.c", + "musl/src/legacy/isastream.c", + "musl/src/legacy/lutimes.c", + "musl/src/legacy/ulimit.c", + "musl/src/legacy/utmpx.c", + "musl/src/legacy/valloc.c", + "musl/src/linux/adjtime.c", + "musl/src/linux/adjtimex.c", + "musl/src/linux/arch_prctl.c", + "musl/src/linux/brk.c", + "musl/src/linux/cache.c", + "musl/src/linux/cap.c", + "musl/src/linux/chroot.c", + "musl/src/linux/clock_adjtime.c", + "musl/src/linux/clone.c", + "musl/src/linux/copy_file_range.c", + "musl/src/linux/epoll.c", + "musl/src/linux/eventfd.c", + "musl/src/linux/fallocate.c", + "musl/src/linux/fanotify.c", + "musl/src/linux/flock.c", + "musl/src/linux/getdents.c", + "musl/src/linux/getrandom.c", + "musl/src/linux/inotify.c", + "musl/src/linux/ioperm.c", + "musl/src/linux/iopl.c", + "musl/src/linux/klogctl.c", + "musl/src/linux/membarrier.c", + "musl/src/linux/memfd_create.c", + "musl/src/linux/mlock2.c", + "musl/src/linux/module.c", + "musl/src/linux/mount.c", + "musl/src/linux/name_to_handle_at.c", + "musl/src/linux/open_by_handle_at.c", + "musl/src/linux/personality.c", + "musl/src/linux/pivot_root.c", + "musl/src/linux/ppoll.c", + "musl/src/linux/prctl.c", + "musl/src/linux/prlimit.c", + "musl/src/linux/process_vm.c", + "musl/src/linux/ptrace.c", + "musl/src/linux/quotactl.c", + "musl/src/linux/readahead.c", + "musl/src/linux/reboot.c", + "musl/src/linux/remap_file_pages.c", + "musl/src/linux/sbrk.c", + "musl/src/linux/sendfile.c", + "musl/src/linux/setfsgid.c", + "musl/src/linux/setfsuid.c", + "musl/src/linux/setgroups.c", + "musl/src/linux/sethostname.c", + "musl/src/linux/setns.c", + "musl/src/linux/settimeofday.c", + "musl/src/linux/signalfd.c", + "musl/src/linux/splice.c", + "musl/src/linux/stime.c", + "musl/src/linux/swap.c", + "musl/src/linux/sync_file_range.c", + "musl/src/linux/syncfs.c", + "musl/src/linux/sysinfo.c", + "musl/src/linux/tee.c", + "musl/src/linux/timerfd.c", + "musl/src/linux/unshare.c", + "musl/src/linux/utimes.c", + "musl/src/linux/vhangup.c", + "musl/src/linux/vmsplice.c", + "musl/src/linux/wait3.c", + "musl/src/linux/wait4.c", + "musl/src/linux/x32/sysinfo.c", + "musl/src/linux/xattr.c", + "musl/src/locale/__lctrans.c", + "musl/src/locale/__mo_lookup.c", + "musl/src/locale/bind_textdomain_codeset.c", + "musl/src/locale/c_locale.c", + "musl/src/locale/catclose.c", + "musl/src/locale/catgets.c", + "musl/src/locale/catopen.c", + "musl/src/locale/dcngettext.c", + "musl/src/locale/duplocale.c", + "musl/src/locale/freelocale.c", + "musl/src/locale/iconv.c", + "musl/src/locale/iconv_close.c", + "musl/src/locale/langinfo.c", + "musl/src/locale/locale_map.c", + "musl/src/locale/localeconv.c", + "musl/src/locale/newlocale.c", + "musl/src/locale/pleval.c", + "musl/src/locale/setlocale.c", + "musl/src/locale/strcoll.c", + "musl/src/locale/strfmon.c", + "musl/src/locale/strxfrm.c", + "musl/src/locale/textdomain.c", + "musl/src/locale/uselocale.c", + "musl/src/locale/wcscoll.c", + "musl/src/locale/wcsxfrm.c", + "musl/src/malloc/aligned_alloc.c", + "musl/src/malloc/expand_heap.c", + "musl/src/malloc/lite_malloc.c", + "musl/src/malloc/malloc.c", + "musl/src/malloc/malloc_usable_size.c", + "musl/src/malloc/memalign.c", + "musl/src/malloc/posix_memalign.c", + "musl/src/math/__cos.c", + "musl/src/math/__cosdf.c", + "musl/src/math/__cosl.c", + "musl/src/math/__expo2.c", + "musl/src/math/__expo2f.c", + "musl/src/math/__fpclassify.c", + "musl/src/math/__fpclassifyf.c", + "musl/src/math/__fpclassifyl.c", + "musl/src/math/__invtrigl.c", + "musl/src/math/__math_divzero.c", + "musl/src/math/__math_divzerof.c", + "musl/src/math/__math_invalid.c", + "musl/src/math/__math_invalidf.c", + "musl/src/math/__math_oflow.c", + "musl/src/math/__math_oflowf.c", + "musl/src/math/__math_uflow.c", + "musl/src/math/__math_uflowf.c", + "musl/src/math/__math_xflow.c", + "musl/src/math/__math_xflowf.c", + "musl/src/math/__polevll.c", + "musl/src/math/__rem_pio2.c", + "musl/src/math/__rem_pio2_large.c", + "musl/src/math/__rem_pio2f.c", + "musl/src/math/__rem_pio2l.c", + "musl/src/math/__signbit.c", + "musl/src/math/__signbitf.c", + "musl/src/math/__signbitl.c", + "musl/src/math/__sin.c", + "musl/src/math/__sindf.c", + "musl/src/math/__sinl.c", + "musl/src/math/__tan.c", + "musl/src/math/__tandf.c", + "musl/src/math/__tanl.c", + "musl/src/math/aarch64/ceil.c", + "musl/src/math/aarch64/ceilf.c", + "musl/src/math/aarch64/fabs.c", + "musl/src/math/aarch64/fabsf.c", + "musl/src/math/aarch64/floor.c", + "musl/src/math/aarch64/floorf.c", + "musl/src/math/aarch64/fma.c", + "musl/src/math/aarch64/fmaf.c", + "musl/src/math/aarch64/fmax.c", + "musl/src/math/aarch64/fmaxf.c", + "musl/src/math/aarch64/fmin.c", + "musl/src/math/aarch64/fminf.c", + "musl/src/math/aarch64/llrint.c", + "musl/src/math/aarch64/llrintf.c", + "musl/src/math/aarch64/llround.c", + "musl/src/math/aarch64/llroundf.c", + "musl/src/math/aarch64/lrint.c", + "musl/src/math/aarch64/lrintf.c", + "musl/src/math/aarch64/lround.c", + "musl/src/math/aarch64/lroundf.c", + "musl/src/math/aarch64/nearbyint.c", + "musl/src/math/aarch64/nearbyintf.c", + "musl/src/math/aarch64/rint.c", + "musl/src/math/aarch64/rintf.c", + "musl/src/math/aarch64/round.c", + "musl/src/math/aarch64/roundf.c", + "musl/src/math/aarch64/sqrt.c", + "musl/src/math/aarch64/sqrtf.c", + "musl/src/math/aarch64/trunc.c", + "musl/src/math/aarch64/truncf.c", + "musl/src/math/acos.c", + "musl/src/math/acosf.c", + "musl/src/math/acosh.c", + "musl/src/math/acoshf.c", + "musl/src/math/acoshl.c", + "musl/src/math/acosl.c", + "musl/src/math/arm/fabs.c", + "musl/src/math/arm/fabsf.c", + "musl/src/math/arm/fma.c", + "musl/src/math/arm/fmaf.c", + "musl/src/math/arm/sqrt.c", + "musl/src/math/arm/sqrtf.c", + "musl/src/math/asin.c", + "musl/src/math/asinf.c", + "musl/src/math/asinh.c", + "musl/src/math/asinhf.c", + "musl/src/math/asinhl.c", + "musl/src/math/asinl.c", + "musl/src/math/atan.c", + "musl/src/math/atan2.c", + "musl/src/math/atan2f.c", + "musl/src/math/atan2l.c", + "musl/src/math/atanf.c", + "musl/src/math/atanh.c", + "musl/src/math/atanhf.c", + "musl/src/math/atanhl.c", + "musl/src/math/atanl.c", + "musl/src/math/cbrt.c", + "musl/src/math/cbrtf.c", + "musl/src/math/cbrtl.c", + "musl/src/math/ceil.c", + "musl/src/math/ceilf.c", + "musl/src/math/ceill.c", + "musl/src/math/copysign.c", + "musl/src/math/copysignf.c", + "musl/src/math/copysignl.c", + "musl/src/math/cos.c", + "musl/src/math/cosf.c", + "musl/src/math/cosh.c", + "musl/src/math/coshf.c", + "musl/src/math/coshl.c", + "musl/src/math/cosl.c", + "musl/src/math/erf.c", + "musl/src/math/erff.c", + "musl/src/math/erfl.c", + "musl/src/math/exp.c", + "musl/src/math/exp10.c", + "musl/src/math/exp10f.c", + "musl/src/math/exp10l.c", + "musl/src/math/exp2.c", + "musl/src/math/exp2f.c", + "musl/src/math/exp2f_data.c", + "musl/src/math/exp2l.c", + "musl/src/math/exp_data.c", + "musl/src/math/expf.c", + "musl/src/math/expl.c", + "musl/src/math/expm1.c", + "musl/src/math/expm1f.c", + "musl/src/math/expm1l.c", + "musl/src/math/fabs.c", + "musl/src/math/fabsf.c", + "musl/src/math/fabsl.c", + "musl/src/math/fdim.c", + "musl/src/math/fdimf.c", + "musl/src/math/fdiml.c", + "musl/src/math/finite.c", + "musl/src/math/finitef.c", + "musl/src/math/floor.c", + "musl/src/math/floorf.c", + "musl/src/math/floorl.c", + "musl/src/math/fma.c", + "musl/src/math/fmaf.c", + "musl/src/math/fmal.c", + "musl/src/math/fmax.c", + "musl/src/math/fmaxf.c", + "musl/src/math/fmaxl.c", + "musl/src/math/fmin.c", + "musl/src/math/fminf.c", + "musl/src/math/fminl.c", + "musl/src/math/fmod.c", + "musl/src/math/fmodf.c", + "musl/src/math/fmodl.c", + "musl/src/math/frexp.c", + "musl/src/math/frexpf.c", + "musl/src/math/frexpl.c", + "musl/src/math/hypot.c", + "musl/src/math/hypotf.c", + "musl/src/math/hypotl.c", + "musl/src/math/i386/__invtrigl.s", + "musl/src/math/i386/acos.s", + "musl/src/math/i386/acosf.s", + "musl/src/math/i386/acosl.s", + "musl/src/math/i386/asin.s", + "musl/src/math/i386/asinf.s", + "musl/src/math/i386/asinl.s", + "musl/src/math/i386/atan.s", + "musl/src/math/i386/atan2.s", + "musl/src/math/i386/atan2f.s", + "musl/src/math/i386/atan2l.s", + "musl/src/math/i386/atanf.s", + "musl/src/math/i386/atanl.s", + "musl/src/math/i386/ceil.s", + "musl/src/math/i386/ceilf.s", + "musl/src/math/i386/ceill.s", + "musl/src/math/i386/exp2l.s", + "musl/src/math/i386/exp_ld.s", + "musl/src/math/i386/expl.s", + "musl/src/math/i386/expm1l.s", + "musl/src/math/i386/fabs.s", + "musl/src/math/i386/fabsf.s", + "musl/src/math/i386/fabsl.s", + "musl/src/math/i386/floor.s", + "musl/src/math/i386/floorf.s", + "musl/src/math/i386/floorl.s", + "musl/src/math/i386/fmod.s", + "musl/src/math/i386/fmodf.s", + "musl/src/math/i386/fmodl.s", + "musl/src/math/i386/hypot.s", + "musl/src/math/i386/hypotf.s", + "musl/src/math/i386/ldexp.s", + "musl/src/math/i386/ldexpf.s", + "musl/src/math/i386/ldexpl.s", + "musl/src/math/i386/llrint.s", + "musl/src/math/i386/llrintf.s", + "musl/src/math/i386/llrintl.s", + "musl/src/math/i386/log.s", + "musl/src/math/i386/log10.s", + "musl/src/math/i386/log10f.s", + "musl/src/math/i386/log10l.s", + "musl/src/math/i386/log1p.s", + "musl/src/math/i386/log1pf.s", + "musl/src/math/i386/log1pl.s", + "musl/src/math/i386/log2.s", + "musl/src/math/i386/log2f.s", + "musl/src/math/i386/log2l.s", + "musl/src/math/i386/logf.s", + "musl/src/math/i386/logl.s", + "musl/src/math/i386/lrint.s", + "musl/src/math/i386/lrintf.s", + "musl/src/math/i386/lrintl.s", + "musl/src/math/i386/remainder.s", + "musl/src/math/i386/remainderf.s", + "musl/src/math/i386/remainderl.s", + "musl/src/math/i386/remquo.s", + "musl/src/math/i386/remquof.s", + "musl/src/math/i386/remquol.s", + "musl/src/math/i386/rint.s", + "musl/src/math/i386/rintf.s", + "musl/src/math/i386/rintl.s", + "musl/src/math/i386/scalbln.s", + "musl/src/math/i386/scalblnf.s", + "musl/src/math/i386/scalblnl.s", + "musl/src/math/i386/scalbn.s", + "musl/src/math/i386/scalbnf.s", + "musl/src/math/i386/scalbnl.s", + "musl/src/math/i386/sqrt.s", + "musl/src/math/i386/sqrtf.s", + "musl/src/math/i386/sqrtl.s", + "musl/src/math/i386/trunc.s", + "musl/src/math/i386/truncf.s", + "musl/src/math/i386/truncl.s", + "musl/src/math/ilogb.c", + "musl/src/math/ilogbf.c", + "musl/src/math/ilogbl.c", + "musl/src/math/j0.c", + "musl/src/math/j0f.c", + "musl/src/math/j1.c", + "musl/src/math/j1f.c", + "musl/src/math/jn.c", + "musl/src/math/jnf.c", + "musl/src/math/ldexp.c", + "musl/src/math/ldexpf.c", + "musl/src/math/ldexpl.c", + "musl/src/math/lgamma.c", + "musl/src/math/lgamma_r.c", + "musl/src/math/lgammaf.c", + "musl/src/math/lgammaf_r.c", + "musl/src/math/lgammal.c", + "musl/src/math/llrint.c", + "musl/src/math/llrintf.c", + "musl/src/math/llrintl.c", + "musl/src/math/llround.c", + "musl/src/math/llroundf.c", + "musl/src/math/llroundl.c", + "musl/src/math/log.c", + "musl/src/math/log10.c", + "musl/src/math/log10f.c", + "musl/src/math/log10l.c", + "musl/src/math/log1p.c", + "musl/src/math/log1pf.c", + "musl/src/math/log1pl.c", + "musl/src/math/log2.c", + "musl/src/math/log2_data.c", + "musl/src/math/log2f.c", + "musl/src/math/log2f_data.c", + "musl/src/math/log2l.c", + "musl/src/math/log_data.c", + "musl/src/math/logb.c", + "musl/src/math/logbf.c", + "musl/src/math/logbl.c", + "musl/src/math/logf.c", + "musl/src/math/logf_data.c", + "musl/src/math/logl.c", + "musl/src/math/lrint.c", + "musl/src/math/lrintf.c", + "musl/src/math/lrintl.c", + "musl/src/math/lround.c", + "musl/src/math/lroundf.c", + "musl/src/math/lroundl.c", + "musl/src/math/mips/fabs.c", + "musl/src/math/mips/fabsf.c", + "musl/src/math/mips/sqrt.c", + "musl/src/math/mips/sqrtf.c", + "musl/src/math/modf.c", + "musl/src/math/modff.c", + "musl/src/math/modfl.c", + "musl/src/math/nan.c", + "musl/src/math/nanf.c", + "musl/src/math/nanl.c", + "musl/src/math/nearbyint.c", + "musl/src/math/nearbyintf.c", + "musl/src/math/nearbyintl.c", + "musl/src/math/nextafter.c", + "musl/src/math/nextafterf.c", + "musl/src/math/nextafterl.c", + "musl/src/math/nexttoward.c", + "musl/src/math/nexttowardf.c", + "musl/src/math/nexttowardl.c", + "musl/src/math/pow.c", + "musl/src/math/pow_data.c", + "musl/src/math/powerpc/fabs.c", + "musl/src/math/powerpc/fabsf.c", + "musl/src/math/powerpc/fma.c", + "musl/src/math/powerpc/fmaf.c", + "musl/src/math/powerpc/sqrt.c", + "musl/src/math/powerpc/sqrtf.c", + "musl/src/math/powerpc64/ceil.c", + "musl/src/math/powerpc64/ceilf.c", + "musl/src/math/powerpc64/fabs.c", + "musl/src/math/powerpc64/fabsf.c", + "musl/src/math/powerpc64/floor.c", + "musl/src/math/powerpc64/floorf.c", + "musl/src/math/powerpc64/fma.c", + "musl/src/math/powerpc64/fmaf.c", + "musl/src/math/powerpc64/fmax.c", + "musl/src/math/powerpc64/fmaxf.c", + "musl/src/math/powerpc64/fmin.c", + "musl/src/math/powerpc64/fminf.c", + "musl/src/math/powerpc64/lrint.c", + "musl/src/math/powerpc64/lrintf.c", + "musl/src/math/powerpc64/lround.c", + "musl/src/math/powerpc64/lroundf.c", + "musl/src/math/powerpc64/round.c", + "musl/src/math/powerpc64/roundf.c", + "musl/src/math/powerpc64/sqrt.c", + "musl/src/math/powerpc64/sqrtf.c", + "musl/src/math/powerpc64/trunc.c", + "musl/src/math/powerpc64/truncf.c", + "musl/src/math/powf.c", + "musl/src/math/powf_data.c", + "musl/src/math/powl.c", + "musl/src/math/remainder.c", + "musl/src/math/remainderf.c", + "musl/src/math/remainderl.c", + "musl/src/math/remquo.c", + "musl/src/math/remquof.c", + "musl/src/math/remquol.c", + "musl/src/math/rint.c", + "musl/src/math/rintf.c", + "musl/src/math/rintl.c", + "musl/src/math/riscv64/copysign.c", + "musl/src/math/riscv64/copysignf.c", + "musl/src/math/riscv64/fabs.c", + "musl/src/math/riscv64/fabsf.c", + "musl/src/math/riscv64/fma.c", + "musl/src/math/riscv64/fmaf.c", + "musl/src/math/riscv64/fmax.c", + "musl/src/math/riscv64/fmaxf.c", + "musl/src/math/riscv64/fmin.c", + "musl/src/math/riscv64/fminf.c", + "musl/src/math/riscv64/sqrt.c", + "musl/src/math/riscv64/sqrtf.c", + "musl/src/math/round.c", + "musl/src/math/roundf.c", + "musl/src/math/roundl.c", + "musl/src/math/s390x/ceil.c", + "musl/src/math/s390x/ceilf.c", + "musl/src/math/s390x/ceill.c", + "musl/src/math/s390x/fabs.c", + "musl/src/math/s390x/fabsf.c", + "musl/src/math/s390x/fabsl.c", + "musl/src/math/s390x/floor.c", + "musl/src/math/s390x/floorf.c", + "musl/src/math/s390x/floorl.c", + "musl/src/math/s390x/fma.c", + "musl/src/math/s390x/fmaf.c", + "musl/src/math/s390x/nearbyint.c", + "musl/src/math/s390x/nearbyintf.c", + "musl/src/math/s390x/nearbyintl.c", + "musl/src/math/s390x/rint.c", + "musl/src/math/s390x/rintf.c", + "musl/src/math/s390x/rintl.c", + "musl/src/math/s390x/round.c", + "musl/src/math/s390x/roundf.c", + "musl/src/math/s390x/roundl.c", + "musl/src/math/s390x/sqrt.c", + "musl/src/math/s390x/sqrtf.c", + "musl/src/math/s390x/sqrtl.c", + "musl/src/math/s390x/trunc.c", + "musl/src/math/s390x/truncf.c", + "musl/src/math/s390x/truncl.c", + "musl/src/math/scalb.c", + "musl/src/math/scalbf.c", + "musl/src/math/scalbln.c", + "musl/src/math/scalblnf.c", + "musl/src/math/scalblnl.c", + "musl/src/math/scalbn.c", + "musl/src/math/scalbnf.c", + "musl/src/math/scalbnl.c", + "musl/src/math/signgam.c", + "musl/src/math/significand.c", + "musl/src/math/significandf.c", + "musl/src/math/sin.c", + "musl/src/math/sincos.c", + "musl/src/math/sincosf.c", + "musl/src/math/sincosl.c", + "musl/src/math/sinf.c", + "musl/src/math/sinh.c", + "musl/src/math/sinhf.c", + "musl/src/math/sinhl.c", + "musl/src/math/sinl.c", + "musl/src/math/sqrt.c", + "musl/src/math/sqrtf.c", + "musl/src/math/sqrtl.c", + "musl/src/math/tan.c", + "musl/src/math/tanf.c", + "musl/src/math/tanh.c", + "musl/src/math/tanhf.c", + "musl/src/math/tanhl.c", + "musl/src/math/tanl.c", + "musl/src/math/tgamma.c", + "musl/src/math/tgammaf.c", + "musl/src/math/tgammal.c", + "musl/src/math/trunc.c", + "musl/src/math/truncf.c", + "musl/src/math/truncl.c", + "musl/src/math/x32/__invtrigl.s", + "musl/src/math/x32/acosl.s", + "musl/src/math/x32/asinl.s", + "musl/src/math/x32/atan2l.s", + "musl/src/math/x32/atanl.s", + "musl/src/math/x32/ceill.s", + "musl/src/math/x32/exp2l.s", + "musl/src/math/x32/expl.s", + "musl/src/math/x32/expm1l.s", + "musl/src/math/x32/fabs.s", + "musl/src/math/x32/fabsf.s", + "musl/src/math/x32/fabsl.s", + "musl/src/math/x32/floorl.s", + "musl/src/math/x32/fma.c", + "musl/src/math/x32/fmaf.c", + "musl/src/math/x32/fmodl.s", + "musl/src/math/x32/llrint.s", + "musl/src/math/x32/llrintf.s", + "musl/src/math/x32/llrintl.s", + "musl/src/math/x32/log10l.s", + "musl/src/math/x32/log1pl.s", + "musl/src/math/x32/log2l.s", + "musl/src/math/x32/logl.s", + "musl/src/math/x32/lrint.s", + "musl/src/math/x32/lrintf.s", + "musl/src/math/x32/lrintl.s", + "musl/src/math/x32/remainderl.s", + "musl/src/math/x32/rintl.s", + "musl/src/math/x32/sqrt.s", + "musl/src/math/x32/sqrtf.s", + "musl/src/math/x32/sqrtl.s", + "musl/src/math/x32/truncl.s", + "musl/src/math/x86_64/__invtrigl.s", + "musl/src/math/x86_64/acosl.s", + "musl/src/math/x86_64/asinl.s", + "musl/src/math/x86_64/atan2l.s", + "musl/src/math/x86_64/atanl.s", + "musl/src/math/x86_64/ceill.s", + "musl/src/math/x86_64/exp2l.s", + "musl/src/math/x86_64/expl.s", + "musl/src/math/x86_64/expm1l.s", + "musl/src/math/x86_64/fabs.s", + "musl/src/math/x86_64/fabsf.s", + "musl/src/math/x86_64/fabsl.s", + "musl/src/math/x86_64/floorl.s", + "musl/src/math/x86_64/fma.c", + "musl/src/math/x86_64/fmaf.c", + "musl/src/math/x86_64/fmodl.s", + "musl/src/math/x86_64/llrint.s", + "musl/src/math/x86_64/llrintf.s", + "musl/src/math/x86_64/llrintl.s", + "musl/src/math/x86_64/log10l.s", + "musl/src/math/x86_64/log1pl.s", + "musl/src/math/x86_64/log2l.s", + "musl/src/math/x86_64/logl.s", + "musl/src/math/x86_64/lrint.s", + "musl/src/math/x86_64/lrintf.s", + "musl/src/math/x86_64/lrintl.s", + "musl/src/math/x86_64/remainderl.s", + "musl/src/math/x86_64/rintl.s", + "musl/src/math/x86_64/sqrt.s", + "musl/src/math/x86_64/sqrtf.s", + "musl/src/math/x86_64/sqrtl.s", + "musl/src/math/x86_64/truncl.s", + "musl/src/misc/a64l.c", + "musl/src/misc/basename.c", + "musl/src/misc/dirname.c", + "musl/src/misc/ffs.c", + "musl/src/misc/ffsl.c", + "musl/src/misc/ffsll.c", + "musl/src/misc/fmtmsg.c", + "musl/src/misc/forkpty.c", + "musl/src/misc/get_current_dir_name.c", + "musl/src/misc/getauxval.c", + "musl/src/misc/getdomainname.c", + "musl/src/misc/getentropy.c", + "musl/src/misc/gethostid.c", + "musl/src/misc/getopt.c", + "musl/src/misc/getopt_long.c", + "musl/src/misc/getpriority.c", + "musl/src/misc/getresgid.c", + "musl/src/misc/getresuid.c", + "musl/src/misc/getrlimit.c", + "musl/src/misc/getrusage.c", + "musl/src/misc/getsubopt.c", + "musl/src/misc/initgroups.c", + "musl/src/misc/ioctl.c", + "musl/src/misc/issetugid.c", + "musl/src/misc/lockf.c", + "musl/src/misc/login_tty.c", + "musl/src/misc/mntent.c", + "musl/src/misc/nftw.c", + "musl/src/misc/openpty.c", + "musl/src/misc/ptsname.c", + "musl/src/misc/pty.c", + "musl/src/misc/realpath.c", + "musl/src/misc/setdomainname.c", + "musl/src/misc/setpriority.c", + "musl/src/misc/setrlimit.c", + "musl/src/misc/syscall.c", + "musl/src/misc/syslog.c", + "musl/src/misc/uname.c", + "musl/src/misc/wordexp.c", + "musl/src/mman/madvise.c", + "musl/src/mman/mincore.c", + "musl/src/mman/mlock.c", + "musl/src/mman/mlockall.c", + "musl/src/mman/mmap.c", + "musl/src/mman/mprotect.c", + "musl/src/mman/mremap.c", + "musl/src/mman/msync.c", + "musl/src/mman/munlock.c", + "musl/src/mman/munlockall.c", + "musl/src/mman/munmap.c", + "musl/src/mman/posix_madvise.c", + "musl/src/mman/shm_open.c", + "musl/src/mq/mq_close.c", + "musl/src/mq/mq_getattr.c", + "musl/src/mq/mq_notify.c", + "musl/src/mq/mq_open.c", + "musl/src/mq/mq_receive.c", + "musl/src/mq/mq_send.c", + "musl/src/mq/mq_setattr.c", + "musl/src/mq/mq_timedreceive.c", + "musl/src/mq/mq_timedsend.c", + "musl/src/mq/mq_unlink.c", + "musl/src/multibyte/btowc.c", + "musl/src/multibyte/c16rtomb.c", + "musl/src/multibyte/c32rtomb.c", + "musl/src/multibyte/internal.c", + "musl/src/multibyte/mblen.c", + "musl/src/multibyte/mbrlen.c", + "musl/src/multibyte/mbrtoc16.c", + "musl/src/multibyte/mbrtoc32.c", + "musl/src/multibyte/mbrtowc.c", + "musl/src/multibyte/mbsinit.c", + "musl/src/multibyte/mbsnrtowcs.c", + "musl/src/multibyte/mbsrtowcs.c", + "musl/src/multibyte/mbstowcs.c", + "musl/src/multibyte/mbtowc.c", + "musl/src/multibyte/wcrtomb.c", + "musl/src/multibyte/wcsnrtombs.c", + "musl/src/multibyte/wcsrtombs.c", + "musl/src/multibyte/wcstombs.c", + "musl/src/multibyte/wctob.c", + "musl/src/multibyte/wctomb.c", + "musl/src/network/accept.c", + "musl/src/network/accept4.c", + "musl/src/network/bind.c", + "musl/src/network/connect.c", + "musl/src/network/dn_comp.c", + "musl/src/network/dn_expand.c", + "musl/src/network/dn_skipname.c", + "musl/src/network/dns_parse.c", + "musl/src/network/ent.c", + "musl/src/network/ether.c", + "musl/src/network/freeaddrinfo.c", + "musl/src/network/gai_strerror.c", + "musl/src/network/getaddrinfo.c", + "musl/src/network/gethostbyaddr.c", + "musl/src/network/gethostbyaddr_r.c", + "musl/src/network/gethostbyname.c", + "musl/src/network/gethostbyname2.c", + "musl/src/network/gethostbyname2_r.c", + "musl/src/network/gethostbyname_r.c", + "musl/src/network/getifaddrs.c", + "musl/src/network/getnameinfo.c", + "musl/src/network/getpeername.c", + "musl/src/network/getservbyname.c", + "musl/src/network/getservbyname_r.c", + "musl/src/network/getservbyport.c", + "musl/src/network/getservbyport_r.c", + "musl/src/network/getsockname.c", + "musl/src/network/getsockopt.c", + "musl/src/network/h_errno.c", + "musl/src/network/herror.c", + "musl/src/network/hstrerror.c", + "musl/src/network/htonl.c", + "musl/src/network/htons.c", + "musl/src/network/if_freenameindex.c", + "musl/src/network/if_indextoname.c", + "musl/src/network/if_nameindex.c", + "musl/src/network/if_nametoindex.c", + "musl/src/network/in6addr_any.c", + "musl/src/network/in6addr_loopback.c", + "musl/src/network/inet_addr.c", + "musl/src/network/inet_aton.c", + "musl/src/network/inet_legacy.c", + "musl/src/network/inet_ntoa.c", + "musl/src/network/inet_ntop.c", + "musl/src/network/inet_pton.c", + "musl/src/network/listen.c", + "musl/src/network/lookup_ipliteral.c", + "musl/src/network/lookup_name.c", + "musl/src/network/lookup_serv.c", + "musl/src/network/netlink.c", + "musl/src/network/netname.c", + "musl/src/network/ns_parse.c", + "musl/src/network/ntohl.c", + "musl/src/network/ntohs.c", + "musl/src/network/proto.c", + "musl/src/network/recv.c", + "musl/src/network/recvfrom.c", + "musl/src/network/recvmmsg.c", + "musl/src/network/recvmsg.c", + "musl/src/network/res_init.c", + "musl/src/network/res_mkquery.c", + "musl/src/network/res_msend.c", + "musl/src/network/res_query.c", + "musl/src/network/res_querydomain.c", + "musl/src/network/res_send.c", + "musl/src/network/res_state.c", + "musl/src/network/resolvconf.c", + "musl/src/network/send.c", + "musl/src/network/sendmmsg.c", + "musl/src/network/sendmsg.c", + "musl/src/network/sendto.c", + "musl/src/network/serv.c", + "musl/src/network/setsockopt.c", + "musl/src/network/shutdown.c", + "musl/src/network/sockatmark.c", + "musl/src/network/socket.c", + "musl/src/network/socketpair.c", + "musl/src/passwd/fgetgrent.c", + "musl/src/passwd/fgetpwent.c", + "musl/src/passwd/fgetspent.c", + "musl/src/passwd/getgr_a.c", + "musl/src/passwd/getgr_r.c", + "musl/src/passwd/getgrent.c", + "musl/src/passwd/getgrent_a.c", + "musl/src/passwd/getgrouplist.c", + "musl/src/passwd/getpw_a.c", + "musl/src/passwd/getpw_r.c", + "musl/src/passwd/getpwent.c", + "musl/src/passwd/getpwent_a.c", + "musl/src/passwd/getspent.c", + "musl/src/passwd/getspnam.c", + "musl/src/passwd/getspnam_r.c", + "musl/src/passwd/lckpwdf.c", + "musl/src/passwd/nscd_query.c", + "musl/src/passwd/putgrent.c", + "musl/src/passwd/putpwent.c", + "musl/src/passwd/putspent.c", + "musl/src/prng/__rand48_step.c", + "musl/src/prng/__seed48.c", + "musl/src/prng/drand48.c", + "musl/src/prng/lcong48.c", + "musl/src/prng/lrand48.c", + "musl/src/prng/mrand48.c", + "musl/src/prng/rand.c", + "musl/src/prng/rand_r.c", + "musl/src/prng/random.c", + "musl/src/prng/seed48.c", + "musl/src/prng/srand48.c", + "musl/src/process/arm/vfork.s", + "musl/src/process/execl.c", + "musl/src/process/execle.c", + "musl/src/process/execlp.c", + "musl/src/process/execv.c", + "musl/src/process/execve.c", + "musl/src/process/execvp.c", + "musl/src/process/fexecve.c", + "musl/src/process/fork.c", + "musl/src/process/i386/vfork.s", + "musl/src/process/posix_spawn.c", + "musl/src/process/posix_spawn_file_actions_addchdir.c", + "musl/src/process/posix_spawn_file_actions_addclose.c", + "musl/src/process/posix_spawn_file_actions_adddup2.c", + "musl/src/process/posix_spawn_file_actions_addfchdir.c", + "musl/src/process/posix_spawn_file_actions_addopen.c", + "musl/src/process/posix_spawn_file_actions_destroy.c", + "musl/src/process/posix_spawn_file_actions_init.c", + "musl/src/process/posix_spawnattr_destroy.c", + "musl/src/process/posix_spawnattr_getflags.c", + "musl/src/process/posix_spawnattr_getpgroup.c", + "musl/src/process/posix_spawnattr_getsigdefault.c", + "musl/src/process/posix_spawnattr_getsigmask.c", + "musl/src/process/posix_spawnattr_init.c", + "musl/src/process/posix_spawnattr_sched.c", + "musl/src/process/posix_spawnattr_setflags.c", + "musl/src/process/posix_spawnattr_setpgroup.c", + "musl/src/process/posix_spawnattr_setsigdefault.c", + "musl/src/process/posix_spawnattr_setsigmask.c", + "musl/src/process/posix_spawnp.c", + "musl/src/process/s390x/vfork.s", + "musl/src/process/sh/vfork.s", + "musl/src/process/system.c", + "musl/src/process/vfork.c", + "musl/src/process/wait.c", + "musl/src/process/waitid.c", + "musl/src/process/waitpid.c", + "musl/src/process/x32/vfork.s", + "musl/src/process/x86_64/vfork.s", + "musl/src/regex/fnmatch.c", + "musl/src/regex/glob.c", + "musl/src/regex/regcomp.c", + "musl/src/regex/regerror.c", + "musl/src/regex/regexec.c", + "musl/src/regex/tre-mem.c", + "musl/src/sched/affinity.c", + "musl/src/sched/sched_cpucount.c", + "musl/src/sched/sched_get_priority_max.c", + "musl/src/sched/sched_getcpu.c", + "musl/src/sched/sched_getparam.c", + "musl/src/sched/sched_getscheduler.c", + "musl/src/sched/sched_rr_get_interval.c", + "musl/src/sched/sched_setparam.c", + "musl/src/sched/sched_setscheduler.c", + "musl/src/sched/sched_yield.c", + "musl/src/search/hsearch.c", + "musl/src/search/insque.c", + "musl/src/search/lsearch.c", + "musl/src/search/tdelete.c", + "musl/src/search/tdestroy.c", + "musl/src/search/tfind.c", + "musl/src/search/tsearch.c", + "musl/src/search/twalk.c", + "musl/src/select/poll.c", + "musl/src/select/pselect.c", + "musl/src/select/select.c", + "musl/src/setjmp/aarch64/longjmp.s", + "musl/src/setjmp/aarch64/setjmp.s", + "musl/src/setjmp/arm/longjmp.S", + "musl/src/setjmp/arm/setjmp.S", + "musl/src/setjmp/i386/longjmp.s", + "musl/src/setjmp/i386/setjmp.s", + "musl/src/setjmp/longjmp.c", + "musl/src/setjmp/m68k/longjmp.s", + "musl/src/setjmp/m68k/setjmp.s", + "musl/src/setjmp/microblaze/longjmp.s", + "musl/src/setjmp/microblaze/setjmp.s", + "musl/src/setjmp/mips/longjmp.S", + "musl/src/setjmp/mips/setjmp.S", + "musl/src/setjmp/mips64/longjmp.S", + "musl/src/setjmp/mips64/setjmp.S", + "musl/src/setjmp/mipsn32/longjmp.S", + "musl/src/setjmp/mipsn32/setjmp.S", + "musl/src/setjmp/or1k/longjmp.s", + "musl/src/setjmp/or1k/setjmp.s", + "musl/src/setjmp/powerpc/longjmp.S", + "musl/src/setjmp/powerpc/setjmp.S", + "musl/src/setjmp/powerpc64/longjmp.s", + "musl/src/setjmp/powerpc64/setjmp.s", + "musl/src/setjmp/riscv64/longjmp.S", + "musl/src/setjmp/riscv64/setjmp.S", + "musl/src/setjmp/s390x/longjmp.s", + "musl/src/setjmp/s390x/setjmp.s", + "musl/src/setjmp/setjmp.c", + "musl/src/setjmp/sh/longjmp.S", + "musl/src/setjmp/sh/setjmp.S", + "musl/src/setjmp/x32/longjmp.s", + "musl/src/setjmp/x32/setjmp.s", + "musl/src/setjmp/x86_64/longjmp.s", + "musl/src/setjmp/x86_64/setjmp.s", + "musl/src/signal/aarch64/restore.s", + "musl/src/signal/aarch64/sigsetjmp.s", + "musl/src/signal/arm/restore.s", + "musl/src/signal/arm/sigsetjmp.s", + "musl/src/signal/block.c", + "musl/src/signal/getitimer.c", + "musl/src/signal/i386/restore.s", + "musl/src/signal/i386/sigsetjmp.s", + "musl/src/signal/kill.c", + "musl/src/signal/killpg.c", + "musl/src/signal/m68k/sigsetjmp.s", + "musl/src/signal/microblaze/restore.s", + "musl/src/signal/microblaze/sigsetjmp.s", + "musl/src/signal/mips/restore.s", + "musl/src/signal/mips/sigsetjmp.s", + "musl/src/signal/mips64/restore.s", + "musl/src/signal/mips64/sigsetjmp.s", + "musl/src/signal/mipsn32/restore.s", + "musl/src/signal/mipsn32/sigsetjmp.s", + "musl/src/signal/or1k/sigsetjmp.s", + "musl/src/signal/powerpc/restore.s", + "musl/src/signal/powerpc/sigsetjmp.s", + "musl/src/signal/powerpc64/restore.s", + "musl/src/signal/powerpc64/sigsetjmp.s", + "musl/src/signal/psiginfo.c", + "musl/src/signal/psignal.c", + "musl/src/signal/raise.c", + "musl/src/signal/restore.c", + "musl/src/signal/riscv64/restore.s", + "musl/src/signal/riscv64/sigsetjmp.s", + "musl/src/signal/s390x/restore.s", + "musl/src/signal/s390x/sigsetjmp.s", + "musl/src/signal/setitimer.c", + "musl/src/signal/sh/restore.s", + "musl/src/signal/sh/sigsetjmp.s", + "musl/src/signal/sigaction.c", + "musl/src/signal/sigaddset.c", + "musl/src/signal/sigaltstack.c", + "musl/src/signal/sigandset.c", + "musl/src/signal/sigdelset.c", + "musl/src/signal/sigemptyset.c", + "musl/src/signal/sigfillset.c", + "musl/src/signal/sighold.c", + "musl/src/signal/sigignore.c", + "musl/src/signal/siginterrupt.c", + "musl/src/signal/sigisemptyset.c", + "musl/src/signal/sigismember.c", + "musl/src/signal/siglongjmp.c", + "musl/src/signal/signal.c", + "musl/src/signal/sigorset.c", + "musl/src/signal/sigpause.c", + "musl/src/signal/sigpending.c", + "musl/src/signal/sigprocmask.c", + "musl/src/signal/sigqueue.c", + "musl/src/signal/sigrelse.c", + "musl/src/signal/sigrtmax.c", + "musl/src/signal/sigrtmin.c", + "musl/src/signal/sigset.c", + "musl/src/signal/sigsetjmp.c", + "musl/src/signal/sigsetjmp_tail.c", + "musl/src/signal/sigsuspend.c", + "musl/src/signal/sigtimedwait.c", + "musl/src/signal/sigwait.c", + "musl/src/signal/sigwaitinfo.c", + "musl/src/signal/x32/getitimer.c", + "musl/src/signal/x32/restore.s", + "musl/src/signal/x32/setitimer.c", + "musl/src/signal/x32/sigsetjmp.s", + "musl/src/signal/x86_64/restore.s", + "musl/src/signal/x86_64/sigsetjmp.s", + "musl/src/stat/__xstat.c", + "musl/src/stat/chmod.c", + "musl/src/stat/fchmod.c", + "musl/src/stat/fchmodat.c", + "musl/src/stat/fstat.c", + "musl/src/stat/fstatat.c", + "musl/src/stat/futimens.c", + "musl/src/stat/futimesat.c", + "musl/src/stat/lchmod.c", + "musl/src/stat/lstat.c", + "musl/src/stat/mkdir.c", + "musl/src/stat/mkdirat.c", + "musl/src/stat/mkfifo.c", + "musl/src/stat/mkfifoat.c", + "musl/src/stat/mknod.c", + "musl/src/stat/mknodat.c", + "musl/src/stat/stat.c", + "musl/src/stat/statvfs.c", + "musl/src/stat/umask.c", + "musl/src/stat/utimensat.c", + "musl/src/stdio/__fclose_ca.c", + "musl/src/stdio/__fdopen.c", + "musl/src/stdio/__fmodeflags.c", + "musl/src/stdio/__fopen_rb_ca.c", + "musl/src/stdio/__lockfile.c", + "musl/src/stdio/__overflow.c", + "musl/src/stdio/__stdio_close.c", + "musl/src/stdio/__stdio_exit.c", + "musl/src/stdio/__stdio_read.c", + "musl/src/stdio/__stdio_seek.c", + "musl/src/stdio/__stdio_write.c", + "musl/src/stdio/__stdout_write.c", + "musl/src/stdio/__string_read.c", + "musl/src/stdio/__toread.c", + "musl/src/stdio/__towrite.c", + "musl/src/stdio/__uflow.c", + "musl/src/stdio/asprintf.c", + "musl/src/stdio/clearerr.c", + "musl/src/stdio/dprintf.c", + "musl/src/stdio/ext.c", + "musl/src/stdio/ext2.c", + "musl/src/stdio/fclose.c", + "musl/src/stdio/feof.c", + "musl/src/stdio/ferror.c", + "musl/src/stdio/fflush.c", + "musl/src/stdio/fgetc.c", + "musl/src/stdio/fgetln.c", + "musl/src/stdio/fgetpos.c", + "musl/src/stdio/fgets.c", + "musl/src/stdio/fgetwc.c", + "musl/src/stdio/fgetws.c", + "musl/src/stdio/fileno.c", + "musl/src/stdio/flockfile.c", + "musl/src/stdio/fmemopen.c", + "musl/src/stdio/fopen.c", + "musl/src/stdio/fopencookie.c", + "musl/src/stdio/fprintf.c", + "musl/src/stdio/fputc.c", + "musl/src/stdio/fputs.c", + "musl/src/stdio/fputwc.c", + "musl/src/stdio/fputws.c", + "musl/src/stdio/fread.c", + "musl/src/stdio/freopen.c", + "musl/src/stdio/fscanf.c", + "musl/src/stdio/fseek.c", + "musl/src/stdio/fsetpos.c", + "musl/src/stdio/ftell.c", + "musl/src/stdio/ftrylockfile.c", + "musl/src/stdio/funlockfile.c", + "musl/src/stdio/fwide.c", + "musl/src/stdio/fwprintf.c", + "musl/src/stdio/fwrite.c", + "musl/src/stdio/fwscanf.c", + "musl/src/stdio/getc.c", + "musl/src/stdio/getc_unlocked.c", + "musl/src/stdio/getchar.c", + "musl/src/stdio/getchar_unlocked.c", + "musl/src/stdio/getdelim.c", + "musl/src/stdio/getline.c", + "musl/src/stdio/gets.c", + "musl/src/stdio/getw.c", + "musl/src/stdio/getwc.c", + "musl/src/stdio/getwchar.c", + "musl/src/stdio/ofl.c", + "musl/src/stdio/ofl_add.c", + "musl/src/stdio/open_memstream.c", + "musl/src/stdio/open_wmemstream.c", + "musl/src/stdio/pclose.c", + "musl/src/stdio/perror.c", + "musl/src/stdio/popen.c", + "musl/src/stdio/printf.c", + "musl/src/stdio/putc.c", + "musl/src/stdio/putc_unlocked.c", + "musl/src/stdio/putchar.c", + "musl/src/stdio/putchar_unlocked.c", + "musl/src/stdio/puts.c", + "musl/src/stdio/putw.c", + "musl/src/stdio/putwc.c", + "musl/src/stdio/putwchar.c", + "musl/src/stdio/remove.c", + "musl/src/stdio/rename.c", + "musl/src/stdio/rewind.c", + "musl/src/stdio/scanf.c", + "musl/src/stdio/setbuf.c", + "musl/src/stdio/setbuffer.c", + "musl/src/stdio/setlinebuf.c", + "musl/src/stdio/setvbuf.c", + "musl/src/stdio/snprintf.c", + "musl/src/stdio/sprintf.c", + "musl/src/stdio/sscanf.c", + "musl/src/stdio/stderr.c", + "musl/src/stdio/stdin.c", + "musl/src/stdio/stdout.c", + "musl/src/stdio/swprintf.c", + "musl/src/stdio/swscanf.c", + "musl/src/stdio/tempnam.c", + "musl/src/stdio/tmpfile.c", + "musl/src/stdio/tmpnam.c", + "musl/src/stdio/ungetc.c", + "musl/src/stdio/ungetwc.c", + "musl/src/stdio/vasprintf.c", + "musl/src/stdio/vdprintf.c", + "musl/src/stdio/vfprintf.c", + "musl/src/stdio/vfscanf.c", + "musl/src/stdio/vfwprintf.c", + "musl/src/stdio/vfwscanf.c", + "musl/src/stdio/vprintf.c", + "musl/src/stdio/vscanf.c", + "musl/src/stdio/vsnprintf.c", + "musl/src/stdio/vsprintf.c", + "musl/src/stdio/vsscanf.c", + "musl/src/stdio/vswprintf.c", + "musl/src/stdio/vswscanf.c", + "musl/src/stdio/vwprintf.c", + "musl/src/stdio/vwscanf.c", + "musl/src/stdio/wprintf.c", + "musl/src/stdio/wscanf.c", + "musl/src/stdlib/abs.c", + "musl/src/stdlib/atof.c", + "musl/src/stdlib/atoi.c", + "musl/src/stdlib/atol.c", + "musl/src/stdlib/atoll.c", + "musl/src/stdlib/bsearch.c", + "musl/src/stdlib/div.c", + "musl/src/stdlib/ecvt.c", + "musl/src/stdlib/fcvt.c", + "musl/src/stdlib/gcvt.c", + "musl/src/stdlib/imaxabs.c", + "musl/src/stdlib/imaxdiv.c", + "musl/src/stdlib/labs.c", + "musl/src/stdlib/ldiv.c", + "musl/src/stdlib/llabs.c", + "musl/src/stdlib/lldiv.c", + "musl/src/stdlib/qsort.c", + "musl/src/stdlib/strtod.c", + "musl/src/stdlib/strtol.c", + "musl/src/stdlib/wcstod.c", + "musl/src/stdlib/wcstol.c", + "musl/src/string/arm/__aeabi_memcpy.s", + "musl/src/string/arm/__aeabi_memset.s", + "musl/src/string/arm/memcpy.c", + "musl/src/string/arm/memcpy_le.S", + "musl/src/string/bcmp.c", + "musl/src/string/bcopy.c", + "musl/src/string/bzero.c", + "musl/src/string/explicit_bzero.c", + "musl/src/string/i386/memcpy.s", + "musl/src/string/i386/memmove.s", + "musl/src/string/i386/memset.s", + "musl/src/string/index.c", + "musl/src/string/memccpy.c", + "musl/src/string/memchr.c", + "musl/src/string/memcmp.c", + "musl/src/string/memcpy.c", + "musl/src/string/memmem.c", + "musl/src/string/memmove.c", + "musl/src/string/mempcpy.c", + "musl/src/string/memrchr.c", + "musl/src/string/memset.c", + "musl/src/string/rindex.c", + "musl/src/string/stpcpy.c", + "musl/src/string/stpncpy.c", + "musl/src/string/strcasecmp.c", + "musl/src/string/strcasestr.c", + "musl/src/string/strcat.c", + "musl/src/string/strchr.c", + "musl/src/string/strchrnul.c", + "musl/src/string/strcmp.c", + "musl/src/string/strcpy.c", + "musl/src/string/strcspn.c", + "musl/src/string/strdup.c", + "musl/src/string/strerror_r.c", + "musl/src/string/strlcat.c", + "musl/src/string/strlcpy.c", + "musl/src/string/strlen.c", + "musl/src/string/strncasecmp.c", + "musl/src/string/strncat.c", + "musl/src/string/strncmp.c", + "musl/src/string/strncpy.c", + "musl/src/string/strndup.c", + "musl/src/string/strnlen.c", + "musl/src/string/strpbrk.c", + "musl/src/string/strrchr.c", + "musl/src/string/strsep.c", + "musl/src/string/strsignal.c", + "musl/src/string/strspn.c", + "musl/src/string/strstr.c", + "musl/src/string/strtok.c", + "musl/src/string/strtok_r.c", + "musl/src/string/strverscmp.c", + "musl/src/string/swab.c", + "musl/src/string/wcpcpy.c", + "musl/src/string/wcpncpy.c", + "musl/src/string/wcscasecmp.c", + "musl/src/string/wcscasecmp_l.c", + "musl/src/string/wcscat.c", + "musl/src/string/wcschr.c", + "musl/src/string/wcscmp.c", + "musl/src/string/wcscpy.c", + "musl/src/string/wcscspn.c", + "musl/src/string/wcsdup.c", + "musl/src/string/wcslen.c", + "musl/src/string/wcsncasecmp.c", + "musl/src/string/wcsncasecmp_l.c", + "musl/src/string/wcsncat.c", + "musl/src/string/wcsncmp.c", + "musl/src/string/wcsncpy.c", + "musl/src/string/wcsnlen.c", + "musl/src/string/wcspbrk.c", + "musl/src/string/wcsrchr.c", + "musl/src/string/wcsspn.c", + "musl/src/string/wcsstr.c", + "musl/src/string/wcstok.c", + "musl/src/string/wcswcs.c", + "musl/src/string/wmemchr.c", + "musl/src/string/wmemcmp.c", + "musl/src/string/wmemcpy.c", + "musl/src/string/wmemmove.c", + "musl/src/string/wmemset.c", + "musl/src/string/x86_64/memcpy.s", + "musl/src/string/x86_64/memmove.s", + "musl/src/string/x86_64/memset.s", + "musl/src/temp/__randname.c", + "musl/src/temp/mkdtemp.c", + "musl/src/temp/mkostemp.c", + "musl/src/temp/mkostemps.c", + "musl/src/temp/mkstemp.c", + "musl/src/temp/mkstemps.c", + "musl/src/temp/mktemp.c", + "musl/src/termios/cfgetospeed.c", + "musl/src/termios/cfmakeraw.c", + "musl/src/termios/cfsetospeed.c", + "musl/src/termios/tcdrain.c", + "musl/src/termios/tcflow.c", + "musl/src/termios/tcflush.c", + "musl/src/termios/tcgetattr.c", + "musl/src/termios/tcgetsid.c", + "musl/src/termios/tcsendbreak.c", + "musl/src/termios/tcsetattr.c", + "musl/src/thread/__lock.c", + "musl/src/thread/__set_thread_area.c", + "musl/src/thread/__syscall_cp.c", + "musl/src/thread/__timedwait.c", + "musl/src/thread/__tls_get_addr.c", + "musl/src/thread/__unmapself.c", + "musl/src/thread/__wait.c", + "musl/src/thread/aarch64/__set_thread_area.s", + "musl/src/thread/aarch64/__unmapself.s", + "musl/src/thread/aarch64/clone.s", + "musl/src/thread/aarch64/syscall_cp.s", + "musl/src/thread/arm/__aeabi_read_tp.s", + "musl/src/thread/arm/__set_thread_area.c", + "musl/src/thread/arm/__unmapself.s", + "musl/src/thread/arm/atomics.s", + "musl/src/thread/arm/clone.s", + "musl/src/thread/arm/syscall_cp.s", + "musl/src/thread/call_once.c", + "musl/src/thread/clone.c", + "musl/src/thread/cnd_broadcast.c", + "musl/src/thread/cnd_destroy.c", + "musl/src/thread/cnd_init.c", + "musl/src/thread/cnd_signal.c", + "musl/src/thread/cnd_timedwait.c", + "musl/src/thread/cnd_wait.c", + "musl/src/thread/default_attr.c", + "musl/src/thread/i386/__set_thread_area.s", + "musl/src/thread/i386/__unmapself.s", + "musl/src/thread/i386/clone.s", + "musl/src/thread/i386/syscall_cp.s", + "musl/src/thread/i386/tls.s", + "musl/src/thread/lock_ptc.c", + "musl/src/thread/m68k/__m68k_read_tp.s", + "musl/src/thread/m68k/clone.s", + "musl/src/thread/m68k/syscall_cp.s", + "musl/src/thread/microblaze/__set_thread_area.s", + "musl/src/thread/microblaze/__unmapself.s", + "musl/src/thread/microblaze/clone.s", + "musl/src/thread/microblaze/syscall_cp.s", + "musl/src/thread/mips/__unmapself.s", + "musl/src/thread/mips/clone.s", + "musl/src/thread/mips/syscall_cp.s", + "musl/src/thread/mips64/__unmapself.s", + "musl/src/thread/mips64/clone.s", + "musl/src/thread/mips64/syscall_cp.s", + "musl/src/thread/mipsn32/__unmapself.s", + "musl/src/thread/mipsn32/clone.s", + "musl/src/thread/mipsn32/syscall_cp.s", + "musl/src/thread/mtx_destroy.c", + "musl/src/thread/mtx_init.c", + "musl/src/thread/mtx_lock.c", + "musl/src/thread/mtx_timedlock.c", + "musl/src/thread/mtx_trylock.c", + "musl/src/thread/mtx_unlock.c", + "musl/src/thread/or1k/__set_thread_area.s", + "musl/src/thread/or1k/__unmapself.s", + "musl/src/thread/or1k/clone.s", + "musl/src/thread/or1k/syscall_cp.s", + "musl/src/thread/powerpc/__set_thread_area.s", + "musl/src/thread/powerpc/__unmapself.s", + "musl/src/thread/powerpc/clone.s", + "musl/src/thread/powerpc/syscall_cp.s", + "musl/src/thread/powerpc64/__set_thread_area.s", + "musl/src/thread/powerpc64/__unmapself.s", + "musl/src/thread/powerpc64/clone.s", + "musl/src/thread/powerpc64/syscall_cp.s", + "musl/src/thread/pthread_atfork.c", + "musl/src/thread/pthread_attr_destroy.c", + "musl/src/thread/pthread_attr_get.c", + "musl/src/thread/pthread_attr_init.c", + "musl/src/thread/pthread_attr_setdetachstate.c", + "musl/src/thread/pthread_attr_setguardsize.c", + "musl/src/thread/pthread_attr_setinheritsched.c", + "musl/src/thread/pthread_attr_setschedparam.c", + "musl/src/thread/pthread_attr_setschedpolicy.c", + "musl/src/thread/pthread_attr_setscope.c", + "musl/src/thread/pthread_attr_setstack.c", + "musl/src/thread/pthread_attr_setstacksize.c", + "musl/src/thread/pthread_barrier_destroy.c", + "musl/src/thread/pthread_barrier_init.c", + "musl/src/thread/pthread_barrier_wait.c", + "musl/src/thread/pthread_barrierattr_destroy.c", + "musl/src/thread/pthread_barrierattr_init.c", + "musl/src/thread/pthread_barrierattr_setpshared.c", + "musl/src/thread/pthread_cancel.c", + "musl/src/thread/pthread_cleanup_push.c", + "musl/src/thread/pthread_cond_broadcast.c", + "musl/src/thread/pthread_cond_destroy.c", + "musl/src/thread/pthread_cond_init.c", + "musl/src/thread/pthread_cond_signal.c", + "musl/src/thread/pthread_cond_timedwait.c", + "musl/src/thread/pthread_cond_wait.c", + "musl/src/thread/pthread_condattr_destroy.c", + "musl/src/thread/pthread_condattr_init.c", + "musl/src/thread/pthread_condattr_setclock.c", + "musl/src/thread/pthread_condattr_setpshared.c", + "musl/src/thread/pthread_create.c", + "musl/src/thread/pthread_detach.c", + "musl/src/thread/pthread_equal.c", + "musl/src/thread/pthread_getattr_np.c", + "musl/src/thread/pthread_getconcurrency.c", + "musl/src/thread/pthread_getcpuclockid.c", + "musl/src/thread/pthread_getschedparam.c", + "musl/src/thread/pthread_getspecific.c", + "musl/src/thread/pthread_join.c", + "musl/src/thread/pthread_key_create.c", + "musl/src/thread/pthread_kill.c", + "musl/src/thread/pthread_mutex_consistent.c", + "musl/src/thread/pthread_mutex_destroy.c", + "musl/src/thread/pthread_mutex_getprioceiling.c", + "musl/src/thread/pthread_mutex_init.c", + "musl/src/thread/pthread_mutex_lock.c", + "musl/src/thread/pthread_mutex_setprioceiling.c", + "musl/src/thread/pthread_mutex_timedlock.c", + "musl/src/thread/pthread_mutex_trylock.c", + "musl/src/thread/pthread_mutex_unlock.c", + "musl/src/thread/pthread_mutexattr_destroy.c", + "musl/src/thread/pthread_mutexattr_init.c", + "musl/src/thread/pthread_mutexattr_setprotocol.c", + "musl/src/thread/pthread_mutexattr_setpshared.c", + "musl/src/thread/pthread_mutexattr_setrobust.c", + "musl/src/thread/pthread_mutexattr_settype.c", + "musl/src/thread/pthread_once.c", + "musl/src/thread/pthread_rwlock_destroy.c", + "musl/src/thread/pthread_rwlock_init.c", + "musl/src/thread/pthread_rwlock_rdlock.c", + "musl/src/thread/pthread_rwlock_timedrdlock.c", + "musl/src/thread/pthread_rwlock_timedwrlock.c", + "musl/src/thread/pthread_rwlock_tryrdlock.c", + "musl/src/thread/pthread_rwlock_trywrlock.c", + "musl/src/thread/pthread_rwlock_unlock.c", + "musl/src/thread/pthread_rwlock_wrlock.c", + "musl/src/thread/pthread_rwlockattr_destroy.c", + "musl/src/thread/pthread_rwlockattr_init.c", + "musl/src/thread/pthread_rwlockattr_setpshared.c", + "musl/src/thread/pthread_self.c", + "musl/src/thread/pthread_setattr_default_np.c", + "musl/src/thread/pthread_setcancelstate.c", + "musl/src/thread/pthread_setcanceltype.c", + "musl/src/thread/pthread_setconcurrency.c", + "musl/src/thread/pthread_setname_np.c", + "musl/src/thread/pthread_setschedparam.c", + "musl/src/thread/pthread_setschedprio.c", + "musl/src/thread/pthread_setspecific.c", + "musl/src/thread/pthread_sigmask.c", + "musl/src/thread/pthread_spin_destroy.c", + "musl/src/thread/pthread_spin_init.c", + "musl/src/thread/pthread_spin_lock.c", + "musl/src/thread/pthread_spin_trylock.c", + "musl/src/thread/pthread_spin_unlock.c", + "musl/src/thread/pthread_testcancel.c", + "musl/src/thread/riscv64/__set_thread_area.s", + "musl/src/thread/riscv64/__unmapself.s", + "musl/src/thread/riscv64/clone.s", + "musl/src/thread/riscv64/syscall_cp.s", + "musl/src/thread/s390x/__set_thread_area.s", + "musl/src/thread/s390x/__tls_get_offset.s", + "musl/src/thread/s390x/__unmapself.s", + "musl/src/thread/s390x/clone.s", + "musl/src/thread/s390x/syscall_cp.s", + "musl/src/thread/sem_destroy.c", + "musl/src/thread/sem_getvalue.c", + "musl/src/thread/sem_init.c", + "musl/src/thread/sem_open.c", + "musl/src/thread/sem_post.c", + "musl/src/thread/sem_timedwait.c", + "musl/src/thread/sem_trywait.c", + "musl/src/thread/sem_unlink.c", + "musl/src/thread/sem_wait.c", + "musl/src/thread/sh/__set_thread_area.c", + "musl/src/thread/sh/__unmapself.c", + "musl/src/thread/sh/__unmapself_mmu.s", + "musl/src/thread/sh/atomics.s", + "musl/src/thread/sh/clone.s", + "musl/src/thread/sh/syscall_cp.s", + "musl/src/thread/synccall.c", + "musl/src/thread/syscall_cp.c", + "musl/src/thread/thrd_create.c", + "musl/src/thread/thrd_exit.c", + "musl/src/thread/thrd_join.c", + "musl/src/thread/thrd_sleep.c", + "musl/src/thread/thrd_yield.c", + "musl/src/thread/tls.c", + "musl/src/thread/tss_create.c", + "musl/src/thread/tss_delete.c", + "musl/src/thread/tss_set.c", + "musl/src/thread/vmlock.c", + "musl/src/thread/x32/__set_thread_area.s", + "musl/src/thread/x32/__unmapself.s", + "musl/src/thread/x32/clone.s", + "musl/src/thread/x32/syscall_cp.s", + "musl/src/thread/x86_64/__set_thread_area.s", + "musl/src/thread/x86_64/__unmapself.s", + "musl/src/thread/x86_64/clone.s", + "musl/src/thread/x86_64/syscall_cp.s", + "musl/src/time/__map_file.c", + "musl/src/time/__month_to_secs.c", + "musl/src/time/__secs_to_tm.c", + "musl/src/time/__tm_to_secs.c", + "musl/src/time/__tz.c", + "musl/src/time/__year_to_secs.c", + "musl/src/time/asctime.c", + "musl/src/time/asctime_r.c", + "musl/src/time/clock.c", + "musl/src/time/clock_getcpuclockid.c", + "musl/src/time/clock_getres.c", + "musl/src/time/clock_gettime.c", + "musl/src/time/clock_nanosleep.c", + "musl/src/time/clock_settime.c", + "musl/src/time/ctime.c", + "musl/src/time/ctime_r.c", + "musl/src/time/difftime.c", + "musl/src/time/ftime.c", + "musl/src/time/getdate.c", + "musl/src/time/gettimeofday.c", + "musl/src/time/gmtime.c", + "musl/src/time/gmtime_r.c", + "musl/src/time/localtime.c", + "musl/src/time/localtime_r.c", + "musl/src/time/mktime.c", + "musl/src/time/nanosleep.c", + "musl/src/time/strftime.c", + "musl/src/time/strptime.c", + "musl/src/time/time.c", + "musl/src/time/timegm.c", + "musl/src/time/timer_create.c", + "musl/src/time/timer_delete.c", + "musl/src/time/timer_getoverrun.c", + "musl/src/time/timer_gettime.c", + "musl/src/time/timer_settime.c", + "musl/src/time/times.c", + "musl/src/time/timespec_get.c", + "musl/src/time/utime.c", + "musl/src/time/wcsftime.c", + "musl/src/unistd/_exit.c", + "musl/src/unistd/access.c", + "musl/src/unistd/acct.c", + "musl/src/unistd/alarm.c", + "musl/src/unistd/chdir.c", + "musl/src/unistd/chown.c", + "musl/src/unistd/close.c", + "musl/src/unistd/ctermid.c", + "musl/src/unistd/dup.c", + "musl/src/unistd/dup2.c", + "musl/src/unistd/dup3.c", + "musl/src/unistd/faccessat.c", + "musl/src/unistd/fchdir.c", + "musl/src/unistd/fchown.c", + "musl/src/unistd/fchownat.c", + "musl/src/unistd/fdatasync.c", + "musl/src/unistd/fsync.c", + "musl/src/unistd/ftruncate.c", + "musl/src/unistd/getcwd.c", + "musl/src/unistd/getegid.c", + "musl/src/unistd/geteuid.c", + "musl/src/unistd/getgid.c", + "musl/src/unistd/getgroups.c", + "musl/src/unistd/gethostname.c", + "musl/src/unistd/getlogin.c", + "musl/src/unistd/getlogin_r.c", + "musl/src/unistd/getpgid.c", + "musl/src/unistd/getpgrp.c", + "musl/src/unistd/getpid.c", + "musl/src/unistd/getppid.c", + "musl/src/unistd/getsid.c", + "musl/src/unistd/getuid.c", + "musl/src/unistd/isatty.c", + "musl/src/unistd/lchown.c", + "musl/src/unistd/link.c", + "musl/src/unistd/linkat.c", + "musl/src/unistd/lseek.c", + "musl/src/unistd/mips/pipe.s", + "musl/src/unistd/mips64/pipe.s", + "musl/src/unistd/mipsn32/lseek.c", + "musl/src/unistd/mipsn32/pipe.s", + "musl/src/unistd/nice.c", + "musl/src/unistd/pause.c", + "musl/src/unistd/pipe.c", + "musl/src/unistd/pipe2.c", + "musl/src/unistd/posix_close.c", + "musl/src/unistd/pread.c", + "musl/src/unistd/preadv.c", + "musl/src/unistd/pwrite.c", + "musl/src/unistd/pwritev.c", + "musl/src/unistd/read.c", + "musl/src/unistd/readlink.c", + "musl/src/unistd/readlinkat.c", + "musl/src/unistd/readv.c", + "musl/src/unistd/renameat.c", + "musl/src/unistd/rmdir.c", + "musl/src/unistd/setegid.c", + "musl/src/unistd/seteuid.c", + "musl/src/unistd/setgid.c", + "musl/src/unistd/setpgid.c", + "musl/src/unistd/setpgrp.c", + "musl/src/unistd/setregid.c", + "musl/src/unistd/setresgid.c", + "musl/src/unistd/setresuid.c", + "musl/src/unistd/setreuid.c", + "musl/src/unistd/setsid.c", + "musl/src/unistd/setuid.c", + "musl/src/unistd/setxid.c", + "musl/src/unistd/sh/pipe.s", + "musl/src/unistd/sleep.c", + "musl/src/unistd/symlink.c", + "musl/src/unistd/symlinkat.c", + "musl/src/unistd/sync.c", + "musl/src/unistd/tcgetpgrp.c", + "musl/src/unistd/tcsetpgrp.c", + "musl/src/unistd/truncate.c", + "musl/src/unistd/ttyname.c", + "musl/src/unistd/ttyname_r.c", + "musl/src/unistd/ualarm.c", + "musl/src/unistd/unlink.c", + "musl/src/unistd/unlinkat.c", + "musl/src/unistd/usleep.c", + "musl/src/unistd/write.c", + "musl/src/unistd/writev.c", + "musl/src/unistd/x32/lseek.c", +}; +const compat_time32_files = [_][]const u8{ + "musl/compat/time32/__xstat.c", + "musl/compat/time32/adjtime32.c", + "musl/compat/time32/adjtimex_time32.c", + "musl/compat/time32/aio_suspend_time32.c", + "musl/compat/time32/clock_adjtime32.c", + "musl/compat/time32/clock_getres_time32.c", + "musl/compat/time32/clock_gettime32.c", + "musl/compat/time32/clock_nanosleep_time32.c", + "musl/compat/time32/clock_settime32.c", + "musl/compat/time32/cnd_timedwait_time32.c", + "musl/compat/time32/ctime32.c", + "musl/compat/time32/ctime32_r.c", + "musl/compat/time32/difftime32.c", + "musl/compat/time32/fstat_time32.c", + "musl/compat/time32/fstatat_time32.c", + "musl/compat/time32/ftime32.c", + "musl/compat/time32/futimens_time32.c", + "musl/compat/time32/futimes_time32.c", + "musl/compat/time32/futimesat_time32.c", + "musl/compat/time32/getitimer_time32.c", + "musl/compat/time32/getrusage_time32.c", + "musl/compat/time32/gettimeofday_time32.c", + "musl/compat/time32/gmtime32.c", + "musl/compat/time32/gmtime32_r.c", + "musl/compat/time32/localtime32.c", + "musl/compat/time32/localtime32_r.c", + "musl/compat/time32/lstat_time32.c", + "musl/compat/time32/lutimes_time32.c", + "musl/compat/time32/mktime32.c", + "musl/compat/time32/mq_timedreceive_time32.c", + "musl/compat/time32/mq_timedsend_time32.c", + "musl/compat/time32/mtx_timedlock_time32.c", + "musl/compat/time32/nanosleep_time32.c", + "musl/compat/time32/ppoll_time32.c", + "musl/compat/time32/pselect_time32.c", + "musl/compat/time32/pthread_cond_timedwait_time32.c", + "musl/compat/time32/pthread_mutex_timedlock_time32.c", + "musl/compat/time32/pthread_rwlock_timedrdlock_time32.c", + "musl/compat/time32/pthread_rwlock_timedwrlock_time32.c", + "musl/compat/time32/pthread_timedjoin_np_time32.c", + "musl/compat/time32/recvmmsg_time32.c", + "musl/compat/time32/sched_rr_get_interval_time32.c", + "musl/compat/time32/select_time32.c", + "musl/compat/time32/sem_timedwait_time32.c", + "musl/compat/time32/semtimedop_time32.c", + "musl/compat/time32/setitimer_time32.c", + "musl/compat/time32/settimeofday_time32.c", + "musl/compat/time32/sigtimedwait_time32.c", + "musl/compat/time32/stat_time32.c", + "musl/compat/time32/stime32.c", + "musl/compat/time32/thrd_sleep_time32.c", + "musl/compat/time32/time32.c", + "musl/compat/time32/time32gm.c", + "musl/compat/time32/timer_gettime32.c", + "musl/compat/time32/timer_settime32.c", + "musl/compat/time32/timerfd_gettime32.c", + "musl/compat/time32/timerfd_settime32.c", + "musl/compat/time32/timespec_get_time32.c", + "musl/compat/time32/utime_time32.c", + "musl/compat/time32/utimensat_time32.c", + "musl/compat/time32/utimes_time32.c", + "musl/compat/time32/wait3_time32.c", + "musl/compat/time32/wait4_time32.c", +}; diff --git a/src/os.cpp b/src/os.cpp deleted file mode 100644 index 33d98fd41679c4f5cdd30e5e4b1941bb84e053f0..0000000000000000000000000000000000000000 --- a/src/os.cpp +++ /dev/null @@ -1,2345 +0,0 @@ -/* - * Copyright (c) 2015 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#include "os.hpp" -#include "buffer.hpp" -#include "heap.hpp" -#include "util.hpp" -#include "error.hpp" -#include "util_base.hpp" -#include -#include - -#if defined(_WIN32) - -#if !defined(NOMINMAX) -#define NOMINMAX -#endif - -#if !defined(VC_EXTRALEAN) -#define VC_EXTRALEAN -#endif - -#if !defined(WIN32_LEAN_AND_MEAN) -#define WIN32_LEAN_AND_MEAN -#endif - -#if !defined(_WIN32_WINNT) -#define _WIN32_WINNT 0x600 -#endif - -#if !defined(NTDDI_VERSION) -#define NTDDI_VERSION 0x06000000 -#endif - -#include -#include -#include -#include -#include - -#if defined(_MSC_VER) -typedef SSIZE_T ssize_t; -#endif -#else -#define ZIG_OS_POSIX - -#include -#include -#include -#include -#include -#include -#include -#include - -#endif - -#if defined(ZIG_OS_LINUX) || defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD) || defined(ZIG_OS_DRAGONFLY) -#include -#endif - -#if defined(ZIG_OS_LINUX) -#include -#endif - -#if defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD) || defined(ZIG_OS_DRAGONFLY) -#include -#endif - -#if defined(__MACH__) -#include -#include -#include -#endif - -#if defined(ZIG_OS_WINDOWS) -static void utf16le_ptr_to_utf8(Buf *out, WCHAR *utf16le); -static size_t utf8_to_utf16le(WCHAR *utf16_le, Slice utf8); -static uint64_t windows_perf_freq; -#elif defined(__MACH__) -static clock_serv_t macos_calendar_clock; -static clock_serv_t macos_monotonic_clock; -#endif - -#include -#include -#include - -#if !defined(environ) -extern char **environ; -#endif - -#if defined(ZIG_OS_POSIX) -static void populate_termination(Termination *term, int status) { - if (WIFEXITED(status)) { - term->how = TerminationIdClean; - term->code = WEXITSTATUS(status); - } else if (WIFSIGNALED(status)) { - term->how = TerminationIdSignaled; - term->code = WTERMSIG(status); - } else if (WIFSTOPPED(status)) { - term->how = TerminationIdStopped; - term->code = WSTOPSIG(status); - } else { - term->how = TerminationIdUnknown; - term->code = status; - } -} - -static void os_spawn_process_posix(ZigList &args, Termination *term) { - const char **argv = heap::c_allocator.allocate(args.length + 1); - for (size_t i = 0; i < args.length; i += 1) { - argv[i] = args.at(i); - } - argv[args.length] = nullptr; - - pid_t pid; - int rc = posix_spawnp(&pid, args.at(0), nullptr, nullptr, const_cast(argv), environ); - if (rc != 0) { - zig_panic("unable to spawn %s: %s", args.at(0), strerror(rc)); - } - - int status; - waitpid(pid, &status, 0); - populate_termination(term, status); -} -#endif - -#if defined(ZIG_OS_WINDOWS) - -static void os_windows_create_command_line(Buf *command_line, ZigList &args) { - buf_resize(command_line, 0); - const char *prefix = "\""; - for (size_t arg_i = 0; arg_i < args.length; arg_i += 1) { - const char *arg = args.at(arg_i); - buf_append_str(command_line, prefix); - prefix = " \""; - size_t arg_len = strlen(arg); - for (size_t c_i = 0; c_i < arg_len; c_i += 1) { - if (arg[c_i] == '\"') { - zig_panic("TODO"); - } - buf_append_char(command_line, arg[c_i]); - } - buf_append_char(command_line, '\"'); - } -} - -static void os_spawn_process_windows(ZigList &args, Termination *term) { - Buf command_line = BUF_INIT; - os_windows_create_command_line(&command_line, args); - - PROCESS_INFORMATION piProcInfo = {0}; - STARTUPINFOW siStartInfo = {0}; - siStartInfo.cb = sizeof(STARTUPINFOW); - - Slice exe_slice = str(args.at(0)); - auto exe_utf16_slice = Slice::alloc(exe_slice.len + 1); - exe_utf16_slice.ptr[utf8_to_utf16le(exe_utf16_slice.ptr, exe_slice)] = 0; - - auto command_line_utf16 = Slice::alloc(buf_len(&command_line) + 1); - command_line_utf16.ptr[utf8_to_utf16le(command_line_utf16.ptr, buf_to_slice(&command_line))] = 0; - - BOOL success = CreateProcessW(exe_utf16_slice.ptr, command_line_utf16.ptr, nullptr, nullptr, TRUE, CREATE_UNICODE_ENVIRONMENT, nullptr, nullptr, - &siStartInfo, &piProcInfo); - - if (!success) { - zig_panic("CreateProcess failed. exe: %s command_line: %s", args.at(0), buf_ptr(&command_line)); - } - - WaitForSingleObject(piProcInfo.hProcess, INFINITE); - - DWORD exit_code; - if (!GetExitCodeProcess(piProcInfo.hProcess, &exit_code)) { - zig_panic("GetExitCodeProcess failed"); - } - term->how = TerminationIdClean; - term->code = exit_code; -} -#endif - -void os_spawn_process(ZigList &args, Termination *term) { -#if defined(ZIG_OS_WINDOWS) - os_spawn_process_windows(args, term); -#elif defined(ZIG_OS_POSIX) - os_spawn_process_posix(args, term); -#else -#error "missing os_spawn_process implementation" -#endif -} - -void os_path_dirname(Buf *full_path, Buf *out_dirname) { - return os_path_split(full_path, out_dirname, nullptr); -} - -bool os_is_sep(uint8_t c) { -#if defined(ZIG_OS_WINDOWS) - return c == '\\' || c == '/'; -#else - return c == '/'; -#endif -} - -void os_path_split(Buf *full_path, Buf *out_dirname, Buf *out_basename) { - size_t len = buf_len(full_path); - if (len != 0) { - size_t last_index = len - 1; - char last_char = buf_ptr(full_path)[last_index]; - if (os_is_sep(last_char)) { - if (last_index == 0) { - if (out_dirname) buf_init_from_mem(out_dirname, &last_char, 1); - if (out_basename) buf_init_from_str(out_basename, ""); - return; - } - last_index -= 1; - } - for (size_t i = last_index;;) { - uint8_t c = buf_ptr(full_path)[i]; - if (os_is_sep(c)) { - if (out_dirname) { - buf_init_from_mem(out_dirname, buf_ptr(full_path), (i == 0) ? 1 : i); - } - if (out_basename) { - buf_init_from_mem(out_basename, buf_ptr(full_path) + i + 1, buf_len(full_path) - (i + 1)); - } - return; - } - if (i == 0) break; - i -= 1; - } - } - if (out_dirname) buf_init_from_mem(out_dirname, ".", 1); - if (out_basename) buf_init_from_buf(out_basename, full_path); -} - -void os_path_extname(Buf *full_path, Buf *out_basename, Buf *out_extname) { - if (buf_len(full_path) == 0) { - if (out_basename) buf_init_from_str(out_basename, ""); - if (out_extname) buf_init_from_str(out_extname, ""); - return; - } - size_t i = buf_len(full_path) - 1; - while (true) { - if (buf_ptr(full_path)[i] == '.') { - if (out_basename) { - buf_resize(out_basename, 0); - buf_append_mem(out_basename, buf_ptr(full_path), i); - } - - if (out_extname) { - buf_resize(out_extname, 0); - buf_append_mem(out_extname, buf_ptr(full_path) + i, buf_len(full_path) - i); - } - return; - } - - if (i == 0) { - if (out_basename) buf_init_from_buf(out_basename, full_path); - if (out_extname) buf_init_from_str(out_extname, ""); - return; - } - i -= 1; - } -} - -void os_path_join(Buf *dirname, Buf *basename, Buf *out_full_path) { - if (buf_len(dirname) == 0) { - buf_init_from_buf(out_full_path, basename); - return; - } - - buf_init_from_buf(out_full_path, dirname); - uint8_t c = *(buf_ptr(out_full_path) + buf_len(out_full_path) - 1); - if (!os_is_sep(c)) - buf_append_char(out_full_path, ZIG_OS_SEP_CHAR); - buf_append_buf(out_full_path, basename); -} - -Error os_path_real(Buf *rel_path, Buf *out_abs_path) { -#if defined(ZIG_OS_WINDOWS) - PathSpace rel_path_space = slice_to_prefixed_file_w(buf_to_slice(rel_path)); - PathSpace out_abs_path_space; - - if (_wfullpath(&out_abs_path_space.data.items[0], &rel_path_space.data.items[0], PATH_MAX_WIDE) == nullptr) { - zig_panic("_wfullpath failed"); - } - utf16le_ptr_to_utf8(out_abs_path, &out_abs_path_space.data.items[0]); - return ErrorNone; -#elif defined(ZIG_OS_POSIX) - buf_resize(out_abs_path, PATH_MAX + 1); - char *result = realpath(buf_ptr(rel_path), buf_ptr(out_abs_path)); - if (!result) { - int err = errno; - if (err == EACCES) { - return ErrorAccess; - } else if (err == ENOENT) { - return ErrorFileNotFound; - } else if (err == ENOMEM) { - return ErrorNoMem; - } else { - return ErrorFileSystem; - } - } - buf_resize(out_abs_path, strlen(buf_ptr(out_abs_path))); - return ErrorNone; -#else -#error "missing os_path_real implementation" -#endif -} - -#if defined(ZIG_OS_WINDOWS) -// Ported from std/os/path.zig -static bool isAbsoluteWindows(Slice path) { - if (path.ptr[0] == '/') - return true; - - if (path.ptr[0] == '\\') { - return true; - } - if (path.len < 3) { - return false; - } - if (path.ptr[1] == ':') { - if (path.ptr[2] == '/') - return true; - if (path.ptr[2] == '\\') - return true; - } - return false; -} -#endif - -bool os_path_is_absolute(Buf *path) { -#if defined(ZIG_OS_WINDOWS) - return isAbsoluteWindows(buf_to_slice(path)); -#elif defined(ZIG_OS_POSIX) - return buf_ptr(path)[0] == '/'; -#else -#error "missing os_path_is_absolute implementation" -#endif -} - -#if defined(ZIG_OS_WINDOWS) - -enum WindowsPathKind { - WindowsPathKindNone, - WindowsPathKindDrive, - WindowsPathKindNetworkShare, -}; - -struct WindowsPath { - Slice disk_designator; - WindowsPathKind kind; - bool is_abs; -}; - - -// Ported from std/os/path.zig -static WindowsPath windowsParsePath(Slice path) { - if (path.len >= 2 && path.ptr[1] == ':') { - return WindowsPath{ - path.slice(0, 2), - WindowsPathKindDrive, - isAbsoluteWindows(path), - }; - } - if (path.len >= 1 && (path.ptr[0] == '/' || path.ptr[0] == '\\') && - (path.len == 1 || (path.ptr[1] != '/' && path.ptr[1] != '\\'))) - { - return WindowsPath{ - path.slice(0, 0), - WindowsPathKindNone, - true, - }; - } - WindowsPath relative_path = { - str(""), - WindowsPathKindNone, - false, - }; - if (path.len < strlen("//a/b")) { - return relative_path; - } - - { - if (memStartsWith(path, str("//"))) { - if (path.ptr[2] == '/') { - return relative_path; - } - - SplitIterator it = memSplit(path, str("/")); - { - Optional> opt_component = SplitIterator_next(&it); - if (!opt_component.is_some) return relative_path; - } - { - Optional> opt_component = SplitIterator_next(&it); - if (!opt_component.is_some) return relative_path; - } - return WindowsPath{ - path.slice(0, it.index), - WindowsPathKindNetworkShare, - isAbsoluteWindows(path), - }; - } - } - { - if (memStartsWith(path, str("\\\\"))) { - if (path.ptr[2] == '\\') { - return relative_path; - } - - SplitIterator it = memSplit(path, str("\\")); - { - Optional> opt_component = SplitIterator_next(&it); - if (!opt_component.is_some) return relative_path; - } - { - Optional> opt_component = SplitIterator_next(&it); - if (!opt_component.is_some) return relative_path; - } - return WindowsPath{ - path.slice(0, it.index), - WindowsPathKindNetworkShare, - isAbsoluteWindows(path), - }; - } - } - return relative_path; -} - -// Ported from std/os/path.zig -static uint8_t asciiUpper(uint8_t byte) { - if (byte >= 'a' && byte <= 'z') { - return 'A' + (byte - 'a'); - } - return byte; -} - -// Ported from std/os/path.zig -static bool asciiEqlIgnoreCase(Slice s1, Slice s2) { - if (s1.len != s2.len) - return false; - for (size_t i = 0; i < s1.len; i += 1) { - if (asciiUpper(s1.ptr[i]) != asciiUpper(s2.ptr[i])) - return false; - } - return true; -} - -// Ported from std/os/path.zig -static bool compareDiskDesignators(WindowsPathKind kind, Slice p1, Slice p2) { - switch (kind) { - case WindowsPathKindNone: - assert(p1.len == 0); - assert(p2.len == 0); - return true; - case WindowsPathKindDrive: - return asciiUpper(p1.ptr[0]) == asciiUpper(p2.ptr[0]); - case WindowsPathKindNetworkShare: - uint8_t sep1 = p1.ptr[0]; - uint8_t sep2 = p2.ptr[0]; - - SplitIterator it1 = memSplit(p1, {&sep1, 1}); - SplitIterator it2 = memSplit(p2, {&sep2, 1}); - - // TODO ASCII is wrong, we actually need full unicode support to compare paths. - return asciiEqlIgnoreCase(SplitIterator_next(&it1).value, SplitIterator_next(&it2).value) && - asciiEqlIgnoreCase(SplitIterator_next(&it1).value, SplitIterator_next(&it2).value); - } - zig_unreachable(); -} - -// Ported from std/os/path.zig -static Buf os_path_resolve_windows(Buf **paths_ptr, size_t paths_len) { - if (paths_len == 0) { - Buf cwd = BUF_INIT; - int err; - if ((err = os_get_cwd(&cwd))) { - zig_panic("get cwd failed"); - } - return cwd; - } - - // determine which disk designator we will result with, if any - char result_drive_buf[3] = {'_', ':', '\0'}; // 0 needed for strlen later - Slice result_disk_designator = str(""); - WindowsPathKind have_drive_kind = WindowsPathKindNone; - bool have_abs_path = false; - size_t first_index = 0; - size_t max_size = 0; - for (size_t i = 0; i < paths_len; i += 1) { - Slice p = buf_to_slice(paths_ptr[i]); - WindowsPath parsed = windowsParsePath(p); - if (parsed.is_abs) { - have_abs_path = true; - first_index = i; - max_size = result_disk_designator.len; - } - switch (parsed.kind) { - case WindowsPathKindDrive: - result_drive_buf[0] = asciiUpper(parsed.disk_designator.ptr[0]); - result_disk_designator = str(result_drive_buf); - have_drive_kind = WindowsPathKindDrive; - break; - case WindowsPathKindNetworkShare: - result_disk_designator = parsed.disk_designator; - have_drive_kind = WindowsPathKindNetworkShare; - break; - case WindowsPathKindNone: - break; - } - max_size += p.len + 1; - } - - // if we will result with a disk designator, loop again to determine - // which is the last time the disk designator is absolutely specified, if any - // and count up the max bytes for paths related to this disk designator - if (have_drive_kind != WindowsPathKindNone) { - have_abs_path = false; - first_index = 0; - max_size = result_disk_designator.len; - bool correct_disk_designator = false; - - for (size_t i = 0; i < paths_len; i += 1) { - Slice p = buf_to_slice(paths_ptr[i]); - WindowsPath parsed = windowsParsePath(p); - if (parsed.kind != WindowsPathKindNone) { - if (parsed.kind == have_drive_kind) { - correct_disk_designator = compareDiskDesignators(have_drive_kind, result_disk_designator, parsed.disk_designator); - } else { - continue; - } - } - if (!correct_disk_designator) { - continue; - } - if (parsed.is_abs) { - first_index = i; - max_size = result_disk_designator.len; - have_abs_path = true; - } - max_size += p.len + 1; - } - } - - // Allocate result and fill in the disk designator, calling getCwd if we have to. - Slice result; - size_t result_index = 0; - - if (have_abs_path) { - switch (have_drive_kind) { - case WindowsPathKindDrive: { - result = Slice::alloc(max_size); - - memCopy(result, result_disk_designator); - result_index += result_disk_designator.len; - break; - } - case WindowsPathKindNetworkShare: { - result = Slice::alloc(max_size); - SplitIterator it = memSplit(buf_to_slice(paths_ptr[first_index]), str("/\\")); - Slice server_name = SplitIterator_next(&it).value; - Slice other_name = SplitIterator_next(&it).value; - - result.ptr[result_index] = '\\'; - result_index += 1; - result.ptr[result_index] = '\\'; - result_index += 1; - memCopy(result.sliceFrom(result_index), server_name); - result_index += server_name.len; - result.ptr[result_index] = '\\'; - result_index += 1; - memCopy(result.sliceFrom(result_index), other_name); - result_index += other_name.len; - - result_disk_designator = result.slice(0, result_index); - break; - } - case WindowsPathKindNone: { - Buf cwd = BUF_INIT; - int err; - if ((err = os_get_cwd(&cwd))) { - zig_panic("get cwd failed"); - } - WindowsPath parsed_cwd = windowsParsePath(buf_to_slice(&cwd)); - result = Slice::alloc(max_size + parsed_cwd.disk_designator.len + 1); - memCopy(result, parsed_cwd.disk_designator); - result_index += parsed_cwd.disk_designator.len; - result_disk_designator = result.slice(0, parsed_cwd.disk_designator.len); - if (parsed_cwd.kind == WindowsPathKindDrive) { - result.ptr[0] = asciiUpper(result.ptr[0]); - } - have_drive_kind = parsed_cwd.kind; - break; - } - } - } else { - // TODO call get cwd for the result_disk_designator instead of the global one - Buf cwd = BUF_INIT; - int err; - if ((err = os_get_cwd(&cwd))) { - zig_panic("get cwd failed"); - } - result = Slice::alloc(max_size + buf_len(&cwd) + 1); - - memCopy(result, buf_to_slice(&cwd)); - result_index += buf_len(&cwd); - WindowsPath parsed_cwd = windowsParsePath(result.slice(0, result_index)); - result_disk_designator = parsed_cwd.disk_designator; - if (parsed_cwd.kind == WindowsPathKindDrive) { - result.ptr[0] = asciiUpper(result.ptr[0]); - } - have_drive_kind = parsed_cwd.kind; - } - - // Now we know the disk designator to use, if any, and what kind it is. And our result - // is big enough to append all the paths to. - bool correct_disk_designator = true; - for (size_t i = 0; i < paths_len; i += 1) { - Slice p = buf_to_slice(paths_ptr[i]); - WindowsPath parsed = windowsParsePath(p); - - if (parsed.kind != WindowsPathKindNone) { - if (parsed.kind == have_drive_kind) { - correct_disk_designator = compareDiskDesignators(have_drive_kind, result_disk_designator, parsed.disk_designator); - } else { - continue; - } - } - if (!correct_disk_designator) { - continue; - } - SplitIterator it = memSplit(p.sliceFrom(parsed.disk_designator.len), str("/\\")); - while (true) { - Optional> opt_component = SplitIterator_next(&it); - if (!opt_component.is_some) break; - Slice component = opt_component.value; - if (memEql(component, str("."))) { - continue; - } else if (memEql(component, str(".."))) { - while (true) { - if (result_index == 0 || result_index == result_disk_designator.len) - break; - result_index -= 1; - if (result.ptr[result_index] == '\\' || result.ptr[result_index] == '/') - break; - } - } else { - result.ptr[result_index] = '\\'; - result_index += 1; - memCopy(result.sliceFrom(result_index), component); - result_index += component.len; - } - } - } - - if (result_index == result_disk_designator.len) { - result.ptr[result_index] = '\\'; - result_index += 1; - } - - Buf return_value = BUF_INIT; - buf_init_from_mem(&return_value, (char *)result.ptr, result_index); - return return_value; -} -#endif - -#if defined(ZIG_OS_POSIX) -// Ported from std/os/path.zig -static Buf os_path_resolve_posix(Buf **paths_ptr, size_t paths_len) { - if (paths_len == 0) { - Buf cwd = BUF_INIT; - int err; - if ((err = os_get_cwd(&cwd))) { - zig_panic("get cwd failed"); - } - return cwd; - } - - size_t first_index = 0; - bool have_abs = false; - size_t max_size = 0; - for (size_t i = 0; i < paths_len; i += 1) { - Buf *p = paths_ptr[i]; - if (os_path_is_absolute(p)) { - first_index = i; - have_abs = true; - max_size = 0; - } - max_size += buf_len(p) + 1; - } - - uint8_t *result_ptr; - size_t result_len; - size_t result_index = 0; - - if (have_abs) { - result_len = max_size; - result_ptr = heap::c_allocator.allocate_nonzero(result_len); - } else { - Buf cwd = BUF_INIT; - int err; - if ((err = os_get_cwd(&cwd))) { - zig_panic("get cwd failed"); - } - result_len = max_size + buf_len(&cwd) + 1; - result_ptr = heap::c_allocator.allocate_nonzero(result_len); - memcpy(result_ptr, buf_ptr(&cwd), buf_len(&cwd)); - result_index += buf_len(&cwd); - } - - for (size_t i = first_index; i < paths_len; i += 1) { - Buf *p = paths_ptr[i]; - SplitIterator it = memSplit(buf_to_slice(p), str("/")); - while (true) { - Optional> opt_component = SplitIterator_next(&it); - if (!opt_component.is_some) break; - Slice component = opt_component.value; - - if (memEql(component, str("."))) { - continue; - } else if (memEql(component, str(".."))) { - while (true) { - if (result_index == 0) - break; - result_index -= 1; - if (result_ptr[result_index] == '/') - break; - } - } else { - result_ptr[result_index] = '/'; - result_index += 1; - memcpy(result_ptr + result_index, component.ptr, component.len); - result_index += component.len; - } - } - } - - if (result_index == 0) { - result_ptr[0] = '/'; - result_index += 1; - } - - Buf return_value = BUF_INIT; - buf_init_from_mem(&return_value, (char *)result_ptr, result_index); - return return_value; -} -#endif - -// Ported from std/os/path.zig -Buf os_path_resolve(Buf **paths_ptr, size_t paths_len) { -#if defined(ZIG_OS_WINDOWS) - return os_path_resolve_windows(paths_ptr, paths_len); -#elif defined(ZIG_OS_POSIX) - return os_path_resolve_posix(paths_ptr, paths_len); -#else -#error "missing os_path_resolve implementation" -#endif -} - -Error os_fetch_file(FILE *f, Buf *out_buf) { - static const ssize_t buf_size = 0x2000; - buf_resize(out_buf, buf_size); - ssize_t actual_buf_len = 0; - - for (;;) { - size_t amt_read = fread(buf_ptr(out_buf) + actual_buf_len, 1, buf_size, f); - actual_buf_len += amt_read; - - if (amt_read != buf_size) { - if (feof(f)) { - buf_resize(out_buf, actual_buf_len); - return ErrorNone; - } else { - return ErrorFileSystem; - } - } - - buf_resize(out_buf, actual_buf_len + buf_size); - } - zig_unreachable(); -} - -Error os_file_exists(Buf *full_path, bool *result) { -#if defined(ZIG_OS_WINDOWS) - PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(full_path)); - *result = GetFileAttributesW(&path_space.data.items[0]) != INVALID_FILE_ATTRIBUTES; - return ErrorNone; -#else - *result = access(buf_ptr(full_path), F_OK) != -1; - return ErrorNone; -#endif -} - -#if defined(ZIG_OS_POSIX) -static Error os_exec_process_posix(ZigList &args, - Termination *term, Buf *out_stderr, Buf *out_stdout) -{ - int stdin_pipe[2]; - int stdout_pipe[2]; - int stderr_pipe[2]; - int err_pipe[2]; - - int err; - if ((err = pipe(stdin_pipe))) - zig_panic("pipe failed"); - if ((err = pipe(stdout_pipe))) - zig_panic("pipe failed"); - if ((err = pipe(stderr_pipe))) - zig_panic("pipe failed"); - if ((err = pipe(err_pipe))) - zig_panic("pipe failed"); - - pid_t pid = fork(); - if (pid == -1) - zig_panic("fork failed: %s", strerror(errno)); - if (pid == 0) { - // child - if (dup2(stdin_pipe[0], STDIN_FILENO) == -1) - zig_panic("dup2 failed"); - - if (dup2(stdout_pipe[1], STDOUT_FILENO) == -1) - zig_panic("dup2 failed"); - - if (dup2(stderr_pipe[1], STDERR_FILENO) == -1) - zig_panic("dup2 failed"); - - const char **argv = heap::c_allocator.allocate(args.length + 1); - argv[args.length] = nullptr; - for (size_t i = 0; i < args.length; i += 1) { - argv[i] = args.at(i); - } - execvp(argv[0], const_cast(argv)); - Error report_err = ErrorUnexpected; - if (errno == ENOENT) { - report_err = ErrorFileNotFound; - } - if (write(err_pipe[1], &report_err, sizeof(Error)) == -1) { - zig_panic("write failed"); - } - exit(1); - } else { - // parent - close(stdin_pipe[0]); - close(stdin_pipe[1]); - close(stdout_pipe[1]); - close(stderr_pipe[1]); - - int status; - waitpid(pid, &status, 0); - populate_termination(term, status); - - FILE *stdout_f = fdopen(stdout_pipe[0], "rb"); - FILE *stderr_f = fdopen(stderr_pipe[0], "rb"); - Error err1 = os_fetch_file(stdout_f, out_stdout); - Error err2 = os_fetch_file(stderr_f, out_stderr); - - fclose(stdout_f); - fclose(stderr_f); - - if (err1) return err1; - if (err2) return err2; - - Error child_err = ErrorNone; - if (write(err_pipe[1], &child_err, sizeof(Error)) == -1) { - zig_panic("write failed"); - } - close(err_pipe[1]); - if (read(err_pipe[0], &child_err, sizeof(Error)) == -1) { - zig_panic("write failed"); - } - close(err_pipe[0]); - return child_err; - } -} -#endif - -#if defined(ZIG_OS_WINDOWS) - -//static void win32_panic(const char *str) { -// DWORD err = GetLastError(); -// LPSTR messageBuffer = nullptr; -// FormatMessageA( -// FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, -// NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL); -// zig_panic(str, messageBuffer); -// LocalFree(messageBuffer); -//} - -static Error os_exec_process_windows(ZigList &args, - Termination *term, Buf *out_stderr, Buf *out_stdout) -{ - Buf command_line = BUF_INIT; - os_windows_create_command_line(&command_line, args); - - HANDLE g_hChildStd_IN_Rd = NULL; - HANDLE g_hChildStd_IN_Wr = NULL; - HANDLE g_hChildStd_OUT_Rd = NULL; - HANDLE g_hChildStd_OUT_Wr = NULL; - HANDLE g_hChildStd_ERR_Rd = NULL; - HANDLE g_hChildStd_ERR_Wr = NULL; - - SECURITY_ATTRIBUTES saAttr; - saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); - saAttr.bInheritHandle = TRUE; - saAttr.lpSecurityDescriptor = NULL; - - if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0)) { - zig_panic("StdoutRd CreatePipe"); - } - - if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)) { - zig_panic("Stdout SetHandleInformation"); - } - - if (!CreatePipe(&g_hChildStd_ERR_Rd, &g_hChildStd_ERR_Wr, &saAttr, 0)) { - zig_panic("stderr CreatePipe"); - } - - if (!SetHandleInformation(g_hChildStd_ERR_Rd, HANDLE_FLAG_INHERIT, 0)) { - zig_panic("stderr SetHandleInformation"); - } - - if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0)) { - zig_panic("Stdin CreatePipe"); - } - - if (!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0)) { - zig_panic("Stdin SetHandleInformation"); - } - - - PROCESS_INFORMATION piProcInfo = {0}; - STARTUPINFO siStartInfo = {0}; - siStartInfo.cb = sizeof(STARTUPINFO); - siStartInfo.hStdError = g_hChildStd_ERR_Wr; - siStartInfo.hStdOutput = g_hChildStd_OUT_Wr; - siStartInfo.hStdInput = g_hChildStd_IN_Rd; - siStartInfo.dwFlags |= STARTF_USESTDHANDLES; - - const char *exe = args.at(0); - BOOL success = CreateProcess(exe, buf_ptr(&command_line), nullptr, nullptr, TRUE, 0, nullptr, nullptr, - &siStartInfo, &piProcInfo); - - if (!success) { - if (GetLastError() == ERROR_FILE_NOT_FOUND) { - CloseHandle(piProcInfo.hProcess); - CloseHandle(piProcInfo.hThread); - return ErrorFileNotFound; - } - zig_panic("CreateProcess failed. exe: %s command_line: %s", exe, buf_ptr(&command_line)); - } - - if (!CloseHandle(g_hChildStd_IN_Wr)) { - zig_panic("stdinwr closehandle"); - } - - CloseHandle(g_hChildStd_IN_Rd); - CloseHandle(g_hChildStd_ERR_Wr); - CloseHandle(g_hChildStd_OUT_Wr); - - static const size_t BUF_SIZE = 4 * 1024; - { - DWORD dwRead; - char chBuf[BUF_SIZE]; - - buf_resize(out_stdout, 0); - for (;;) { - success = ReadFile( g_hChildStd_OUT_Rd, chBuf, BUF_SIZE, &dwRead, NULL); - if (!success || dwRead == 0) break; - - buf_append_mem(out_stdout, chBuf, dwRead); - } - CloseHandle(g_hChildStd_OUT_Rd); - } - { - DWORD dwRead; - char chBuf[BUF_SIZE]; - - buf_resize(out_stderr, 0); - for (;;) { - success = ReadFile( g_hChildStd_ERR_Rd, chBuf, BUF_SIZE, &dwRead, NULL); - if (!success || dwRead == 0) break; - - buf_append_mem(out_stderr, chBuf, dwRead); - } - CloseHandle(g_hChildStd_ERR_Rd); - } - - WaitForSingleObject(piProcInfo.hProcess, INFINITE); - - DWORD exit_code; - if (!GetExitCodeProcess(piProcInfo.hProcess, &exit_code)) { - zig_panic("GetExitCodeProcess failed"); - } - term->how = TerminationIdClean; - term->code = exit_code; - - CloseHandle(piProcInfo.hProcess); - CloseHandle(piProcInfo.hThread); - - return ErrorNone; -} -#endif - -Error os_execv(const char *exe, const char **argv) { -#if defined(ZIG_OS_WINDOWS) - return ErrorUnsupportedOperatingSystem; -#else - execv(exe, (char *const *)argv); - switch (errno) { - case ENOMEM: - return ErrorSystemResources; - case EIO: - return ErrorFileSystem; - default: - return ErrorUnexpected; - } -#endif -} - -Error os_exec_process(ZigList &args, - Termination *term, Buf *out_stderr, Buf *out_stdout) -{ -#if defined(ZIG_OS_WINDOWS) - return os_exec_process_windows(args, term, out_stderr, out_stdout); -#elif defined(ZIG_OS_POSIX) - return os_exec_process_posix(args, term, out_stderr, out_stdout); -#else -#error "missing os_exec_process implementation" -#endif -} - -Error os_write_file(Buf *full_path, Buf *contents) { -#if defined(ZIG_OS_WINDOWS) - PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(full_path)); - FILE *f = _wfopen(&path_space.data.items[0], L"wb"); -#else - FILE *f = fopen(buf_ptr(full_path), "wb"); -#endif - if (!f) { - zig_panic("os_write_file failed for %s", buf_ptr(full_path)); - } - size_t amt_written = fwrite(buf_ptr(contents), 1, buf_len(contents), f); - if (amt_written != (size_t)buf_len(contents)) - zig_panic("write failed: %s", strerror(errno)); - if (fclose(f)) - zig_panic("close failed"); - return ErrorNone; -} - -static Error copy_open_files(FILE *src_f, FILE *dest_f) { - static const size_t buf_size = 2048; - char buf[buf_size]; - for (;;) { - size_t amt_read = fread(buf, 1, buf_size, src_f); - if (amt_read != buf_size) { - if (ferror(src_f)) { - return ErrorFileSystem; - } - } - size_t amt_written = fwrite(buf, 1, amt_read, dest_f); - if (amt_written != amt_read) { - return ErrorFileSystem; - } - if (feof(src_f)) { - return ErrorNone; - } - } -} - -Error os_dump_file(Buf *src_path, FILE *dest_file) { - Error err; - -#if defined(ZIG_OS_WINDOWS) - PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(src_path)); - FILE *src_f = _wfopen(&path_space.data.items[0], L"rb"); -#else - FILE *src_f = fopen(buf_ptr(src_path), "rb"); -#endif - if (!src_f) { - int err = errno; - if (err == ENOENT) { - return ErrorFileNotFound; - } else if (err == EACCES || err == EPERM) { - return ErrorAccess; - } else { - return ErrorFileSystem; - } - } - copy_open_files(src_f, dest_file); - if ((err = copy_open_files(src_f, dest_file))) { - fclose(src_f); - return err; - } - - fclose(src_f); - return ErrorNone; -} - -#if defined(ZIG_OS_WINDOWS) -static void windows_filetime_to_os_timestamp(FILETIME *ft, OsTimeStamp *mtime) { - mtime->sec = (((ULONGLONG) ft->dwHighDateTime) << 32) + ft->dwLowDateTime; - mtime->nsec = 0; -} -static FILETIME windows_os_timestamp_to_filetime(OsTimeStamp mtime) { - FILETIME result; - result.dwHighDateTime = mtime.sec >> 32; - result.dwLowDateTime = mtime.sec; - return result; -} -#endif - -static Error set_file_times(OsFile file, OsTimeStamp ts) { -#if defined(ZIG_OS_WINDOWS) - FILETIME ft = windows_os_timestamp_to_filetime(ts); - if (SetFileTime(file, nullptr, &ft, &ft) == 0) { - return ErrorUnexpected; - } - return ErrorNone; -#else - struct timespec times[2] = { - { (time_t)ts.sec, (long)ts.nsec }, - { (time_t)ts.sec, (long)ts.nsec }, - }; - if (futimens(file, times) == -1) { - switch (errno) { - case EBADF: - zig_panic("futimens EBADF"); - default: - return ErrorUnexpected; - } - } - return ErrorNone; -#endif -} - -Error os_update_file(Buf *src_path, Buf *dst_path) { - Error err; - - OsFile src_file; - OsFileAttr src_attr; - if ((err = os_file_open_r(src_path, &src_file, &src_attr))) { - return err; - } - - OsFile dst_file; - OsFileAttr dst_attr; - if ((err = os_file_open_w(dst_path, &dst_file, &dst_attr, src_attr.mode))) { - os_file_close(&src_file); - return err; - } - - if (src_attr.size == dst_attr.size && - src_attr.mode == dst_attr.mode && - src_attr.mtime.sec == dst_attr.mtime.sec && - src_attr.mtime.nsec == dst_attr.mtime.nsec) - { - os_file_close(&src_file); - os_file_close(&dst_file); - return ErrorNone; - } -#if defined(ZIG_OS_WINDOWS) - if (SetEndOfFile(dst_file) == 0) { - return ErrorUnexpected; - } -#else - if (ftruncate(dst_file, 0) == -1) { - return ErrorUnexpected; - } -#endif -#if defined(ZIG_OS_WINDOWS) - FILE *src_libc_file = _fdopen(_open_osfhandle((intptr_t)src_file, _O_RDONLY), "rb"); - FILE *dst_libc_file = _fdopen(_open_osfhandle((intptr_t)dst_file, 0), "wb"); -#else - FILE *src_libc_file = fdopen(src_file, "rb"); - FILE *dst_libc_file = fdopen(dst_file, "wb"); -#endif - assert(src_libc_file); - assert(dst_libc_file); - - if ((err = copy_open_files(src_libc_file, dst_libc_file))) { - fclose(src_libc_file); - fclose(dst_libc_file); - return err; - } - if (fflush(dst_libc_file) == -1) { - return ErrorUnexpected; - } - err = set_file_times(dst_file, src_attr.mtime); - fclose(src_libc_file); - fclose(dst_libc_file); - return err; -} - -Error os_copy_file(Buf *src_path, Buf *dest_path) { -#if defined(ZIG_OS_WINDOWS) - PathSpace src_path_space = slice_to_prefixed_file_w(buf_to_slice(src_path)); - FILE *src_f = _wfopen(&src_path_space.data.items[0], L"rb"); -#else - FILE *src_f = fopen(buf_ptr(src_path), "rb"); -#endif - if (!src_f) { - int err = errno; - if (err == ENOENT) { - return ErrorFileNotFound; - } else if (err == EACCES || err == EPERM) { - return ErrorAccess; - } else { - return ErrorFileSystem; - } - } -#if defined(ZIG_OS_WINDOWS) - PathSpace dest_path_space = slice_to_prefixed_file_w(buf_to_slice(dest_path)); - FILE *dest_f = _wfopen(&dest_path_space.data.items[0], L"wb"); -#else - FILE *dest_f = fopen(buf_ptr(dest_path), "wb"); -#endif - if (!dest_f) { - int err = errno; - if (err == ENOENT) { - fclose(src_f); - return ErrorFileNotFound; - } else if (err == EACCES || err == EPERM) { - fclose(src_f); - return ErrorAccess; - } else { - fclose(src_f); - return ErrorFileSystem; - } - } - Error err = copy_open_files(src_f, dest_f); - fclose(src_f); - fclose(dest_f); - return err; -} - -Error os_fetch_file_path(Buf *full_path, Buf *out_contents) { -#if defined(ZIG_OS_WINDOWS) - PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(full_path)); - FILE *f = _wfopen(&path_space.data.items[0], L"rb"); -#else - FILE *f = fopen(buf_ptr(full_path), "rb"); -#endif - if (!f) { - switch (errno) { - case EACCES: - return ErrorAccess; - case EINTR: - return ErrorInterrupted; - case EINVAL: - return ErrorInvalidFilename; - case ENFILE: - case ENOMEM: - return ErrorSystemResources; - case ENOENT: - return ErrorFileNotFound; - default: - return ErrorFileSystem; - } - } - Error result = os_fetch_file(f, out_contents); - fclose(f); - return result; -} - -Error os_get_cwd(Buf *out_cwd) { -#if defined(ZIG_OS_WINDOWS) - PathSpace path_space; - if (GetCurrentDirectoryW(PATH_MAX_WIDE, &path_space.data.items[0]) == 0) { - zig_panic("GetCurrentDirectory failed"); - } - utf16le_ptr_to_utf8(out_cwd, &path_space.data.items[0]); - return ErrorNone; -#elif defined(ZIG_OS_POSIX) - char buf[PATH_MAX]; - char *res = getcwd(buf, PATH_MAX); - if (res == nullptr) { - zig_panic("unable to get cwd: %s", strerror(errno)); - } - buf_init_from_str(out_cwd, res); - return ErrorNone; -#else -#error "missing os_get_cwd implementation" -#endif -} - -#if defined(ZIG_OS_WINDOWS) -#define is_wprefix(s, prefix) \ - (wcsncmp((s), (prefix), sizeof(prefix) / sizeof(WCHAR) - 1) == 0) -static bool is_stderr_cyg_pty(void) { - HANDLE stderr_handle = GetStdHandle(STD_ERROR_HANDLE); - if (stderr_handle == INVALID_HANDLE_VALUE) - return false; - - const int size = sizeof(FILE_NAME_INFO) + sizeof(WCHAR) * MAX_PATH; - FILE_NAME_INFO *nameinfo; - WCHAR *p = NULL; - - // Cygwin/msys's pty is a pipe. - if (GetFileType(stderr_handle) != FILE_TYPE_PIPE) { - return 0; - } - nameinfo = reinterpret_cast(heap::c_allocator.allocate(size)); - if (nameinfo == NULL) { - return 0; - } - // Check the name of the pipe: - // '\{cygwin,msys}-XXXXXXXXXXXXXXXX-ptyN-{from,to}-master' - if (GetFileInformationByHandleEx(stderr_handle, FileNameInfo, nameinfo, size)) { - nameinfo->FileName[nameinfo->FileNameLength / sizeof(WCHAR)] = L'\0'; - p = nameinfo->FileName; - if (is_wprefix(p, L"\\cygwin-")) { /* Cygwin */ - p += 8; - } else if (is_wprefix(p, L"\\msys-")) { /* MSYS and MSYS2 */ - p += 6; - } else { - p = NULL; - } - if (p != NULL) { - while (*p && isxdigit(*p)) /* Skip 16-digit hexadecimal. */ - ++p; - if (is_wprefix(p, L"-pty")) { - p += 4; - } else { - p = NULL; - } - } - if (p != NULL) { - while (*p && isdigit(*p)) /* Skip pty number. */ - ++p; - if (is_wprefix(p, L"-from-master")) { - //p += 12; - } else if (is_wprefix(p, L"-to-master")) { - //p += 10; - } else { - p = NULL; - } - } - } - heap::c_allocator.deallocate(reinterpret_cast(nameinfo), size); - return (p != NULL); -} -#endif - -bool os_stderr_tty(void) { -#if defined(ZIG_OS_WINDOWS) - return _isatty(_fileno(stderr)) != 0 || is_stderr_cyg_pty(); -#elif defined(ZIG_OS_POSIX) - return isatty(STDERR_FILENO) != 0; -#else -#error "missing os_stderr_tty implementation" -#endif -} - -Error os_delete_file(Buf *path) { - if (remove(buf_ptr(path))) { - return ErrorFileSystem; - } else { - return ErrorNone; - } -} - -Error os_rename(Buf *src_path, Buf *dest_path) { - if (buf_eql_buf(src_path, dest_path)) { - return ErrorNone; - } -#if defined(ZIG_OS_WINDOWS) - PathSpace src_path_space = slice_to_prefixed_file_w(buf_to_slice(src_path)); - PathSpace dest_path_space = slice_to_prefixed_file_w(buf_to_slice(dest_path)); - if (!MoveFileExW(&src_path_space.data.items[0], &dest_path_space.data.items[0], MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH)) { - return ErrorFileSystem; - } -#else - if (rename(buf_ptr(src_path), buf_ptr(dest_path)) == -1) { - return ErrorFileSystem; - } -#endif - return ErrorNone; -} - -OsTimeStamp os_timestamp_calendar(void) { - OsTimeStamp result; -#if defined(ZIG_OS_WINDOWS) - FILETIME ft; - GetSystemTimeAsFileTime(&ft); - windows_filetime_to_os_timestamp(&ft, &result); -#elif defined(__MACH__) - mach_timespec_t mts; - - kern_return_t err = clock_get_time(macos_calendar_clock, &mts); - assert(!err); - - result.sec = mts.tv_sec; - result.nsec = mts.tv_nsec; -#else - struct timespec tms; - clock_gettime(CLOCK_REALTIME, &tms); - - result.sec = tms.tv_sec; - result.nsec = tms.tv_nsec; -#endif - return result; -} - -OsTimeStamp os_timestamp_monotonic(void) { - OsTimeStamp result; -#if defined(ZIG_OS_WINDOWS) - uint64_t counts; - QueryPerformanceCounter((LARGE_INTEGER*)&counts); - result.sec = counts / windows_perf_freq; - result.nsec = (counts % windows_perf_freq) * 1000000000u / windows_perf_freq; -#elif defined(__MACH__) - mach_timespec_t mts; - - kern_return_t err = clock_get_time(macos_monotonic_clock, &mts); - assert(!err); - - result.sec = mts.tv_sec; - result.nsec = mts.tv_nsec; -#else - struct timespec tms; - clock_gettime(CLOCK_MONOTONIC, &tms); - - result.sec = tms.tv_sec; - result.nsec = tms.tv_nsec; -#endif - return result; -} - -Error os_make_path(Buf *path) { - Buf resolved_path = os_path_resolve(&path, 1); - - size_t end_index = buf_len(&resolved_path); - Error err; - while (true) { - if ((err = os_make_dir(buf_slice(&resolved_path, 0, end_index)))) { - if (err == ErrorPathAlreadyExists) { - if (end_index == buf_len(&resolved_path)) - return ErrorNone; - } else if (err == ErrorFileNotFound) { - // march end_index backward until next path component - while (true) { - end_index -= 1; - if (os_is_sep(buf_ptr(&resolved_path)[end_index])) - break; - } - continue; - } else { - return err; - } - } - if (end_index == buf_len(&resolved_path)) - return ErrorNone; - // march end_index forward until next path component - while (true) { - end_index += 1; - if (end_index == buf_len(&resolved_path) || os_is_sep(buf_ptr(&resolved_path)[end_index])) - break; - } - } - return ErrorNone; -} - -Error os_make_dir(Buf *path) { -#if defined(ZIG_OS_WINDOWS) - PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(path)); - if (memEql(buf_to_slice(path), str("C:\\dev\\tést"))) { - for (size_t i = 0; i < path_space.len; i++) { - fprintf(stderr, "%d ", path_space.data.items[i]); - } - fprintf(stderr, "\n"); - } - - if (!CreateDirectoryW(&path_space.data.items[0], NULL)) { - if (GetLastError() == ERROR_ALREADY_EXISTS) - return ErrorPathAlreadyExists; - if (GetLastError() == ERROR_PATH_NOT_FOUND) - return ErrorFileNotFound; - if (GetLastError() == ERROR_ACCESS_DENIED) - return ErrorAccess; - return ErrorUnexpected; - } - return ErrorNone; -#else - if (mkdir(buf_ptr(path), 0755) == -1) { - if (errno == EEXIST) - return ErrorPathAlreadyExists; - if (errno == ENOENT) - return ErrorFileNotFound; - if (errno == EACCES) - return ErrorAccess; - return ErrorUnexpected; - } - return ErrorNone; -#endif -} - -static void init_rand() { -#if defined(ZIG_OS_WINDOWS) - char bytes[sizeof(unsigned)]; - unsigned seed; - RtlGenRandom(bytes, sizeof(unsigned)); - memcpy(&seed, bytes, sizeof(unsigned)); - srand(seed); -#elif defined(ZIG_OS_LINUX) - unsigned char *ptr_random = (unsigned char*)getauxval(AT_RANDOM); - unsigned seed; - memcpy(&seed, ptr_random, sizeof(seed)); - srand(seed); -#elif defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD) - unsigned seed; - size_t len = sizeof(seed); - int mib[2] = { CTL_KERN, KERN_ARND }; - if (sysctl(mib, 2, &seed, &len, NULL, 0) != 0) { - zig_panic("unable to query random data from sysctl"); - } - srand(seed); -#else - int fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC); - if (fd == -1) { - zig_panic("unable to open /dev/urandom"); - } - char bytes[sizeof(unsigned)]; - ssize_t amt_read; - while ((amt_read = read(fd, bytes, sizeof(unsigned))) == -1) { - if (errno == EINTR) continue; - zig_panic("unable to read /dev/urandom"); - } - if (amt_read != sizeof(unsigned)) { - zig_panic("unable to read enough bytes from /dev/urandom"); - } - close(fd); - unsigned seed; - memcpy(&seed, bytes, sizeof(unsigned)); - srand(seed); -#endif -} - -int os_init(void) { - init_rand(); -#if defined(ZIG_OS_WINDOWS) - _setmode(fileno(stdout), _O_BINARY); - _setmode(fileno(stderr), _O_BINARY); - if (!QueryPerformanceFrequency((LARGE_INTEGER*)&windows_perf_freq)) { - return ErrorSystemResources; - } -#elif defined(__MACH__) - host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &macos_monotonic_clock); - host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &macos_calendar_clock); -#endif -#if defined(ZIG_OS_POSIX) - // Raise the open file descriptor limit. - // Code lifted from node.js - struct rlimit lim; - if (getrlimit(RLIMIT_NOFILE, &lim) == 0 && lim.rlim_cur != lim.rlim_max) { - // Do a binary search for the limit. - rlim_t min = lim.rlim_cur; - rlim_t max = 1 << 20; - // But if there's a defined upper bound, don't search, just set it. - if (lim.rlim_max != RLIM_INFINITY) { - min = lim.rlim_max; - max = lim.rlim_max; - } - do { - lim.rlim_cur = min + (max - min) / 2; - if (setrlimit(RLIMIT_NOFILE, &lim)) { - max = lim.rlim_cur; - } else { - min = lim.rlim_cur; - } - } while (min + 1 < max); - } -#endif - return 0; -} - -Error os_self_exe_path(Buf *out_path) { -#if defined(ZIG_OS_WINDOWS) - PathSpace path_space; - DWORD copied_amt = GetModuleFileNameW(nullptr, &path_space.data.items[0], PATH_MAX_WIDE); - if (copied_amt <= 0) { - return ErrorFileNotFound; - } - utf16le_ptr_to_utf8(out_path, &path_space.data.items[0]); - return ErrorNone; - -#elif defined(ZIG_OS_DARWIN) - // How long is the executable's path? - uint32_t u32_len = 0; - int ret1 = _NSGetExecutablePath(nullptr, &u32_len); - assert(ret1 != 0); - - Buf *tmp = buf_alloc_fixed(u32_len); - - // Fill the executable path. - int ret2 = _NSGetExecutablePath(buf_ptr(tmp), &u32_len); - assert(ret2 == 0); - - // According to libuv project, PATH_MAX*2 works around a libc bug where - // the resolved path is sometimes bigger than PATH_MAX. - buf_resize(out_path, PATH_MAX*2); - char *real_path = realpath(buf_ptr(tmp), buf_ptr(out_path)); - if (!real_path) { - buf_init_from_buf(out_path, tmp); - return ErrorNone; - } - - // Resize out_path for the correct length. - buf_resize(out_path, strlen(buf_ptr(out_path))); - - return ErrorNone; -#elif defined(ZIG_OS_LINUX) - buf_resize(out_path, PATH_MAX); - ssize_t amt = readlink("/proc/self/exe", buf_ptr(out_path), buf_len(out_path)); - if (amt == -1) { - return ErrorUnexpected; - } - buf_resize(out_path, amt); - return ErrorNone; -#elif defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_DRAGONFLY) - buf_resize(out_path, PATH_MAX); - int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; - size_t cb = PATH_MAX; - if (sysctl(mib, 4, buf_ptr(out_path), &cb, nullptr, 0) != 0) { - return ErrorUnexpected; - } - buf_resize(out_path, cb - 1); - return ErrorNone; -#elif defined(ZIG_OS_NETBSD) - buf_resize(out_path, PATH_MAX); - int mib[4] = { CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME }; - size_t cb = PATH_MAX; - if (sysctl(mib, 4, buf_ptr(out_path), &cb, nullptr, 0) != 0) { - return ErrorUnexpected; - } - buf_resize(out_path, cb - 1); - return ErrorNone; -#endif - return ErrorFileNotFound; -} - -#define VT_RED "\x1b[31;1m" -#define VT_GREEN "\x1b[32;1m" -#define VT_CYAN "\x1b[36;1m" -#define VT_WHITE "\x1b[37;1m" -#define VT_BOLD "\x1b[0;1m" -#define VT_RESET "\x1b[0m" - -static void set_color_posix(TermColor color) { - switch (color) { - case TermColorRed: - fprintf(stderr, VT_RED); - break; - case TermColorGreen: - fprintf(stderr, VT_GREEN); - break; - case TermColorCyan: - fprintf(stderr, VT_CYAN); - break; - case TermColorWhite: - fprintf(stderr, VT_WHITE); - break; - case TermColorBold: - fprintf(stderr, VT_BOLD); - break; - case TermColorReset: - fprintf(stderr, VT_RESET); - break; - } -} - - -#if defined(ZIG_OS_WINDOWS) -bool got_orig_console_attrs = false; -WORD original_console_attributes = FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE; -#endif - -void os_stderr_set_color(TermColor color) { -#if defined(ZIG_OS_WINDOWS) - if (is_stderr_cyg_pty()) { - set_color_posix(color); - return; - } - HANDLE stderr_handle = GetStdHandle(STD_ERROR_HANDLE); - if (stderr_handle == INVALID_HANDLE_VALUE) - zig_panic("unable to get stderr handle"); - fflush(stderr); - - if (!got_orig_console_attrs) { - got_orig_console_attrs = true; - CONSOLE_SCREEN_BUFFER_INFO info; - if (GetConsoleScreenBufferInfo(stderr_handle, &info)) { - original_console_attributes = info.wAttributes; - } - } - - switch (color) { - case TermColorRed: - SetConsoleTextAttribute(stderr_handle, FOREGROUND_RED|FOREGROUND_INTENSITY); - break; - case TermColorGreen: - SetConsoleTextAttribute(stderr_handle, FOREGROUND_GREEN|FOREGROUND_INTENSITY); - break; - case TermColorCyan: - SetConsoleTextAttribute(stderr_handle, FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_INTENSITY); - break; - case TermColorWhite: - case TermColorBold: - SetConsoleTextAttribute(stderr_handle, - FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_INTENSITY); - break; - case TermColorReset: - SetConsoleTextAttribute(stderr_handle, original_console_attributes); - break; - } -#else - set_color_posix(color); -#endif -} - -#if defined(ZIG_OS_WINDOWS) -// Ported from std/unicode.zig -struct Utf16LeIterator { - uint8_t *bytes; - size_t i; -}; - -// Ported from std/unicode.zig -static Utf16LeIterator Utf16LeIterator_init(WCHAR *ptr) { - return {(uint8_t*)ptr, 0}; -} - -// Ported from std/unicode.zig -static Optional Utf16LeIterator_nextCodepoint(Utf16LeIterator *it) { - if (it->bytes[it->i] == 0 && it->bytes[it->i + 1] == 0) - return {}; - uint32_t c0 = ((uint32_t)it->bytes[it->i]) | (((uint32_t)it->bytes[it->i + 1]) << 8); - if ((c0 & ~((uint32_t)0x03ff)) == 0xd800) { - // surrogate pair - it->i += 2; - assert(it->bytes[it->i] != 0 || it->bytes[it->i + 1] != 0); - uint32_t c1 = ((uint32_t)it->bytes[it->i]) | (((uint32_t)it->bytes[it->i + 1]) << 8); - assert((c1 & ~((uint32_t)0x03ff)) == 0xdc00); - it->i += 2; - return Optional::some(0x10000 + (((c0 & 0x03ff) << 10) | (c1 & 0x03ff))); - } else { - assert((c0 & ~((uint32_t)0x03ff)) != 0xdc00); - it->i += 2; - return Optional::some(c0); - } -} - -// Ported from std/unicode.zig -static uint8_t utf8CodepointSequenceLength(uint32_t c) { - if (c < 0x80) return 1; - if (c < 0x800) return 2; - if (c < 0x10000) return 3; - if (c < 0x110000) return 4; - zig_unreachable(); -} - -// Ported from std.unicode.utf8ByteSequenceLength -static uint8_t utf8ByteSequenceLength(uint8_t first_byte) { - if (first_byte < 0b10000000) return 1; - if ((first_byte & 0b11100000) == 0b11000000) return 2; - if ((first_byte & 0b11110000) == 0b11100000) return 3; - if ((first_byte & 0b11111000) == 0b11110000) return 4; - zig_unreachable(); -} - -// Ported from std/unicode.zig -static size_t utf8Encode(uint32_t c, Slice out) { - size_t length = utf8CodepointSequenceLength(c); - assert(out.len >= length); - switch (length) { - // The pattern for each is the same - // - Increasing the initial shift by 6 each time - // - Each time after the first shorten the shifted - // value to a max of 0b111111 (63) - case 1: - out.ptr[0] = c; // Can just do 0 + codepoint for initial range - break; - case 2: - out.ptr[0] = 0b11000000 | (c >> 6); - out.ptr[1] = 0b10000000 | (c & 0b111111); - break; - case 3: - assert(!(0xd800 <= c && c <= 0xdfff)); - out.ptr[0] = 0b11100000 | (c >> 12); - out.ptr[1] = 0b10000000 | ((c >> 6) & 0b111111); - out.ptr[2] = 0b10000000 | (c & 0b111111); - break; - case 4: - out.ptr[0] = 0b11110000 | (c >> 18); - out.ptr[1] = 0b10000000 | ((c >> 12) & 0b111111); - out.ptr[2] = 0b10000000 | ((c >> 6) & 0b111111); - out.ptr[3] = 0b10000000 | (c & 0b111111); - break; - default: - zig_unreachable(); - } - return length; -} - -// Ported from std.unicode.utf8Decode2 -static uint32_t utf8Decode2(Slice bytes) { - assert(bytes.len == 2); - assert((bytes.at(0) & 0b11100000) == 0b11000000); - - uint32_t value = bytes.at(0) & 0b00011111; - assert((bytes.at(1) & 0b11000000) == 0b10000000); - value <<= 6; - value |= bytes.at(1) & 0b00111111; - - assert(value >= 0x80); - return value; -} - -// Ported from std.unicode.utf8Decode3 -static uint32_t utf8Decode3(Slice bytes) { - assert(bytes.len == 3); - assert((bytes.at(0) & 0b11110000) == 0b11100000); - - uint32_t value = bytes.at(0) & 0b00001111; - assert((bytes.at(1) & 0b11000000) == 0b10000000); - value <<= 6; - value |= bytes.at(1) & 0b00111111; - - assert((bytes.at(2) & 0b11000000) == 0b10000000); - value <<= 6; - value |= bytes.at(2) & 0b00111111; - - assert(value >= 0x80); - assert(value < 0xd800 || value > 0xdfff); - return value; -} - -// Ported from std.unicode.utf8Decode4 -static uint32_t utf8Decode4(Slice bytes) { - assert(bytes.len == 4); - assert((bytes.at(0) & 0b11111000) == 0b11110000); - - uint32_t value = bytes.at(0) & 0b00000111; - assert((bytes.at(1) & 0b11000000) == 0b10000000); - value <<= 6; - value |= bytes.at(1) & 0b00111111; - - assert((bytes.at(2) & 0b11000000) == 0b10000000); - value <<= 6; - value |= bytes.at(2) & 0b00111111; - - assert((bytes.at(3) & 0b11000000) == 0b10000000); - value <<= 6; - value |= bytes.at(3) & 0b00111111; - - assert(value >= 0x10000 && value <= 0x10FFFF); - return value; -} - -// Ported from std.unicode.utf8Decode -static uint32_t utf8Decode(Slice bytes) { - switch (bytes.len) { - case 1: - return bytes.at(0); - break; - case 2: - return utf8Decode2(bytes); - break; - case 3: - return utf8Decode3(bytes); - break; - case 4: - return utf8Decode4(bytes); - break; - default: - zig_unreachable(); - } -} -// Ported from std.unicode.utf16leToUtf8Alloc -static void utf16le_ptr_to_utf8(Buf *out, WCHAR *utf16le) { - // optimistically guess that it will all be ascii. - buf_resize(out, 0); - size_t out_index = 0; - Utf16LeIterator it = Utf16LeIterator_init(utf16le); - for (;;) { - Optional opt_codepoint = Utf16LeIterator_nextCodepoint(&it); - if (!opt_codepoint.is_some) break; - uint32_t codepoint = opt_codepoint.value; - - size_t utf8_len = utf8CodepointSequenceLength(codepoint); - buf_resize(out, buf_len(out) + utf8_len); - utf8Encode(codepoint, {(uint8_t*)buf_ptr(out)+out_index, buf_len(out)-out_index}); - out_index += utf8_len; - } -} - -// Ported from std.unicode.utf8ToUtf16Le -static size_t utf8_to_utf16le(WCHAR *utf16_le, Slice utf8) { - size_t dest_i = 0; - size_t src_i = 0; - while (src_i < utf8.len) { - uint8_t n = utf8ByteSequenceLength(utf8.at(src_i)); - size_t next_src_i = src_i + n; - uint32_t codepoint = utf8Decode(utf8.slice(src_i, next_src_i)); - if (codepoint < 0x10000) { - utf16_le[dest_i] = codepoint; - dest_i += 1; - } else { - WCHAR high = ((codepoint - 0x10000) >> 10) + 0xD800; - WCHAR low = (codepoint & 0x3FF) + 0xDC00; - utf16_le[dest_i] = high; - utf16_le[dest_i + 1] = low; - dest_i += 2; - } - src_i = next_src_i; - } - return dest_i; -} - -// Ported from std.os.windows.sliceToPrefixedFileW -PathSpace slice_to_prefixed_file_w(Slice path) { - PathSpace path_space; - for (size_t idx = 0; idx < path.len; idx++) { - assert(path.ptr[idx] != '*' && path.ptr[idx] != '?' && path.ptr[idx] != '"' && - path.ptr[idx] != '<' && path.ptr[idx] != '>' && path.ptr[idx] != '|'); - } - - size_t start_index; - if (memStartsWith(path, str("\\?")) || !isAbsoluteWindows(path)) { - start_index = 0; - } else { - static WCHAR prefix[4] = { u'\\', u'?', u'?', u'\\' }; - memCopy(path_space.data.slice(), Slice { prefix, 4 }); - start_index = 4; - } - - path_space.len = start_index + utf8_to_utf16le(path_space.data.slice().sliceFrom(start_index).ptr, path); - assert(path_space.len <= path_space.data.len); - - Slice path_slice = path_space.data.slice().slice(0, path_space.len); - for (size_t elem_idx = 0; elem_idx < path_slice.len; elem_idx += 1) { - if (path_slice.at(elem_idx) == '/') { - path_slice.at(elem_idx) = '\\'; - } - } - - path_space.data.items[path_space.len] = 0; - return path_space; -} -#endif - -// Ported from std.os.getAppDataDir -Error os_get_app_data_dir(Buf *out_path, const char *appname) { -#if defined(ZIG_OS_WINDOWS) - WCHAR *dir_path_ptr; - switch (SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, nullptr, &dir_path_ptr)) { - case S_OK: - // defer os.windows.CoTaskMemFree(@ptrCast(*c_void, dir_path_ptr)); - utf16le_ptr_to_utf8(out_path, dir_path_ptr); - CoTaskMemFree(dir_path_ptr); - buf_appendf(out_path, "\\%s", appname); - return ErrorNone; - case E_OUTOFMEMORY: - return ErrorNoMem; - default: - return ErrorUnexpected; - } - zig_unreachable(); -#elif defined(ZIG_OS_DARWIN) - const char *home_dir = getenv("HOME"); - if (home_dir == nullptr) { - // TODO use /etc/passwd - return ErrorFileNotFound; - } - buf_resize(out_path, 0); - buf_appendf(out_path, "%s/Library/Application Support/%s", home_dir, appname); - return ErrorNone; -#elif defined(ZIG_OS_POSIX) - const char *cache_dir = getenv("XDG_CACHE_HOME"); - if (cache_dir == nullptr) { - cache_dir = getenv("HOME"); - if (cache_dir == nullptr) { - // TODO use /etc/passwd - return ErrorFileNotFound; - } - if (cache_dir[0] == 0) { - return ErrorFileNotFound; - } - buf_init_from_str(out_path, cache_dir); - if (buf_ptr(out_path)[buf_len(out_path) - 1] != '/') { - buf_append_char(out_path, '/'); - } - buf_appendf(out_path, ".cache/%s", appname); - } else { - if (cache_dir[0] == 0) { - return ErrorFileNotFound; - } - buf_init_from_str(out_path, cache_dir); - if (buf_ptr(out_path)[buf_len(out_path) - 1] != '/') { - buf_append_char(out_path, '/'); - } - buf_appendf(out_path, "%s", appname); - } - return ErrorNone; -#endif -} - -#if defined(ZIG_OS_LINUX) || defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD) || defined(ZIG_OS_DRAGONFLY) -static int self_exe_shared_libs_callback(struct dl_phdr_info *info, size_t size, void *data) { - ZigList *libs = reinterpret_cast< ZigList *>(data); - if (info->dlpi_name[0] == '/') { - libs->append(buf_create_from_str(info->dlpi_name)); - } - return 0; -} -#endif - -Error os_self_exe_shared_libs(ZigList &paths) { -#if defined(ZIG_OS_LINUX) || defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD) || defined(ZIG_OS_DRAGONFLY) - paths.resize(0); - dl_iterate_phdr(self_exe_shared_libs_callback, &paths); - return ErrorNone; -#elif defined(ZIG_OS_DARWIN) - paths.resize(0); - uint32_t img_count = _dyld_image_count(); - for (uint32_t i = 0; i != img_count; i += 1) { - const char *name = _dyld_get_image_name(i); - paths.append(buf_create_from_str(name)); - } - return ErrorNone; -#elif defined(ZIG_OS_WINDOWS) - // zig is built statically on windows, so we can return an empty list - paths.resize(0); - return ErrorNone; -#else -#error unimplemented -#endif -} - -Error os_file_open_rw(Buf *full_path, OsFile *out_file, OsFileAttr *attr, bool need_write, uint32_t mode) { -#if defined(ZIG_OS_WINDOWS) - PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(full_path)); - HANDLE result = CreateFileW(&path_space.data.items[0], - need_write ? (GENERIC_READ|GENERIC_WRITE) : GENERIC_READ, - need_write ? 0 : FILE_SHARE_READ, - nullptr, - need_write ? OPEN_ALWAYS : OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, nullptr); - - if (result == INVALID_HANDLE_VALUE) { - DWORD err = GetLastError(); - switch (err) { - case ERROR_SHARING_VIOLATION: - return ErrorSharingViolation; - case ERROR_ALREADY_EXISTS: - return ErrorPathAlreadyExists; - case ERROR_FILE_EXISTS: - return ErrorPathAlreadyExists; - case ERROR_FILE_NOT_FOUND: - return ErrorFileNotFound; - case ERROR_PATH_NOT_FOUND: - return ErrorFileNotFound; - case ERROR_ACCESS_DENIED: - return ErrorAccess; - case ERROR_PIPE_BUSY: - return ErrorPipeBusy; - default: - return ErrorUnexpected; - } - } - *out_file = result; - - if (attr != nullptr) { - BY_HANDLE_FILE_INFORMATION file_info; - if (!GetFileInformationByHandle(result, &file_info)) { - CloseHandle(result); - return ErrorUnexpected; - } - windows_filetime_to_os_timestamp(&file_info.ftLastWriteTime, &attr->mtime); - attr->inode = (((uint64_t)file_info.nFileIndexHigh) << 32) | file_info.nFileIndexLow; - attr->mode = 0; - attr->size = (((uint64_t)file_info.nFileSizeHigh) << 32) | file_info.nFileSizeLow; - } - - return ErrorNone; -#else - for (;;) { - int fd = open(buf_ptr(full_path), - need_write ? (O_RDWR|O_CLOEXEC|O_CREAT) : (O_RDONLY|O_CLOEXEC), mode); - if (fd == -1) { - switch (errno) { - case EINTR: - continue; - case EINVAL: - zig_unreachable(); - case EFAULT: - zig_unreachable(); - case EACCES: - case EPERM: - return ErrorAccess; - case EISDIR: - return ErrorIsDir; - case ENOENT: - return ErrorFileNotFound; - default: - return ErrorFileSystem; - } - } - struct stat statbuf; - if (fstat(fd, &statbuf) == -1) { - close(fd); - return ErrorFileSystem; - } - if (S_ISDIR(statbuf.st_mode)) { - close(fd); - return ErrorIsDir; - } - *out_file = fd; - - if (attr != nullptr) { - attr->inode = statbuf.st_ino; -#if defined(ZIG_OS_DARWIN) - attr->mtime.sec = statbuf.st_mtimespec.tv_sec; - attr->mtime.nsec = statbuf.st_mtimespec.tv_nsec; -#else - attr->mtime.sec = statbuf.st_mtim.tv_sec; - attr->mtime.nsec = statbuf.st_mtim.tv_nsec; -#endif - attr->mode = statbuf.st_mode; - attr->size = statbuf.st_size; - } - return ErrorNone; - } -#endif -} - -Error os_file_open_r(Buf *full_path, OsFile *out_file, OsFileAttr *attr) { - return os_file_open_rw(full_path, out_file, attr, false, 0); -} - -Error os_file_open_w(Buf *full_path, OsFile *out_file, OsFileAttr *attr, uint32_t mode) { - return os_file_open_rw(full_path, out_file, attr, true, mode); -} - -Error os_file_open_lock_rw(Buf *full_path, OsFile *out_file) { -#if defined(ZIG_OS_WINDOWS) - PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(full_path)); - for (;;) { - HANDLE result = CreateFileW(&path_space.data.items[0], GENERIC_READ | GENERIC_WRITE, - 0, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); - - if (result == INVALID_HANDLE_VALUE) { - DWORD err = GetLastError(); - switch (err) { - case ERROR_SHARING_VIOLATION: - // TODO wait for the lock instead of sleeping - Sleep(10); - continue; - case ERROR_ALREADY_EXISTS: - return ErrorPathAlreadyExists; - case ERROR_FILE_EXISTS: - return ErrorPathAlreadyExists; - case ERROR_FILE_NOT_FOUND: - return ErrorFileNotFound; - case ERROR_PATH_NOT_FOUND: - return ErrorFileNotFound; - case ERROR_ACCESS_DENIED: - return ErrorAccess; - case ERROR_PIPE_BUSY: - return ErrorPipeBusy; - default: - return ErrorUnexpected; - } - } - *out_file = result; - return ErrorNone; - } -#else - int fd; - for (;;) { - fd = open(buf_ptr(full_path), O_RDWR|O_CLOEXEC|O_CREAT, 0666); - if (fd == -1) { - switch (errno) { - case EINTR: - continue; - case EINVAL: - zig_unreachable(); - case EFAULT: - zig_unreachable(); - case EACCES: - case EPERM: - return ErrorAccess; - case EISDIR: - return ErrorIsDir; - case ENOENT: - return ErrorFileNotFound; - case ENOTDIR: - return ErrorNotDir; - default: - return ErrorFileSystem; - } - } - break; - } - for (;;) { - struct flock lock; - lock.l_type = F_WRLCK; - lock.l_whence = SEEK_SET; - lock.l_start = 0; - lock.l_len = 0; - if (fcntl(fd, F_SETLKW, &lock) == -1) { - switch (errno) { - case EINTR: - continue; - case EBADF: - zig_unreachable(); - case EFAULT: - zig_unreachable(); - case EINVAL: - zig_unreachable(); - default: - close(fd); - return ErrorFileSystem; - } - } - break; - } - *out_file = fd; - return ErrorNone; -#endif -} - -Error os_file_read(OsFile file, void *ptr, size_t *len) { -#if defined(ZIG_OS_WINDOWS) - DWORD amt_read; - if (ReadFile(file, ptr, *len, &amt_read, nullptr) == 0) - return ErrorUnexpected; - *len = amt_read; - return ErrorNone; -#else - for (;;) { - ssize_t rc = read(file, ptr, *len); - if (rc == -1) { - switch (errno) { - case EINTR: - continue; - case EBADF: - zig_unreachable(); - case EFAULT: - zig_unreachable(); - case EISDIR: - return ErrorIsDir; - default: - return ErrorFileSystem; - } - } - *len = rc; - return ErrorNone; - } -#endif -} - -Error os_file_read_all(OsFile file, Buf *contents) { - Error err; - size_t index = 0; - for (;;) { - size_t amt = buf_len(contents) - index; - - if (amt < 4096) { - buf_resize(contents, buf_len(contents) + (4096 - amt)); - amt = buf_len(contents) - index; - } - - if ((err = os_file_read(file, buf_ptr(contents) + index, &amt))) - return err; - - if (amt == 0) { - buf_resize(contents, index); - return ErrorNone; - } - - index += amt; - } -} - -Error os_file_overwrite(OsFile file, Buf *contents) { -#if defined(ZIG_OS_WINDOWS) - if (SetFilePointer(file, 0, nullptr, FILE_BEGIN) == INVALID_SET_FILE_POINTER) - return ErrorFileSystem; - if (!SetEndOfFile(file)) - return ErrorFileSystem; - DWORD bytes_written; - if (!WriteFile(file, buf_ptr(contents), buf_len(contents), &bytes_written, nullptr)) - return ErrorFileSystem; - return ErrorNone; -#else - if (lseek(file, 0, SEEK_SET) == -1) - return ErrorUnexpectedSeekFailure; - if (ftruncate(file, 0) == -1) - return ErrorUnexpectedFileTruncationFailure; - for (;;) { - if (write(file, buf_ptr(contents), buf_len(contents)) == -1) { - switch (errno) { - case EINTR: - continue; - case EINVAL: - zig_unreachable(); - case EBADF: - zig_unreachable(); - case EFAULT: - zig_unreachable(); - case EDQUOT: - return ErrorDiskQuota; - case ENOSPC: - return ErrorDiskSpace; - case EFBIG: - return ErrorFileTooBig; - case EIO: - return ErrorFileSystem; - case EPERM: - return ErrorAccess; - default: - return ErrorUnexpectedWriteFailure; - } - } - return ErrorNone; - } -#endif -} - -void os_file_close(OsFile *file) { -#if defined(ZIG_OS_WINDOWS) - CloseHandle(*file); - *file = NULL; -#else - close(*file); - *file = -1; -#endif -} diff --git a/src/os.hpp b/src/os.hpp deleted file mode 100644 index 9792a42c453754adc60326e97903f66fe1bd34b1..0000000000000000000000000000000000000000 --- a/src/os.hpp +++ /dev/null @@ -1,166 +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_OS_HPP -#define ZIG_OS_HPP - -#include "list.hpp" -#include "buffer.hpp" -#include "error.hpp" -#include "zig_llvm.h" -#include "windows_sdk.h" - -#include -#include - -#if defined(__APPLE__) -#define ZIG_OS_DARWIN -#elif defined(_WIN32) -#define ZIG_OS_WINDOWS -#elif defined(__linux__) -#define ZIG_OS_LINUX -#elif defined(__FreeBSD__) -#define ZIG_OS_FREEBSD -#elif defined(__NetBSD__) -#define ZIG_OS_NETBSD -#elif defined(__DragonFly__) -#define ZIG_OS_DRAGONFLY -#else -#define ZIG_OS_UNKNOWN -#endif - -#if defined(__x86_64__) -#define ZIG_ARCH_X86_64 -#elif defined(__aarch64__) -#define ZIG_ARCH_ARM64 -#elif defined(__ARM_EABI__) -#define ZIG_ARCH_ARM -#else -#define ZIG_ARCH_UNKNOWN -#endif - -#if defined(ZIG_OS_WINDOWS) -#define ZIG_PRI_usize "I64u" -#define ZIG_PRI_i64 "I64d" -#define ZIG_PRI_u64 "I64u" -#define ZIG_PRI_llu "I64u" -#define ZIG_PRI_x64 "I64x" -#define OS_SEP "\\" -#define ZIG_OS_SEP_CHAR '\\' -#else -#define ZIG_PRI_usize "zu" -#define ZIG_PRI_i64 PRId64 -#define ZIG_PRI_u64 PRIu64 -#define ZIG_PRI_llu "llu" -#define ZIG_PRI_x64 PRIx64 -#define OS_SEP "/" -#define ZIG_OS_SEP_CHAR '/' -#endif - -enum TermColor { - TermColorRed, - TermColorGreen, - TermColorCyan, - TermColorWhite, - TermColorBold, - TermColorReset, -}; - -enum TerminationId { - TerminationIdClean, - TerminationIdSignaled, - TerminationIdStopped, - TerminationIdUnknown, -}; - -struct Termination { - TerminationId how; - int code; -}; - -#if defined(ZIG_OS_WINDOWS) -#define OsFile void * -#else -#define OsFile int -#endif - -struct OsTimeStamp { - int64_t sec; - int64_t nsec; -}; - -struct OsFileAttr { - OsTimeStamp mtime; - uint64_t size; - uint64_t inode; - uint32_t mode; -}; - -int os_init(void); - -void os_spawn_process(ZigList &args, Termination *term); -Error os_exec_process(ZigList &args, - Termination *term, Buf *out_stderr, Buf *out_stdout); -Error os_execv(const char *exe, const char **argv); - -void os_path_dirname(Buf *full_path, Buf *out_dirname); -void os_path_split(Buf *full_path, Buf *out_dirname, Buf *out_basename); -void os_path_extname(Buf *full_path, Buf *out_basename, Buf *out_extname); -void os_path_join(Buf *dirname, Buf *basename, Buf *out_full_path); -Error os_path_real(Buf *rel_path, Buf *out_abs_path); -Buf os_path_resolve(Buf **paths_ptr, size_t paths_len); -bool os_path_is_absolute(Buf *path); - -Error ATTRIBUTE_MUST_USE os_make_path(Buf *path); -Error ATTRIBUTE_MUST_USE os_make_dir(Buf *path); - -Error ATTRIBUTE_MUST_USE os_file_open_r(Buf *full_path, OsFile *out_file, OsFileAttr *attr); -Error ATTRIBUTE_MUST_USE os_file_open_w(Buf *full_path, OsFile *out_file, OsFileAttr *attr, uint32_t mode); -Error ATTRIBUTE_MUST_USE os_file_open_lock_rw(Buf *full_path, OsFile *out_file); -Error ATTRIBUTE_MUST_USE os_file_read(OsFile file, void *ptr, size_t *len); -Error ATTRIBUTE_MUST_USE os_file_read_all(OsFile file, Buf *contents); -Error ATTRIBUTE_MUST_USE os_file_overwrite(OsFile file, Buf *contents); -void os_file_close(OsFile *file); - -Error ATTRIBUTE_MUST_USE os_write_file(Buf *full_path, Buf *contents); -Error ATTRIBUTE_MUST_USE os_copy_file(Buf *src_path, Buf *dest_path); -Error ATTRIBUTE_MUST_USE os_update_file(Buf *src_path, Buf *dest_path); -Error ATTRIBUTE_MUST_USE os_dump_file(Buf *src_path, FILE *dest_file); - -Error ATTRIBUTE_MUST_USE os_fetch_file(FILE *file, Buf *out_contents); -Error ATTRIBUTE_MUST_USE os_fetch_file_path(Buf *full_path, Buf *out_contents); - -Error ATTRIBUTE_MUST_USE os_get_cwd(Buf *out_cwd); - -bool os_stderr_tty(void); -void os_stderr_set_color(TermColor color); - -Error os_delete_file(Buf *path); - -Error ATTRIBUTE_MUST_USE os_file_exists(Buf *full_path, bool *result); - -Error os_rename(Buf *src_path, Buf *dest_path); -OsTimeStamp os_timestamp_monotonic(void); -OsTimeStamp os_timestamp_calendar(void); - -bool os_is_sep(uint8_t c); - -Error ATTRIBUTE_MUST_USE os_self_exe_path(Buf *out_path); - -Error ATTRIBUTE_MUST_USE os_get_app_data_dir(Buf *out_path, const char *appname); - -Error ATTRIBUTE_MUST_USE os_self_exe_shared_libs(ZigList &paths); - -const size_t PATH_MAX_WIDE = 32767; - -struct PathSpace { - Array data; - size_t len; -}; - -PathSpace slice_to_prefixed_file_w(Slice path); -#endif diff --git a/src/parse_f128.c b/src/parse_f128.c deleted file mode 100644 index 9b5c287a3c015388a6e749fcc0a07379b3d2e58f..0000000000000000000000000000000000000000 --- a/src/parse_f128.c +++ /dev/null @@ -1,1084 +0,0 @@ -// Code ported from musl libc 8f12c4e110acb3bbbdc8abfb3a552c3ced718039 -// and then modified to use softfloat and to assume f128 for everything - -#include "parse_f128.h" -#include "softfloat.h" -#include -#include -#include -#include -#include -#include - -#define shcnt(f) ((f)->shcnt + ((f)->rpos - (f)->buf)) -#define shlim(f, lim) __shlim((f), (lim)) -#define shgetc(f) (((f)->rpos != (f)->shend) ? *(f)->rpos++ : __shgetc(f)) -#define shunget(f) ((f)->shlim>=0 ? (void)(f)->rpos-- : (void)0) - -#define sh_fromstring(f, s) \ - ((f)->buf = (f)->rpos = (void *)(s), (f)->rend = (void*)-1) - -#define LD_B1B_DIG 4 -#define LD_B1B_MAX 10384593, 717069655, 257060992, 658440191 -#define KMAX 2048 - -#define MASK (KMAX-1) - -#define CONCAT2(x,y) x ## y -#define CONCAT(x,y) CONCAT2(x,y) - -#define F_PERM 1 -#define F_NORD 4 -#define F_NOWR 8 -#define F_EOF 16 -#define F_ERR 32 -#define F_SVB 64 -#define F_APP 128 - -#define EOF (-1) - -#define LDBL_MANT_DIG 113 -#define LDBL_MIN_EXP (-16381) -#define LDBL_MAX_EXP 16384 - -#define LDBL_DIG 33 -#define LDBL_MIN_10_EXP (-4931) -#define LDBL_MAX_10_EXP 4932 - -#define DECIMAL_DIG 36 - - -#if __BYTE_ORDER == __LITTLE_ENDIAN -union ldshape { - float128_t f; - struct { - uint64_t lo; - uint32_t mid; - uint16_t top; - uint16_t se; - } i; - struct { - uint64_t lo; - uint64_t hi; - } i2; -}; -#elif __BYTE_ORDER == __BIG_ENDIAN -union ldshape { - float128_t f; - struct { - uint16_t se; - uint16_t top; - uint32_t mid; - uint64_t lo; - } i; - struct { - uint64_t hi; - uint64_t lo; - } i2; -}; -#error Unsupported endian -#endif - -struct MuslFILE { - unsigned flags; - unsigned char *rpos, *rend; - int (*close)(struct MuslFILE *); - unsigned char *wend, *wpos; - unsigned char *mustbezero_1; - unsigned char *wbase; - size_t (*read)(struct MuslFILE *, unsigned char *, size_t); - size_t (*write)(struct MuslFILE *, const unsigned char *, size_t); - off_t (*seek)(struct MuslFILE *, off_t, int); - unsigned char *buf; - size_t buf_size; - struct MuslFILE *prev, *next; - int fd; - int pipe_pid; - long lockcount; - int mode; - volatile int lock; - int lbf; - void *cookie; - off_t off; - char *getln_buf; - void *mustbezero_2; - unsigned char *shend; - off_t shlim, shcnt; - struct MuslFILE *prev_locked, *next_locked; - struct __locale_struct *locale; -}; - -static void __shlim(struct MuslFILE *f, off_t lim) -{ - f->shlim = lim; - f->shcnt = f->buf - f->rpos; - /* If lim is nonzero, rend must be a valid pointer. */ - if (lim && f->rend - f->rpos > lim) - f->shend = f->rpos + lim; - else - f->shend = f->rend; -} - -static int __toread(struct MuslFILE *f) -{ - f->mode |= f->mode-1; - if (f->wpos != f->wbase) f->write(f, 0, 0); - f->wpos = f->wbase = f->wend = 0; - if (f->flags & F_NORD) { - f->flags |= F_ERR; - return EOF; - } - f->rpos = f->rend = f->buf + f->buf_size; - return (f->flags & F_EOF) ? EOF : 0; -} - -static int __uflow(struct MuslFILE *f) -{ - unsigned char c; - if (!__toread(f) && f->read(f, &c, 1)==1) return c; - return EOF; -} - -static int __shgetc(struct MuslFILE *f) -{ - int c; - off_t cnt = shcnt(f); - if ((f->shlim && cnt >= f->shlim) || (c=__uflow(f)) < 0) { - f->shcnt = f->buf - f->rpos + cnt; - f->shend = f->rpos; - f->shlim = -1; - return EOF; - } - cnt++; - if (f->shlim && f->rend - f->rpos > f->shlim - cnt) - f->shend = f->rpos + (f->shlim - cnt); - else - f->shend = f->rend; - f->shcnt = f->buf - f->rpos + cnt; - if (f->rpos[-1] != c) f->rpos[-1] = c; - return c; -} - -static long long scanexp(struct MuslFILE *f, int pok) -{ - int c; - int x; - long long y; - int neg = 0; - - c = shgetc(f); - if (c=='+' || c=='-') { - neg = (c=='-'); - c = shgetc(f); - if (c-'0'>=10U && pok) shunget(f); - } - if (c-'0'>=10U && c!='_') { - shunget(f); - return LLONG_MIN; - } - for (x=0; ; c = shgetc(f)) { - if (c=='_') { - continue; - } else if (c-'0'<10U && x>16) | 1ULL<<48; - yhi = (uy.i2.hi & -1ULL>>16) | 1ULL<<48; - xlo = ux.i2.lo; - ylo = uy.i2.lo; - for (; ex > ey; ex--) { - hi = xhi - yhi; - lo = xlo - ylo; - if (xlo < ylo) - hi -= 1; - if (hi >> 63 == 0) { - if ((hi|lo) == 0) { - //return 0*x; - float128_t result; - f128M_mul(&zero, &x, &result); - return result; - } - xhi = 2*hi + (lo>>63); - xlo = 2*lo; - } else { - xhi = 2*xhi + (xlo>>63); - xlo = 2*xlo; - } - } - hi = xhi - yhi; - lo = xlo - ylo; - if (xlo < ylo) - hi -= 1; - if (hi >> 63 == 0) { - if ((hi|lo) == 0) { - //return 0*x; - float128_t result; - f128M_mul(&zero, &x, &result); - return result; - } - xhi = hi; - xlo = lo; - } - for (; xhi >> 48 == 0; xhi = 2*xhi + (xlo>>63), xlo = 2*xlo, ex--); - ux.i2.hi = xhi; - ux.i2.lo = xlo; - - /* scale result */ - if (ex <= 0) { - ux.i.se = (ex+120)|sx; - //ux.f *= 0x1p-120f; - mul_eq_f128_float(&ux.f, 0x1p-120f); - } else - ux.i.se = ex|sx; - return ux.f; -} - -static float128_t int_mul_f128_cast_u32(int sign, uint32_t x0) { - float128_t x0_f128; - ui32_to_f128M(x0, &x0_f128); - float128_t sign_f128; - i32_to_f128M(sign, &sign_f128); - float128_t result; - f128M_mul(&sign_f128, &x0_f128, &result); - return result; -} - -static float128_t triple_divide(int sign, uint32_t x0, int p10s) { - float128_t part1 = int_mul_f128_cast_u32(sign, x0); - float128_t p10s_f128; - i32_to_f128M(p10s, &p10s_f128); - float128_t result; - f128M_div(&part1, &p10s_f128, &result); - return result; -} - -static float128_t triple_multiply(int sign, uint32_t x0, int p10s) { - float128_t part1 = int_mul_f128_cast_u32(sign, x0); - float128_t p10s_f128; - i32_to_f128M(p10s, &p10s_f128); - float128_t result; - f128M_mul(&part1, &p10s_f128, &result); - return result; -} - -static void mul_eq_f128_int(float128_t *y, int sign) { - float128_t sign_f128; - i32_to_f128M(sign, &sign_f128); - float128_t new_value; - f128M_mul(y, &sign_f128, &new_value); - *y = new_value; -} - -static float128_t make_f128(uint64_t hi, uint64_t lo) { - union ldshape ux; - ux.i2.hi = hi; - ux.i2.lo = lo; - return ux.f; -} - -static void mul_eq_f128_f128(float128_t *a, float128_t b) { - float128_t new_value; - f128M_mul(a, &b, &new_value); - *a = new_value; -} - -static void add_eq_f128_dbl(float128_t *a, double b) { - float64_t b_f64; - memcpy(&b_f64, &b, sizeof(double)); - - float128_t b_f128; - f64_to_f128M(b_f64, &b_f128); - - float128_t new_value; - f128M_add(a, &b_f128, &new_value); - *a = new_value; -} - -static float128_t scalbnf128(float128_t x, int n) -{ - union ldshape u; - - if (n > 16383) { - //x *= 0x1p16383q; - mul_eq_f128_f128(&x, make_f128(0x7ffe000000000000, 0x0000000000000000)); - n -= 16383; - if (n > 16383) { - //x *= 0x1p16383q; - mul_eq_f128_f128(&x, make_f128(0x7ffe000000000000, 0x0000000000000000)); - n -= 16383; - if (n > 16383) - n = 16383; - } - } else if (n < -16382) { - //x *= 0x1p-16382q * 0x1p113q; - { - float128_t mul_result; - float128_t a = make_f128(0x0001000000000000, 0x0000000000000000); - float128_t b = make_f128(0x4070000000000000, 0x0000000000000000); - f128M_mul(&a, &b, &mul_result); - mul_eq_f128_f128(&x, mul_result); - } - n += 16382 - 113; - if (n < -16382) { - //x *= 0x1p-16382q * 0x1p113q; - { - float128_t mul_result; - float128_t a = make_f128(0x0001000000000000, 0x0000000000000000); - float128_t b = make_f128(0x4070000000000000, 0x0000000000000000); - f128M_mul(&a, &b, &mul_result); - mul_eq_f128_f128(&x, mul_result); - } - n += 16382 - 113; - if (n < -16382) - n = -16382; - } - } - //u.f = 1.0; - ui32_to_f128M(1, &u.f); - u.i.se = 0x3fff + n; - mul_eq_f128_f128(&x, u.f); - return x; -} - -static float128_t fabsf128(float128_t x) -{ - union ldshape u = {x}; - - u.i.se &= 0x7fff; - return u.f; -} - -static float128_t decfloat(struct MuslFILE *f, int c, int bits, int emin, int sign, int pok) -{ - uint32_t x[KMAX]; - static const uint32_t th[] = { LD_B1B_MAX }; - int i, j, k, a, z; - long long lrp=0, dc=0; - long long e10=0; - int lnz = 0; - int gotdig = 0, gotrad = 0; - int rp; - int e2; - int emax = -emin-bits+3; - int denormal = 0; - float128_t y; - float128_t zero; - ui32_to_f128M(0, &zero); - float128_t frac=zero; - float128_t bias=zero; - static const int p10s[] = { 10, 100, 1000, 10000, - 100000, 1000000, 10000000, 100000000 }; - - j=0; - k=0; - - /* Don't let leading zeros/underscores consume buffer space */ - for (; ; c = shgetc(f)) { - if (c=='_') { - continue; - } else if (c=='0') { - gotdig=1; - } else { - break; - } - } - - if (c=='.') { - gotrad = 1; - for (c = shgetc(f); ; c = shgetc(f)) { - if (c == '_') { - continue; - } else if (c=='0') { - gotdig=1; - lrp--; - } else { - break; - } - } - } - - x[0] = 0; - for (; c-'0'<10U || c=='.' || c=='_'; c = shgetc(f)) { - if (c == '_') { - continue; - } else if (c == '.') { - if (gotrad) break; - gotrad = 1; - lrp = dc; - } else if (k < KMAX-3) { - dc++; - if (c!='0') lnz = dc; - if (j) x[k] = x[k]*10 + c-'0'; - else x[k] = c-'0'; - if (++j==9) { - k++; - j=0; - } - gotdig=1; - } else { - dc++; - if (c!='0') { - lnz = (KMAX-4)*9; - x[KMAX-4] |= 1; - } - } - } - if (!gotrad) lrp=dc; - - if (gotdig && (c|32)=='e') { - e10 = scanexp(f, pok); - if (e10 == LLONG_MIN) { - if (pok) { - shunget(f); - } else { - shlim(f, 0); - return zero; - } - e10 = 0; - } - lrp += e10; - } else if (c>=0) { - shunget(f); - } - if (!gotdig) { - errno = EINVAL; - shlim(f, 0); - return zero; - } - - /* Handle zero specially to avoid nasty special cases later */ - if (!x[0]) { - //return sign * 0.0; - return dbl_to_f128(sign * 0.0); - } - - /* Optimize small integers (w/no exponent) and over/under-flow */ - if (lrp==dc && dc<10 && (bits>30 || x[0]>>bits==0)) { - //return sign * (float128_t)x[0]; - float128_t sign_f128; - i32_to_f128M(sign, &sign_f128); - float128_t x0_f128; - ui32_to_f128M(x[0], &x0_f128); - float128_t result; - f128M_mul(&sign_f128, &x0_f128, &result); - return result; - } - if (lrp > -emin/2) { - errno = ERANGE; - //return sign * LDBL_MAX * LDBL_MAX; - return zero; - } - if (lrp < emin-2*LDBL_MANT_DIG) { - errno = ERANGE; - //return sign * LDBL_MIN * LDBL_MIN; - return zero; - } - - /* Align incomplete final B1B digit */ - if (j) { - for (; j<9; j++) x[k]*=10; - k++; - j=0; - } - - a = 0; - z = k; - e2 = 0; - rp = lrp; - - /* Optimize small to mid-size integers (even in exp. notation) */ - if (lnz<9 && lnz<=rp && rp < 18) { - if (rp == 9) { - //return sign * (float128_t)(x[0]); - return int_mul_f128_cast_u32(sign, x[0]); - } - if (rp < 9) { - //return sign * (float128_t)(x[0]) / p10s[8-rp]; - return triple_divide(sign, x[0], p10s[8-rp]); - } - int bitlim = bits-3*(int)(rp-9); - if (bitlim>30 || x[0]>>bitlim==0) - //return sign * (float128_t)(x[0]) * p10s[rp-10]; - return triple_multiply(sign, x[0], p10s[rp-10]); - } - - /* Drop trailing zeros */ - for (; !x[z-1]; z--); - - /* Align radix point to B1B digit boundary */ - if (rp % 9) { - int rpm9 = rp>=0 ? rp%9 : rp%9+9; - int p10 = p10s[8-rpm9]; - uint32_t carry = 0; - for (k=a; k!=z; k++) { - uint32_t tmp = x[k] % p10; - x[k] = x[k]/p10 + carry; - carry = 1000000000/p10 * tmp; - if (k==a && !x[k]) { - a = (a+1 & MASK); - rp -= 9; - } - } - if (carry) x[z++] = carry; - rp += 9-rpm9; - } - - /* Upscale until desired number of bits are left of radix point */ - while (rp < 9*LD_B1B_DIG || (rp == 9*LD_B1B_DIG && x[a] 1000000000) { - carry = tmp / 1000000000; - x[k] = tmp % 1000000000; - } else { - carry = 0; - x[k] = tmp; - } - if (k==(z-1 & MASK) && k!=a && !x[k]) z = k; - if (k==a) break; - } - if (carry) { - rp += 9; - a = (a-1 & MASK); - if (a == z) { - z = (z-1 & MASK); - x[z-1 & MASK] |= x[z]; - } - x[a] = carry; - } - } - - /* Downscale until exactly number of bits are left of radix point */ - for (;;) { - uint32_t carry = 0; - int sh = 1; - for (i=0; i th[i]) break; - } - if (i==LD_B1B_DIG && rp==9*LD_B1B_DIG) break; - /* FIXME: find a way to compute optimal sh */ - if (rp > 9+9*LD_B1B_DIG) sh = 9; - e2 += sh; - for (k=a; k!=z; k=(k+1 & MASK)) { - uint32_t tmp = x[k] & (1<>sh) + carry; - carry = (1000000000>>sh) * tmp; - if (k==a && !x[k]) { - a = (a+1 & MASK); - i--; - rp -= 9; - } - } - if (carry) { - if ((z+1 & MASK) != a) { - x[z] = carry; - z = (z+1 & MASK); - } else x[z-1 & MASK] |= 1; - } - } - - /* Assemble desired bits into floating point variable */ - for (y=zero,i=0; i LDBL_MANT_DIG+e2-emin) { - bits = LDBL_MANT_DIG+e2-emin; - if (bits<0) bits=0; - denormal = 1; - } - - /* Calculate bias term to force rounding, move out lower bits */ - if (bits < LDBL_MANT_DIG) { - bias = copysignf128(dbl_to_f128(scalbn(1, 2*LDBL_MANT_DIG-bits-1)), y); - frac = fmodf128(y, dbl_to_f128(scalbn(1, LDBL_MANT_DIG-bits))); - //y -= frac; - { - float128_t new_value; - f128M_sub(&y, &frac, &new_value); - y = new_value; - } - //y += bias; - { - float128_t new_value; - f128M_add(&y, &frac, &new_value); - y = new_value; - } - } - - /* Process tail of decimal input so it can affect rounding */ - if ((a+i & MASK) != z) { - uint32_t t = x[a+i & MASK]; - if (t < 500000000 && (t || (a+i+1 & MASK) != z)) { - //frac += 0.25*sign; - add_eq_f128_dbl(&frac, 0.25*sign); - } else if (t > 500000000) { - //frac += 0.75*sign; - add_eq_f128_dbl(&frac, 0.75*sign); - } else if (t == 500000000) { - if ((a+i+1 & MASK) == z) { - //frac += 0.5*sign; - add_eq_f128_dbl(&frac, 0.5*sign); - } else { - //frac += 0.75*sign; - add_eq_f128_dbl(&frac, 0.75*sign); - } - } - //if (LDBL_MANT_DIG-bits >= 2 && !fmodf128(frac, 1)) - if (LDBL_MANT_DIG-bits >= 2) { - float128_t one; - ui32_to_f128M(1, &one); - float128_t mod_result = fmodf128(frac, one); - if (f128M_eq(&mod_result, &zero)) { - //frac++; - add_eq_f128_dbl(&frac, 1.0); - } - } - } - - //y += frac; - { - float128_t new_value; - f128M_add(&y, &frac, &new_value); - y = new_value; - } - //y -= bias; - { - float128_t new_value; - f128M_sub(&y, &bias, &new_value); - y = new_value; - } - - if ((e2+LDBL_MANT_DIG & INT_MAX) > emax-5) { - //if (fabsf128(y) >= 0x1p113) - float128_t abs_y = fabsf128(y); - float128_t mant_f128 = make_f128(0x4070000000000000, 0x0000000000000000); - if (!f128M_lt(&abs_y, &mant_f128)) { - if (denormal && bits==LDBL_MANT_DIG+e2-emin) - denormal = 0; - //y *= 0.5; - { - float128_t point_5 = dbl_to_f128(0.5); - float128_t new_value; - f128M_mul(&y, &point_5, &new_value); - y = new_value; - } - - e2++; - } - if (e2+LDBL_MANT_DIG>emax || (denormal && !f128M_eq(&frac, &zero))) - errno = ERANGE; - } - - return scalbnf128(y, e2); -} - -static float128_t hexfloat(struct MuslFILE *f, int bits, int emin, int sign, int pok) -{ - float128_t zero; - ui32_to_f128M(0, &zero); - float128_t one; - ui32_to_f128M(1, &one); - float128_t sixteen; - ui32_to_f128M(16, &sixteen); - float128_t point_5 = dbl_to_f128(0.5); - - uint32_t x = 0; - float128_t y = zero; - float128_t scale = one; - float128_t bias = zero; - int gottail = 0, gotrad = 0, gotdig = 0; - long long rp = 0; - long long dc = 0; - long long e2 = 0; - int d; - int c; - - c = shgetc(f); - - /* Skip leading zeros/underscores */ - for (; c=='0' || c=='_'; c = shgetc(f)) gotdig = 1; - - if (c=='.') { - gotrad = 1; - c = shgetc(f); - /* Count zeros after the radix point before significand */ - for (rp=0; ; c = shgetc(f)) { - if (c == '_') { - continue; - } else if (c == '0') { - gotdig = 1; - rp--; - } else { - break; - } - } - } - - for (; c-'0'<10U || (c|32)-'a'<6U || c=='.' || c=='_'; c = shgetc(f)) { - if (c=='_') { - continue; - } else if (c=='.') { - if (gotrad) break; - rp = dc; - gotrad = 1; - } else { - gotdig = 1; - if (c > '9') d = (c|32)+10-'a'; - else d = c-'0'; - if (dc<8) { - x = x*16 + d; - } else if (dc < LDBL_MANT_DIG/4+1) { - //y += d*(scale/=16); - { - float128_t divided; - f128M_div(&scale, &sixteen, ÷d); - scale = divided; - float128_t d_f128; - i32_to_f128M(d, &d_f128); - float128_t add_op; - f128M_mul(&d_f128, &scale, &add_op); - float128_t new_y; - f128M_add(&y, &add_op, &new_y); - y = new_y; - } - } else if (d && !gottail) { - //y += 0.5*scale; - { - float128_t add_op; - f128M_mul(&point_5, &scale, &add_op); - float128_t new_y; - f128M_add(&y, &add_op, &new_y); - y = new_y; - } - gottail = 1; - } - dc++; - } - } - if (!gotdig) { - shunget(f); - if (pok) { - shunget(f); - if (gotrad) shunget(f); - } else { - shlim(f, 0); - } - //return sign * 0.0; - return dbl_to_f128(sign * 0.0); - } - if (!gotrad) rp = dc; - while (dc<8) x *= 16, dc++; - if ((c|32)=='p') { - e2 = scanexp(f, pok); - if (e2 == LLONG_MIN) { - if (pok) { - shunget(f); - } else { - shlim(f, 0); - return zero; - } - e2 = 0; - } - } else { - shunget(f); - } - e2 += 4*rp - 32; - - if (!x) { - //return sign * 0.0; - return dbl_to_f128(sign * 0.0); - } - if (e2 > -emin) { - errno = ERANGE; - //return sign * LDBL_MAX * LDBL_MAX; - return zero; - } - if (e2 < emin-2*LDBL_MANT_DIG) { - errno = ERANGE; - //return sign * LDBL_MIN * LDBL_MIN; - return zero; - } - - while (x < 0x80000000) { - //if (y>=0.5) - if (!f128M_lt(&y, &point_5)) { - x += x + 1; - //y += y - 1; - { - float128_t minus_one; - f128M_sub(&y, &one, &minus_one); - float128_t new_y; - f128M_add(&y, &minus_one, &new_y); - y = new_y; - } - } else { - x += x; - //y += y; - { - float128_t new_y; - f128M_add(&y, &y, &new_y); - y = new_y; - } - } - e2--; - } - - if (bits > 32+e2-emin) { - bits = 32+e2-emin; - if (bits<0) bits=0; - } - - if (bits < LDBL_MANT_DIG) { - float128_t sign_f128; - i32_to_f128M(sign, &sign_f128); - bias = copysignf128(dbl_to_f128(scalbn(1, 32+LDBL_MANT_DIG-bits-1)), sign_f128); - } - - //if (bits<32 && y && !(x&1)) x++, y=0; - if (bits<32 && !f128M_eq(&y, &zero) && !(x&1)) x++, y=zero; - - //y = bias + sign*(float128_t)x + sign*y; - { - float128_t x_f128; - ui32_to_f128M(x, &x_f128); - float128_t sign_f128; - i32_to_f128M(sign, &sign_f128); - float128_t sign_mul_x; - f128M_mul(&sign_f128, &x_f128, &sign_mul_x); - float128_t sign_mul_y; - f128M_mul(&sign_f128, &y, &sign_mul_y); - float128_t bias_op; - f128M_add(&bias, &sign_mul_x, &bias_op); - float128_t new_y; - f128M_add(&bias_op, &sign_mul_y, &new_y); - y = new_y; - } - //y -= bias; - { - float128_t new_y; - f128M_sub(&y, &bias, &new_y); - y = new_y; - } - - if (f128M_eq(&y, &zero)) errno = ERANGE; - - return scalbnf128(y, e2); -} - -static int isspace(int c) -{ - return c == ' ' || (unsigned)c-'\t' < 5; -} - -static inline float128_t makeInf128() { - union ldshape ux; - ux.i2.hi = 0x7fff000000000000UL; - ux.i2.lo = 0x0UL; - return ux.f; -} - -static inline float128_t makeNaN128() { - uint64_t rand = 0UL; - union ldshape ux; - ux.i2.hi = 0x7fff000000000000UL | (rand & 0xffffffffffffUL); - ux.i2.lo = 0x0UL; - return ux.f; -} - -float128_t __floatscan(struct MuslFILE *f, int prec, int pok) -{ - int sign = 1; - size_t i; - int bits = LDBL_MANT_DIG; - int emin = LDBL_MIN_EXP-bits; - int c; - - while (isspace((c=shgetc(f)))); - - if (c=='+' || c=='-') { - sign -= 2*(c=='-'); - c = shgetc(f); - } - - for (i=0; i<8 && (c|32)=="infinity"[i]; i++) - if (i<7) c = shgetc(f); - if (i==3 || i==8 || (i>3 && pok)) { - if (i!=8) { - shunget(f); - if (pok) for (; i>3; i--) shunget(f); - } - //return sign * INFINITY; - float128_t sign_f128; - i32_to_f128M(sign, &sign_f128); - float128_t infinity_f128 = makeInf128(); - float128_t result; - f128M_mul(&sign_f128, &infinity_f128, &result); - return result; - } - if (!i) for (i=0; i<3 && (c|32)=="nan"[i]; i++) - if (i<2) c = shgetc(f); - if (i==3) { - if (shgetc(f) != '(') { - shunget(f); - return makeNaN128(); - } - for (i=1; ; i++) { - c = shgetc(f); - if (c-'0'<10U || c-'A'<26U || c-'a'<26U || c=='_') - continue; - if (c==')') return makeNaN128(); - shunget(f); - if (!pok) { - errno = EINVAL; - shlim(f, 0); - float128_t zero; - ui32_to_f128M(0, &zero); - return zero; - } - while (i--) shunget(f); - return makeNaN128(); - } - return makeNaN128(); - } - - if (i) { - shunget(f); - errno = EINVAL; - shlim(f, 0); - float128_t zero; - ui32_to_f128M(0, &zero); - return zero; - } - - if (c=='0') { - c = shgetc(f); - if ((c|32) == 'x') - return hexfloat(f, bits, emin, sign, pok); - shunget(f); - c = '0'; - } - - return decfloat(f, c, bits, emin, sign, pok); -} - -float128_t parse_f128(const char *s, char **p) { - struct MuslFILE f; - sh_fromstring(&f, s); - shlim(&f, 0); - float128_t y = __floatscan(&f, 2, 1); - off_t cnt = shcnt(&f); - if (p) *p = cnt ? (char *)s + cnt : (char *)s; - return y; -} diff --git a/src/parse_f128.h b/src/parse_f128.h deleted file mode 100644 index 82cdf6c9a0bfcc38a6e91f2e2da29df58cb757a5..0000000000000000000000000000000000000000 --- a/src/parse_f128.h +++ /dev/null @@ -1,21 +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_PARSE_F128_H -#define ZIG_PARSE_F128_H - -#include "softfloat_types.h" - -#ifdef __cplusplus -#define ZIG_EXTERN_C extern "C" -#else -#define ZIG_EXTERN_C -#endif - -ZIG_EXTERN_C float128_t parse_f128(const char *s, char **p); - -#endif diff --git a/src/parser.cpp b/src/parser.cpp deleted file mode 100644 index 1253baf9ea5104ee655e42d061927ccec3b5bc61..0000000000000000000000000000000000000000 --- a/src/parser.cpp +++ /dev/null @@ -1,3216 +0,0 @@ -/* - * Copyright (c) 2015 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#include "parser.hpp" -#include "errmsg.hpp" -#include "analyze.hpp" - -#include -#include -#include -#include - -struct ParseContext { - Buf *buf; - size_t current_token; - ZigList *tokens; - ZigType *owner; - ErrColor err_color; -}; - -struct PtrPayload { - Token *asterisk; - Token *payload; -}; - -struct PtrIndexPayload { - Token *asterisk; - Token *payload; - Token *index; -}; - -static AstNode *ast_parse_root(ParseContext *pc); -static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc); -static AstNode *ast_parse_test_decl(ParseContext *pc); -static AstNode *ast_parse_top_level_comptime(ParseContext *pc); -static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod, Buf *doc_comments); -static AstNode *ast_parse_fn_proto(ParseContext *pc); -static AstNode *ast_parse_var_decl(ParseContext *pc); -static AstNode *ast_parse_container_field(ParseContext *pc); -static AstNode *ast_parse_statement(ParseContext *pc); -static AstNode *ast_parse_if_statement(ParseContext *pc); -static AstNode *ast_parse_labeled_statement(ParseContext *pc); -static AstNode *ast_parse_loop_statement(ParseContext *pc); -static AstNode *ast_parse_for_statement(ParseContext *pc); -static AstNode *ast_parse_while_statement(ParseContext *pc); -static AstNode *ast_parse_block_expr_statement(ParseContext *pc); -static AstNode *ast_parse_block_expr(ParseContext *pc); -static AstNode *ast_parse_assign_expr(ParseContext *pc); -static AstNode *ast_parse_expr(ParseContext *pc); -static AstNode *ast_parse_bool_or_expr(ParseContext *pc); -static AstNode *ast_parse_bool_and_expr(ParseContext *pc); -static AstNode *ast_parse_compare_expr(ParseContext *pc); -static AstNode *ast_parse_bitwise_expr(ParseContext *pc); -static AstNode *ast_parse_bit_shift_expr(ParseContext *pc); -static AstNode *ast_parse_addition_expr(ParseContext *pc); -static AstNode *ast_parse_multiply_expr(ParseContext *pc); -static AstNode *ast_parse_prefix_expr(ParseContext *pc); -static AstNode *ast_parse_primary_expr(ParseContext *pc); -static AstNode *ast_parse_if_expr(ParseContext *pc); -static AstNode *ast_parse_block(ParseContext *pc); -static AstNode *ast_parse_loop_expr(ParseContext *pc); -static AstNode *ast_parse_for_expr(ParseContext *pc); -static AstNode *ast_parse_while_expr(ParseContext *pc); -static AstNode *ast_parse_curly_suffix_expr(ParseContext *pc); -static AstNode *ast_parse_init_list(ParseContext *pc); -static AstNode *ast_parse_type_expr(ParseContext *pc); -static AstNode *ast_parse_error_union_expr(ParseContext *pc); -static AstNode *ast_parse_suffix_expr(ParseContext *pc); -static AstNode *ast_parse_primary_type_expr(ParseContext *pc); -static AstNode *ast_parse_container_decl(ParseContext *pc); -static AstNode *ast_parse_error_set_decl(ParseContext *pc); -static AstNode *ast_parse_grouped_expr(ParseContext *pc); -static AstNode *ast_parse_if_type_expr(ParseContext *pc); -static AstNode *ast_parse_labeled_type_expr(ParseContext *pc); -static AstNode *ast_parse_loop_type_expr(ParseContext *pc); -static AstNode *ast_parse_for_type_expr(ParseContext *pc); -static AstNode *ast_parse_while_type_expr(ParseContext *pc); -static AstNode *ast_parse_switch_expr(ParseContext *pc); -static AstNode *ast_parse_asm_expr(ParseContext *pc); -static AstNode *ast_parse_anon_lit(ParseContext *pc); -static AstNode *ast_parse_asm_output(ParseContext *pc); -static AsmOutput *ast_parse_asm_output_item(ParseContext *pc); -static AstNode *ast_parse_asm_input(ParseContext *pc); -static AsmInput *ast_parse_asm_input_item(ParseContext *pc); -static AstNode *ast_parse_asm_clobbers(ParseContext *pc); -static Token *ast_parse_break_label(ParseContext *pc); -static Token *ast_parse_block_label(ParseContext *pc); -static AstNode *ast_parse_field_init(ParseContext *pc); -static AstNode *ast_parse_while_continue_expr(ParseContext *pc); -static AstNode *ast_parse_link_section(ParseContext *pc); -static AstNode *ast_parse_callconv(ParseContext *pc); -static AstNode *ast_parse_param_decl(ParseContext *pc); -static AstNode *ast_parse_param_type(ParseContext *pc); -static AstNode *ast_parse_if_prefix(ParseContext *pc); -static AstNode *ast_parse_while_prefix(ParseContext *pc); -static AstNode *ast_parse_for_prefix(ParseContext *pc); -static Token *ast_parse_payload(ParseContext *pc); -static Optional ast_parse_ptr_payload(ParseContext *pc); -static Optional ast_parse_ptr_index_payload(ParseContext *pc); -static AstNode *ast_parse_switch_prong(ParseContext *pc); -static AstNode *ast_parse_switch_case(ParseContext *pc); -static AstNode *ast_parse_switch_item(ParseContext *pc); -static AstNode *ast_parse_assign_op(ParseContext *pc); -static AstNode *ast_parse_compare_op(ParseContext *pc); -static AstNode *ast_parse_bitwise_op(ParseContext *pc); -static AstNode *ast_parse_bit_shift_op(ParseContext *pc); -static AstNode *ast_parse_addition_op(ParseContext *pc); -static AstNode *ast_parse_multiply_op(ParseContext *pc); -static AstNode *ast_parse_prefix_op(ParseContext *pc); -static AstNode *ast_parse_prefix_type_op(ParseContext *pc); -static AstNode *ast_parse_suffix_op(ParseContext *pc); -static AstNode *ast_parse_fn_call_arguments(ParseContext *pc); -static AstNode *ast_parse_array_type_start(ParseContext *pc); -static AstNode *ast_parse_ptr_type_start(ParseContext *pc); -static AstNode *ast_parse_container_decl_auto(ParseContext *pc); -static AstNode *ast_parse_container_decl_type(ParseContext *pc); -static AstNode *ast_parse_byte_align(ParseContext *pc); - -ATTRIBUTE_PRINTF(3, 4) -ATTRIBUTE_NORETURN -static void ast_error(ParseContext *pc, Token *token, const char *format, ...) { - va_list ap; - va_start(ap, format); - Buf *msg = buf_vprintf(format, ap); - va_end(ap); - - - ErrorMsg *err = err_msg_create_with_line(pc->owner->data.structure.root_struct->path, - token->start_line, token->start_column, - pc->owner->data.structure.root_struct->source_code, - pc->owner->data.structure.root_struct->line_offsets, msg); - err->line_start = token->start_line; - err->column_start = token->start_column; - - print_err_msg(err, pc->err_color); - exit(EXIT_FAILURE); -} - -ATTRIBUTE_NORETURN -static void ast_invalid_token_error(ParseContext *pc, Token *token) { - ast_error(pc, token, "invalid token: '%s'", token_name(token->id)); -} - -static AstNode *ast_create_node_no_line_info(ParseContext *pc, NodeType type) { - AstNode *node = heap::c_allocator.create(); - node->type = type; - node->owner = pc->owner; - return node; -} - -static AstNode *ast_create_node(ParseContext *pc, NodeType type, Token *first_token) { - assert(first_token); - AstNode *node = ast_create_node_no_line_info(pc, type); - node->line = first_token->start_line; - node->column = first_token->start_column; - return node; -} - -static AstNode *ast_create_node_copy_line_info(ParseContext *pc, NodeType type, AstNode *from) { - assert(from); - AstNode *node = ast_create_node_no_line_info(pc, type); - node->line = from->line; - node->column = from->column; - return node; -} - -static Token *peek_token_i(ParseContext *pc, size_t i) { - return &pc->tokens->at(pc->current_token + i); -} - -static Token *peek_token(ParseContext *pc) { - return peek_token_i(pc, 0); -} - -static Token *eat_token(ParseContext *pc) { - Token *res = peek_token(pc); - pc->current_token += 1; - return res; -} - -static Token *eat_token_if(ParseContext *pc, TokenId id) { - Token *res = peek_token(pc); - if (res->id == id) - return eat_token(pc); - - return nullptr; -} - -static Token *expect_token(ParseContext *pc, TokenId id) { - Token *res = eat_token(pc); - if (res->id != id) - ast_error(pc, res, "expected token '%s', found '%s'", token_name(id), token_name(res->id)); - - return res; -} - -static void put_back_token(ParseContext *pc) { - pc->current_token -= 1; -} - -static Buf *token_buf(Token *token) { - if (token == nullptr) - return nullptr; - assert(token->id == TokenIdStringLiteral || token->id == TokenIdMultilineStringLiteral || token->id == TokenIdSymbol); - return &token->data.str_lit.str; -} - -static BigInt *token_bigint(Token *token) { - assert(token->id == TokenIdIntLiteral); - return &token->data.int_lit.bigint; -} - -static AstNode *token_symbol(ParseContext *pc, Token *token) { - assert(token->id == TokenIdSymbol); - AstNode *res = ast_create_node(pc, NodeTypeSymbol, token); - res->data.symbol_expr.symbol = token_buf(token); - return res; -} - -// (Rule SEP)* Rule? -template -static ZigList ast_parse_list(ParseContext *pc, TokenId sep, T *(*parser)(ParseContext*)) { - ZigList res = {}; - while (true) { - T *curr = parser(pc); - if (curr == nullptr) - break; - - res.append(curr); - if (eat_token_if(pc, sep) == nullptr) - break; - } - - return res; -} - -static AstNode *ast_expect(ParseContext *pc, AstNode *(*parser)(ParseContext*)) { - AstNode *res = parser(pc); - if (res == nullptr) - ast_invalid_token_error(pc, peek_token(pc)); - return res; -} - -enum BinOpChain { - BinOpChainOnce, - BinOpChainInf, -}; - -// Op* Child -static AstNode *ast_parse_prefix_op_expr( - ParseContext *pc, - AstNode *(*op_parser)(ParseContext *), - AstNode *(*child_parser)(ParseContext *) -) { - AstNode *res = nullptr; - AstNode **right = &res; - while (true) { - AstNode *prefix = op_parser(pc); - if (prefix == nullptr) - break; - - *right = prefix; - switch (prefix->type) { - case NodeTypePrefixOpExpr: - right = &prefix->data.prefix_op_expr.primary_expr; - break; - case NodeTypeReturnExpr: - right = &prefix->data.return_expr.expr; - break; - case NodeTypeAwaitExpr: - right = &prefix->data.await_expr.expr; - break; - case NodeTypeAnyFrameType: - right = &prefix->data.anyframe_type.payload_type; - break; - case NodeTypeArrayType: - right = &prefix->data.array_type.child_type; - break; - case NodeTypeInferredArrayType: - right = &prefix->data.inferred_array_type.child_type; - break; - case NodeTypePointerType: { - // We might get two pointers from *_ptr_type_start - AstNode *child = prefix->data.pointer_type.op_expr; - if (child == nullptr) - child = prefix; - right = &child->data.pointer_type.op_expr; - break; - } - default: - zig_unreachable(); - } - } - - // If we have already consumed a token, and determined that - // this node is a prefix op, then we expect that the node has - // a child. - if (res != nullptr) { - *right = ast_expect(pc, child_parser); - } else { - // Otherwise, if we didn't consume a token, then we can return - // null, if the child expr did. - *right = child_parser(pc); - if (*right == nullptr) - return nullptr; - } - - return res; -} - -// Child (Op Child)(*/?) -static AstNode *ast_parse_bin_op_expr( - ParseContext *pc, - BinOpChain chain, - AstNode *(*op_parse)(ParseContext*), - AstNode *(*child_parse)(ParseContext*) -) { - AstNode *res = child_parse(pc); - if (res == nullptr) - return nullptr; - - do { - AstNode *op = op_parse(pc); - if (op == nullptr) - break; - - AstNode *left = res; - AstNode *right = ast_expect(pc, child_parse); - res = op; - switch (op->type) { - case NodeTypeBinOpExpr: - op->data.bin_op_expr.op1 = left; - op->data.bin_op_expr.op2 = right; - break; - case NodeTypeCatchExpr: - op->data.unwrap_err_expr.op1 = left; - op->data.unwrap_err_expr.op2 = right; - break; - default: - zig_unreachable(); - } - } while (chain == BinOpChainInf); - - return res; -} - -// IfPrefix Body (KEYWORD_else Payload? Body)? -static AstNode *ast_parse_if_expr_helper(ParseContext *pc, AstNode *(*body_parser)(ParseContext*)) { - AstNode *res = ast_parse_if_prefix(pc); - if (res == nullptr) - return nullptr; - - AstNode *body = ast_expect(pc, body_parser); - Token *err_payload = nullptr; - AstNode *else_body = nullptr; - if (eat_token_if(pc, TokenIdKeywordElse) != nullptr) { - err_payload = ast_parse_payload(pc); - else_body = ast_expect(pc, body_parser); - } - - assert(res->type == NodeTypeIfOptional); - if (err_payload != nullptr) { - AstNodeTestExpr old = res->data.test_expr; - res->type = NodeTypeIfErrorExpr; - res->data.if_err_expr.target_node = old.target_node; - res->data.if_err_expr.var_is_ptr = old.var_is_ptr; - res->data.if_err_expr.var_symbol = old.var_symbol; - res->data.if_err_expr.then_node = body; - res->data.if_err_expr.err_symbol = token_buf(err_payload); - res->data.if_err_expr.else_node = else_body; - return res; - } - - if (res->data.test_expr.var_symbol != nullptr) { - res->data.test_expr.then_node = body; - res->data.test_expr.else_node = else_body; - return res; - } - - AstNodeTestExpr old = res->data.test_expr; - res->type = NodeTypeIfBoolExpr; - res->data.if_bool_expr.condition = old.target_node; - res->data.if_bool_expr.then_block = body; - res->data.if_bool_expr.else_node = else_body; - return res; -} - -// KEYWORD_inline? (ForLoop / WhileLoop) -static AstNode *ast_parse_loop_expr_helper( - ParseContext *pc, - AstNode *(*for_parser)(ParseContext *), - AstNode *(*while_parser)(ParseContext *) -) { - Token *inline_token = eat_token_if(pc, TokenIdKeywordInline); - AstNode *for_expr = for_parser(pc); - if (for_expr != nullptr) { - assert(for_expr->type == NodeTypeForExpr); - for_expr->data.for_expr.is_inline = inline_token != nullptr; - return for_expr; - } - - AstNode *while_expr = while_parser(pc); - if (while_expr != nullptr) { - assert(while_expr->type == NodeTypeWhileExpr); - while_expr->data.while_expr.is_inline = inline_token != nullptr; - return while_expr; - } - - if (inline_token != nullptr) - ast_invalid_token_error(pc, peek_token(pc)); - return nullptr; -} - -// ForPrefix Body (KEYWORD_else Body)? -static AstNode *ast_parse_for_expr_helper(ParseContext *pc, AstNode *(*body_parser)(ParseContext*)) { - AstNode *res = ast_parse_for_prefix(pc); - if (res == nullptr) - return nullptr; - - AstNode *body = ast_expect(pc, body_parser); - AstNode *else_body = nullptr; - if (eat_token_if(pc, TokenIdKeywordElse) != nullptr) - else_body = ast_expect(pc, body_parser); - - assert(res->type == NodeTypeForExpr); - res->data.for_expr.body = body; - res->data.for_expr.else_node = else_body; - return res; -} - -// WhilePrefix Body (KEYWORD_else Payload? Body)? -static AstNode *ast_parse_while_expr_helper(ParseContext *pc, AstNode *(*body_parser)(ParseContext*)) { - AstNode *res = ast_parse_while_prefix(pc); - if (res == nullptr) - return nullptr; - - AstNode *body = ast_expect(pc, body_parser); - Token *err_payload = nullptr; - AstNode *else_body = nullptr; - if (eat_token_if(pc, TokenIdKeywordElse) != nullptr) { - err_payload = ast_parse_payload(pc); - else_body = ast_expect(pc, body_parser); - } - - assert(res->type == NodeTypeWhileExpr); - res->data.while_expr.body = body; - res->data.while_expr.err_symbol = token_buf(err_payload); - res->data.while_expr.else_node = else_body; - return res; -} - -template -AstNode *ast_parse_bin_op_simple(ParseContext *pc) { - Token *op_token = eat_token_if(pc, id); - if (op_token == nullptr) - return nullptr; - - AstNode *res = ast_create_node(pc, NodeTypeBinOpExpr, op_token); - res->data.bin_op_expr.bin_op = op; - return res; -} - -AstNode *ast_parse(Buf *buf, ZigList *tokens, ZigType *owner, ErrColor err_color) { - ParseContext pc = {}; - pc.err_color = err_color; - pc.owner = owner; - pc.buf = buf; - pc.tokens = tokens; - return ast_parse_root(&pc); -} - -// Root <- skip ContainerMembers eof -static AstNode *ast_parse_root(ParseContext *pc) { - Token *first = peek_token(pc); - AstNodeContainerDecl members = ast_parse_container_members(pc); - if (pc->current_token != pc->tokens->length - 1) - ast_invalid_token_error(pc, peek_token(pc)); - - AstNode *node = ast_create_node(pc, NodeTypeContainerDecl, first); - node->data.container_decl.fields = members.fields; - node->data.container_decl.decls = members.decls; - node->data.container_decl.layout = ContainerLayoutAuto; - node->data.container_decl.kind = ContainerKindStruct; - node->data.container_decl.is_root = true; - if (buf_len(&members.doc_comments) != 0) { - node->data.container_decl.doc_comments = members.doc_comments; - } - - return node; -} - -static Token *ast_parse_doc_comments(ParseContext *pc, Buf *buf) { - Token *first_doc_token = nullptr; - Token *doc_token = nullptr; - while ((doc_token = eat_token_if(pc, TokenIdDocComment))) { - if (first_doc_token == nullptr) { - first_doc_token = doc_token; - } - if (buf->list.length == 0) { - buf_resize(buf, 0); - } - // chops off '///' but leaves '\n' - buf_append_mem(buf, buf_ptr(pc->buf) + doc_token->start_pos + 3, - doc_token->end_pos - doc_token->start_pos - 3); - } - return first_doc_token; -} - -static void ast_parse_container_doc_comments(ParseContext *pc, Buf *buf) { - if (buf_len(buf) != 0 && peek_token(pc)->id == TokenIdContainerDocComment) { - buf_append_char(buf, '\n'); - } - Token *doc_token = nullptr; - while ((doc_token = eat_token_if(pc, TokenIdContainerDocComment))) { - if (buf->list.length == 0) { - buf_resize(buf, 0); - } - // chops off '//!' but leaves '\n' - buf_append_mem(buf, buf_ptr(pc->buf) + doc_token->start_pos + 3, - doc_token->end_pos - doc_token->start_pos - 3); - } -} - -enum ContainerFieldState { - // no fields have been seen - ContainerFieldStateNone, - // currently parsing fields - ContainerFieldStateSeen, - // saw fields and then a declaration after them - ContainerFieldStateEnd, -}; - -// ContainerMembers -// <- TestDecl ContainerMembers -// / TopLevelComptime ContainerMembers -// / KEYWORD_pub? TopLevelDecl ContainerMembers -// / ContainerField COMMA ContainerMembers -// / ContainerField -// / -static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc) { - AstNodeContainerDecl res = {}; - Buf tld_doc_comment_buf = BUF_INIT; - buf_resize(&tld_doc_comment_buf, 0); - ContainerFieldState field_state = ContainerFieldStateNone; - Token *first_token = nullptr; - for (;;) { - ast_parse_container_doc_comments(pc, &tld_doc_comment_buf); - - Token *peeked_token = peek_token(pc); - - AstNode *test_decl = ast_parse_test_decl(pc); - if (test_decl != nullptr) { - if (field_state == ContainerFieldStateSeen) { - field_state = ContainerFieldStateEnd; - first_token = peeked_token; - } - res.decls.append(test_decl); - continue; - } - - AstNode *top_level_comptime = ast_parse_top_level_comptime(pc); - if (top_level_comptime != nullptr) { - if (field_state == ContainerFieldStateSeen) { - field_state = ContainerFieldStateEnd; - first_token = peeked_token; - } - res.decls.append(top_level_comptime); - continue; - } - - Buf doc_comment_buf = BUF_INIT; - ast_parse_doc_comments(pc, &doc_comment_buf); - - peeked_token = peek_token(pc); - - Token *visib_token = eat_token_if(pc, TokenIdKeywordPub); - VisibMod visib_mod = visib_token != nullptr ? VisibModPub : VisibModPrivate; - - AstNode *top_level_decl = ast_parse_top_level_decl(pc, visib_mod, &doc_comment_buf); - if (top_level_decl != nullptr) { - if (field_state == ContainerFieldStateSeen) { - field_state = ContainerFieldStateEnd; - first_token = peeked_token; - } - res.decls.append(top_level_decl); - continue; - } - - if (visib_token != nullptr) { - ast_error(pc, peek_token(pc), "expected function or variable declaration after pub"); - } - - Token *comptime_token = eat_token_if(pc, TokenIdKeywordCompTime); - - AstNode *container_field = ast_parse_container_field(pc); - if (container_field != nullptr) { - switch (field_state) { - case ContainerFieldStateNone: - field_state = ContainerFieldStateSeen; - break; - case ContainerFieldStateSeen: - break; - case ContainerFieldStateEnd: - ast_error(pc, first_token, "declarations are not allowed between container fields"); - } - - assert(container_field->type == NodeTypeStructField); - container_field->data.struct_field.doc_comments = doc_comment_buf; - container_field->data.struct_field.comptime_token = comptime_token; - res.fields.append(container_field); - if (eat_token_if(pc, TokenIdComma) != nullptr) { - continue; - } else { - break; - } - } - - break; - } - res.doc_comments = tld_doc_comment_buf; - return res; -} - -// TestDecl <- KEYWORD_test STRINGLITERALSINGLE Block -static AstNode *ast_parse_test_decl(ParseContext *pc) { - Token *test = eat_token_if(pc, TokenIdKeywordTest); - if (test == nullptr) - return nullptr; - - Token *name = expect_token(pc, TokenIdStringLiteral); - AstNode *block = ast_expect(pc, ast_parse_block); - AstNode *res = ast_create_node(pc, NodeTypeTestDecl, test); - res->data.test_decl.name = token_buf(name); - res->data.test_decl.body = block; - return res; -} - -// TopLevelComptime <- KEYWORD_comptime BlockExpr -static AstNode *ast_parse_top_level_comptime(ParseContext *pc) { - Token *comptime = eat_token_if(pc, TokenIdKeywordCompTime); - if (comptime == nullptr) - return nullptr; - - // 1 token lookahead because it could be a comptime struct field - Token *lbrace = peek_token(pc); - if (lbrace->id != TokenIdLBrace) { - put_back_token(pc); - return nullptr; - } - - AstNode *block = ast_expect(pc, ast_parse_block_expr); - AstNode *res = ast_create_node(pc, NodeTypeCompTime, comptime); - res->data.comptime_expr.expr = block; - return res; -} - -// TopLevelDecl -// <- (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE? / (KEYWORD_inline / KEYWORD_noinline))? FnProto (SEMICOLON / Block) -// / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl -// / KEYWORD_use Expr SEMICOLON -static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod, Buf *doc_comments) { - Token *first = eat_token_if(pc, TokenIdKeywordExport); - if (first == nullptr) - first = eat_token_if(pc, TokenIdKeywordExtern); - if (first == nullptr) - first = eat_token_if(pc, TokenIdKeywordInline); - if (first == nullptr) - first = eat_token_if(pc, TokenIdKeywordNoInline); - if (first != nullptr) { - Token *lib_name = nullptr; - if (first->id == TokenIdKeywordExtern) - lib_name = eat_token_if(pc, TokenIdStringLiteral); - - if (first->id != TokenIdKeywordInline && first->id != TokenIdKeywordNoInline) { - Token *thread_local_kw = eat_token_if(pc, TokenIdKeywordThreadLocal); - AstNode *var_decl = ast_parse_var_decl(pc); - if (var_decl != nullptr) { - assert(var_decl->type == NodeTypeVariableDeclaration); - if (first->id == TokenIdKeywordExtern && var_decl->data.variable_declaration.expr != nullptr) { - ast_error(pc, first, "extern variables have no initializers"); - } - var_decl->line = first->start_line; - var_decl->column = first->start_column; - var_decl->data.variable_declaration.threadlocal_tok = thread_local_kw; - var_decl->data.variable_declaration.visib_mod = visib_mod; - var_decl->data.variable_declaration.doc_comments = *doc_comments; - var_decl->data.variable_declaration.is_extern = first->id == TokenIdKeywordExtern; - var_decl->data.variable_declaration.is_export = first->id == TokenIdKeywordExport; - var_decl->data.variable_declaration.lib_name = token_buf(lib_name); - return var_decl; - } - - if (thread_local_kw != nullptr) - put_back_token(pc); - } - - AstNode *fn_proto = ast_parse_fn_proto(pc); - if (fn_proto != nullptr) { - AstNode *body = ast_parse_block(pc); - if (body == nullptr) - expect_token(pc, TokenIdSemicolon); - - assert(fn_proto->type == NodeTypeFnProto); - fn_proto->line = first->start_line; - fn_proto->column = first->start_column; - fn_proto->data.fn_proto.visib_mod = visib_mod; - fn_proto->data.fn_proto.doc_comments = *doc_comments; - if (!fn_proto->data.fn_proto.is_extern) - fn_proto->data.fn_proto.is_extern = first->id == TokenIdKeywordExtern; - fn_proto->data.fn_proto.is_export = first->id == TokenIdKeywordExport; - switch (first->id) { - case TokenIdKeywordInline: - fn_proto->data.fn_proto.fn_inline = FnInlineAlways; - break; - case TokenIdKeywordNoInline: - fn_proto->data.fn_proto.fn_inline = FnInlineNever; - break; - default: - fn_proto->data.fn_proto.fn_inline = FnInlineAuto; - break; - } - fn_proto->data.fn_proto.lib_name = token_buf(lib_name); - - AstNode *res = fn_proto; - if (body != nullptr) { - if (fn_proto->data.fn_proto.is_extern) { - ast_error(pc, first, "extern functions have no body"); - } - res = ast_create_node_copy_line_info(pc, NodeTypeFnDef, fn_proto); - res->data.fn_def.fn_proto = fn_proto; - res->data.fn_def.body = body; - fn_proto->data.fn_proto.fn_def_node = res; - } - - return res; - } - - ast_invalid_token_error(pc, peek_token(pc)); - } - - Token *thread_local_kw = eat_token_if(pc, TokenIdKeywordThreadLocal); - AstNode *var_decl = ast_parse_var_decl(pc); - if (var_decl != nullptr) { - assert(var_decl->type == NodeTypeVariableDeclaration); - var_decl->data.variable_declaration.visib_mod = visib_mod; - var_decl->data.variable_declaration.doc_comments = *doc_comments; - var_decl->data.variable_declaration.threadlocal_tok = thread_local_kw; - return var_decl; - } - - if (thread_local_kw != nullptr) - put_back_token(pc); - - AstNode *fn_proto = ast_parse_fn_proto(pc); - if (fn_proto != nullptr) { - AstNode *body = ast_parse_block(pc); - if (body == nullptr) - expect_token(pc, TokenIdSemicolon); - - assert(fn_proto->type == NodeTypeFnProto); - fn_proto->data.fn_proto.visib_mod = visib_mod; - fn_proto->data.fn_proto.doc_comments = *doc_comments; - AstNode *res = fn_proto; - if (body != nullptr) { - res = ast_create_node_copy_line_info(pc, NodeTypeFnDef, fn_proto); - res->data.fn_def.fn_proto = fn_proto; - res->data.fn_def.body = body; - fn_proto->data.fn_proto.fn_def_node = res; - } - - return res; - } - - Token *usingnamespace = eat_token_if(pc, TokenIdKeywordUsingNamespace); - if (usingnamespace != nullptr) { - AstNode *expr = ast_expect(pc, ast_parse_expr); - expect_token(pc, TokenIdSemicolon); - - AstNode *res = ast_create_node(pc, NodeTypeUsingNamespace, usingnamespace); - res->data.using_namespace.visib_mod = visib_mod; - res->data.using_namespace.expr = expr; - return res; - } - - return nullptr; -} - -// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? EXCLAMATIONMARK? (KEYWORD_anytype / TypeExpr) -static AstNode *ast_parse_fn_proto(ParseContext *pc) { - Token *first = eat_token_if(pc, TokenIdKeywordFn); - if (first == nullptr) { - return nullptr; - } - - Token *identifier = eat_token_if(pc, TokenIdSymbol); - expect_token(pc, TokenIdLParen); - ZigList params = ast_parse_list(pc, TokenIdComma, ast_parse_param_decl); - expect_token(pc, TokenIdRParen); - - AstNode *align_expr = ast_parse_byte_align(pc); - AstNode *section_expr = ast_parse_link_section(pc); - AstNode *callconv_expr = ast_parse_callconv(pc); - Token *anytype = eat_token_if(pc, TokenIdKeywordAnyType); - Token *exmark = nullptr; - AstNode *return_type = nullptr; - if (anytype == nullptr) { - exmark = eat_token_if(pc, TokenIdBang); - return_type = ast_expect(pc, ast_parse_type_expr); - } - - AstNode *res = ast_create_node(pc, NodeTypeFnProto, first); - res->data.fn_proto = {}; - res->data.fn_proto.name = token_buf(identifier); - res->data.fn_proto.params = params; - res->data.fn_proto.align_expr = align_expr; - res->data.fn_proto.section_expr = section_expr; - res->data.fn_proto.callconv_expr = callconv_expr; - res->data.fn_proto.return_anytype_token = anytype; - res->data.fn_proto.auto_err_set = exmark != nullptr; - res->data.fn_proto.return_type = return_type; - - for (size_t i = 0; i < params.length; i++) { - AstNode *param_decl = params.at(i); - assert(param_decl->type == NodeTypeParamDecl); - if (param_decl->data.param_decl.is_var_args) - res->data.fn_proto.is_var_args = true; - if (i != params.length - 1 && res->data.fn_proto.is_var_args) - ast_error(pc, first, "Function prototype have varargs as a none last parameter."); - } - return res; -} - -// VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON -static AstNode *ast_parse_var_decl(ParseContext *pc) { - Token *mut_kw = eat_token_if(pc, TokenIdKeywordConst); - if (mut_kw == nullptr) - mut_kw = eat_token_if(pc, TokenIdKeywordVar); - if (mut_kw == nullptr) - return nullptr; - - Token *identifier = expect_token(pc, TokenIdSymbol); - AstNode *type_expr = nullptr; - if (eat_token_if(pc, TokenIdColon) != nullptr) - type_expr = ast_expect(pc, ast_parse_type_expr); - - AstNode *align_expr = ast_parse_byte_align(pc); - AstNode *section_expr = ast_parse_link_section(pc); - AstNode *expr = nullptr; - if (eat_token_if(pc, TokenIdEq) != nullptr) - expr = ast_expect(pc, ast_parse_expr); - - expect_token(pc, TokenIdSemicolon); - - AstNode *res = ast_create_node(pc, NodeTypeVariableDeclaration, mut_kw); - res->data.variable_declaration.is_const = mut_kw->id == TokenIdKeywordConst; - res->data.variable_declaration.symbol = token_buf(identifier); - res->data.variable_declaration.type = type_expr; - res->data.variable_declaration.align_expr = align_expr; - res->data.variable_declaration.section_expr = section_expr; - res->data.variable_declaration.expr = expr; - return res; -} - -// ContainerField <- KEYWORD_comptime? IDENTIFIER (COLON TypeExpr ByteAlign?)? (EQUAL Expr)? -static AstNode *ast_parse_container_field(ParseContext *pc) { - Token *identifier = eat_token_if(pc, TokenIdSymbol); - if (identifier == nullptr) - return nullptr; - - AstNode *type_expr = nullptr; - if (eat_token_if(pc, TokenIdColon) != nullptr) { - Token *anytype_tok = eat_token_if(pc, TokenIdKeywordAnyType); - if (anytype_tok != nullptr) { - type_expr = ast_create_node(pc, NodeTypeAnyTypeField, anytype_tok); - } else { - type_expr = ast_expect(pc, ast_parse_type_expr); - } - } - AstNode *align_expr = ast_parse_byte_align(pc); - AstNode *expr = nullptr; - if (eat_token_if(pc, TokenIdEq) != nullptr) - expr = ast_expect(pc, ast_parse_expr); - - AstNode *res = ast_create_node(pc, NodeTypeStructField, identifier); - res->data.struct_field.name = token_buf(identifier); - res->data.struct_field.type = type_expr; - res->data.struct_field.value = expr; - res->data.struct_field.align_expr = align_expr; - return res; -} - -// Statement -// <- KEYWORD_comptime? VarDecl -// / KEYWORD_comptime BlockExprStatement -// / KEYWORD_nosuspend BlockExprStatement -// / KEYWORD_suspend (SEMICOLON / BlockExprStatement) -// / KEYWORD_defer BlockExprStatement -// / KEYWORD_errdefer Payload? BlockExprStatement -// / IfStatement -// / LabeledStatement -// / SwitchExpr -// / AssignExpr SEMICOLON -static AstNode *ast_parse_statement(ParseContext *pc) { - Token *comptime = eat_token_if(pc, TokenIdKeywordCompTime); - AstNode *var_decl = ast_parse_var_decl(pc); - if (var_decl != nullptr) { - assert(var_decl->type == NodeTypeVariableDeclaration); - var_decl->data.variable_declaration.is_comptime = comptime != nullptr; - return var_decl; - } - - if (comptime != nullptr) { - AstNode *statement = ast_expect(pc, ast_parse_block_expr_statement); - AstNode *res = ast_create_node(pc, NodeTypeCompTime, comptime); - res->data.comptime_expr.expr = statement; - return res; - } - - Token *nosuspend = eat_token_if(pc, TokenIdKeywordNoSuspend); - if (nosuspend != nullptr) { - AstNode *statement = ast_expect(pc, ast_parse_block_expr_statement); - AstNode *res = ast_create_node(pc, NodeTypeNoSuspend, nosuspend); - res->data.nosuspend_expr.expr = statement; - return res; - } - - Token *suspend = eat_token_if(pc, TokenIdKeywordSuspend); - if (suspend != nullptr) { - AstNode *statement = nullptr; - if (eat_token_if(pc, TokenIdSemicolon) == nullptr) - statement = ast_expect(pc, ast_parse_block_expr_statement); - - AstNode *res = ast_create_node(pc, NodeTypeSuspend, suspend); - res->data.suspend.block = statement; - return res; - } - - Token *defer = eat_token_if(pc, TokenIdKeywordDefer); - if (defer == nullptr) - defer = eat_token_if(pc, TokenIdKeywordErrdefer); - if (defer != nullptr) { - Token *payload = (defer->id == TokenIdKeywordErrdefer) ? - ast_parse_payload(pc) : nullptr; - AstNode *statement = ast_expect(pc, ast_parse_block_expr_statement); - AstNode *res = ast_create_node(pc, NodeTypeDefer, defer); - - res->data.defer.kind = ReturnKindUnconditional; - res->data.defer.expr = statement; - if (defer->id == TokenIdKeywordErrdefer) { - res->data.defer.kind = ReturnKindError; - if (payload != nullptr) - res->data.defer.err_payload = token_symbol(pc, payload); - } - return res; - } - - AstNode *if_statement = ast_parse_if_statement(pc); - if (if_statement != nullptr) - return if_statement; - - AstNode *labeled_statement = ast_parse_labeled_statement(pc); - if (labeled_statement != nullptr) - return labeled_statement; - - AstNode *switch_expr = ast_parse_switch_expr(pc); - if (switch_expr != nullptr) - return switch_expr; - - AstNode *assign = ast_parse_assign_expr(pc); - if (assign != nullptr) { - expect_token(pc, TokenIdSemicolon); - return assign; - } - - return nullptr; -} - -// IfStatement -// <- IfPrefix BlockExpr ( KEYWORD_else Payload? Statement )? -// / IfPrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement ) -static AstNode *ast_parse_if_statement(ParseContext *pc) { - AstNode *res = ast_parse_if_prefix(pc); - if (res == nullptr) - return nullptr; - - AstNode *body = ast_parse_block_expr(pc); - bool requires_semi = false; - if (body == nullptr) { - requires_semi = true; - body = ast_parse_assign_expr(pc); - } - - if (body == nullptr) { - Token *tok = eat_token(pc); - ast_error(pc, tok, "expected if body, found '%s'", token_name(tok->id)); - } - - Token *err_payload = nullptr; - AstNode *else_body = nullptr; - if (eat_token_if(pc, TokenIdKeywordElse) != nullptr) { - err_payload = ast_parse_payload(pc); - else_body = ast_expect(pc, ast_parse_statement); - } - - if (requires_semi && else_body == nullptr) - expect_token(pc, TokenIdSemicolon); - - assert(res->type == NodeTypeIfOptional); - if (err_payload != nullptr) { - AstNodeTestExpr old = res->data.test_expr; - res->type = NodeTypeIfErrorExpr; - res->data.if_err_expr.target_node = old.target_node; - res->data.if_err_expr.var_is_ptr = old.var_is_ptr; - res->data.if_err_expr.var_symbol = old.var_symbol; - res->data.if_err_expr.then_node = body; - res->data.if_err_expr.err_symbol = token_buf(err_payload); - res->data.if_err_expr.else_node = else_body; - return res; - } - - if (res->data.test_expr.var_symbol != nullptr) { - res->data.test_expr.then_node = body; - res->data.test_expr.else_node = else_body; - return res; - } - - AstNodeTestExpr old = res->data.test_expr; - res->type = NodeTypeIfBoolExpr; - res->data.if_bool_expr.condition = old.target_node; - res->data.if_bool_expr.then_block = body; - res->data.if_bool_expr.else_node = else_body; - return res; -} - -// LabeledStatement <- BlockLabel? (Block / LoopStatement) -static AstNode *ast_parse_labeled_statement(ParseContext *pc) { - Token *label = ast_parse_block_label(pc); - AstNode *block = ast_parse_block(pc); - if (block != nullptr) { - assert(block->type == NodeTypeBlock); - block->data.block.name = token_buf(label); - return block; - } - - AstNode *loop = ast_parse_loop_statement(pc); - if (loop != nullptr) { - switch (loop->type) { - case NodeTypeForExpr: - loop->data.for_expr.name = token_buf(label); - break; - case NodeTypeWhileExpr: - loop->data.while_expr.name = token_buf(label); - break; - default: - zig_unreachable(); - } - return loop; - } - - if (label != nullptr) - ast_invalid_token_error(pc, peek_token(pc)); - return nullptr; -} - -// LoopStatement <- KEYWORD_inline? (ForStatement / WhileStatement) -static AstNode *ast_parse_loop_statement(ParseContext *pc) { - Token *inline_token = eat_token_if(pc, TokenIdKeywordInline); - AstNode *for_statement = ast_parse_for_statement(pc); - if (for_statement != nullptr) { - assert(for_statement->type == NodeTypeForExpr); - for_statement->data.for_expr.is_inline = inline_token != nullptr; - return for_statement; - } - - AstNode *while_statement = ast_parse_while_statement(pc); - if (while_statement != nullptr) { - assert(while_statement->type == NodeTypeWhileExpr); - while_statement->data.while_expr.is_inline = inline_token != nullptr; - return while_statement; - } - - if (inline_token != nullptr) - ast_invalid_token_error(pc, peek_token(pc)); - return nullptr; -} - -// ForStatement -// <- ForPrefix BlockExpr ( KEYWORD_else Statement )? -// / ForPrefix AssignExpr ( SEMICOLON / KEYWORD_else Statement ) -static AstNode *ast_parse_for_statement(ParseContext *pc) { - AstNode *res = ast_parse_for_prefix(pc); - if (res == nullptr) - return nullptr; - - AstNode *body = ast_parse_block_expr(pc); - bool requires_semi = false; - if (body == nullptr) { - requires_semi = true; - body = ast_parse_assign_expr(pc); - } - - if (body == nullptr) { - Token *tok = eat_token(pc); - ast_error(pc, tok, "expected loop body, found '%s'", token_name(tok->id)); - } - - AstNode *else_body = nullptr; - if (eat_token_if(pc, TokenIdKeywordElse) != nullptr) { - else_body = ast_expect(pc, ast_parse_statement); - } - - if (requires_semi && else_body == nullptr) - expect_token(pc, TokenIdSemicolon); - - assert(res->type == NodeTypeForExpr); - res->data.for_expr.body = body; - res->data.for_expr.else_node = else_body; - return res; -} - -// WhileStatement -// <- WhilePrefix BlockExpr ( KEYWORD_else Payload? Statement )? -// / WhilePrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement ) -static AstNode *ast_parse_while_statement(ParseContext *pc) { - AstNode *res = ast_parse_while_prefix(pc); - if (res == nullptr) - return nullptr; - - AstNode *body = ast_parse_block_expr(pc); - bool requires_semi = false; - if (body == nullptr) { - requires_semi = true; - body = ast_parse_assign_expr(pc); - } - - if (body == nullptr) { - Token *tok = eat_token(pc); - ast_error(pc, tok, "expected loop body, found '%s'", token_name(tok->id)); - } - - Token *err_payload = nullptr; - AstNode *else_body = nullptr; - if (eat_token_if(pc, TokenIdKeywordElse) != nullptr) { - err_payload = ast_parse_payload(pc); - else_body = ast_expect(pc, ast_parse_statement); - } - - if (requires_semi && else_body == nullptr) - expect_token(pc, TokenIdSemicolon); - - assert(res->type == NodeTypeWhileExpr); - res->data.while_expr.body = body; - res->data.while_expr.err_symbol = token_buf(err_payload); - res->data.while_expr.else_node = else_body; - return res; -} - - -// BlockExprStatement -// <- BlockExpr -// / AssignExpr SEMICOLON -static AstNode *ast_parse_block_expr_statement(ParseContext *pc) { - AstNode *block = ast_parse_block_expr(pc); - if (block != nullptr) - return block; - - AstNode *assign_expr = ast_parse_assign_expr(pc); - if (assign_expr != nullptr) { - expect_token(pc, TokenIdSemicolon); - return assign_expr; - } - - return nullptr; -} - -// BlockExpr <- BlockLabel? Block -static AstNode *ast_parse_block_expr(ParseContext *pc) { - Token *label = ast_parse_block_label(pc); - if (label != nullptr) { - AstNode *res = ast_expect(pc, ast_parse_block); - assert(res->type == NodeTypeBlock); - res->data.block.name = token_buf(label); - return res; - } - - return ast_parse_block(pc); -} - -// AssignExpr <- Expr (AssignOp Expr)? -static AstNode *ast_parse_assign_expr(ParseContext *pc) { - return ast_parse_bin_op_expr(pc, BinOpChainOnce, ast_parse_assign_op, ast_parse_expr); -} - -// Expr <- KEYWORD_try* BoolOrExpr -static AstNode *ast_parse_expr(ParseContext *pc) { - return ast_parse_prefix_op_expr( - pc, - [](ParseContext *context) { - Token *try_token = eat_token_if(context, TokenIdKeywordTry); - if (try_token != nullptr) { - AstNode *res = ast_create_node(context, NodeTypeReturnExpr, try_token); - res->data.return_expr.kind = ReturnKindError; - return res; - } - - return (AstNode*)nullptr; - }, - ast_parse_bool_or_expr - ); -} - -// BoolOrExpr <- BoolAndExpr (KEYWORD_or BoolAndExpr)* -static AstNode *ast_parse_bool_or_expr(ParseContext *pc) { - return ast_parse_bin_op_expr( - pc, - BinOpChainInf, - ast_parse_bin_op_simple, - ast_parse_bool_and_expr - ); -} - -// BoolAndExpr <- CompareExpr (KEYWORD_and CompareExpr)* -static AstNode *ast_parse_bool_and_expr(ParseContext *pc) { - return ast_parse_bin_op_expr( - pc, - BinOpChainInf, - ast_parse_bin_op_simple, - ast_parse_compare_expr - ); -} - -// CompareExpr <- BitwiseExpr (CompareOp BitwiseExpr)? -static AstNode *ast_parse_compare_expr(ParseContext *pc) { - return ast_parse_bin_op_expr(pc, BinOpChainOnce, ast_parse_compare_op, ast_parse_bitwise_expr); -} - -// BitwiseExpr <- BitShiftExpr (BitwiseOp BitShiftExpr)* -static AstNode *ast_parse_bitwise_expr(ParseContext *pc) { - return ast_parse_bin_op_expr(pc, BinOpChainInf, ast_parse_bitwise_op, ast_parse_bit_shift_expr); -} - -// BitShiftExpr <- AdditionExpr (BitShiftOp AdditionExpr)* -static AstNode *ast_parse_bit_shift_expr(ParseContext *pc) { - return ast_parse_bin_op_expr(pc, BinOpChainInf, ast_parse_bit_shift_op, ast_parse_addition_expr); -} - -// AdditionExpr <- MultiplyExpr (AdditionOp MultiplyExpr)* -static AstNode *ast_parse_addition_expr(ParseContext *pc) { - return ast_parse_bin_op_expr(pc, BinOpChainInf, ast_parse_addition_op, ast_parse_multiply_expr); -} - -// MultiplyExpr <- PrefixExpr (MultiplyOp PrefixExpr)* -static AstNode *ast_parse_multiply_expr(ParseContext *pc) { - return ast_parse_bin_op_expr(pc, BinOpChainInf, ast_parse_multiply_op, ast_parse_prefix_expr); -} - -// PrefixExpr <- PrefixOp* PrimaryExpr -static AstNode *ast_parse_prefix_expr(ParseContext *pc) { - return ast_parse_prefix_op_expr( - pc, - ast_parse_prefix_op, - ast_parse_primary_expr - ); -} - -// PrimaryExpr -// <- AsmExpr -// / IfExpr -// / KEYWORD_break BreakLabel? Expr? -// / KEYWORD_comptime Expr -// / KEYWORD_nosuspend Expr -// / KEYWORD_continue BreakLabel? -// / KEYWORD_resume Expr -// / KEYWORD_return Expr? -// / BlockLabel? LoopExpr -// / Block -// / CurlySuffixExpr -static AstNode *ast_parse_primary_expr(ParseContext *pc) { - AstNode *asm_expr = ast_parse_asm_expr(pc); - if (asm_expr != nullptr) - return asm_expr; - - AstNode *if_expr = ast_parse_if_expr(pc); - if (if_expr != nullptr) - return if_expr; - - Token *break_token = eat_token_if(pc, TokenIdKeywordBreak); - if (break_token != nullptr) { - Token *label = ast_parse_break_label(pc); - AstNode *expr = ast_parse_expr(pc); - - AstNode *res = ast_create_node(pc, NodeTypeBreak, break_token); - res->data.break_expr.name = token_buf(label); - res->data.break_expr.expr = expr; - return res; - } - - Token *comptime = eat_token_if(pc, TokenIdKeywordCompTime); - if (comptime != nullptr) { - AstNode *expr = ast_expect(pc, ast_parse_expr); - AstNode *res = ast_create_node(pc, NodeTypeCompTime, comptime); - res->data.comptime_expr.expr = expr; - return res; - } - - Token *nosuspend = eat_token_if(pc, TokenIdKeywordNoSuspend); - if (nosuspend != nullptr) { - AstNode *expr = ast_expect(pc, ast_parse_expr); - AstNode *res = ast_create_node(pc, NodeTypeNoSuspend, nosuspend); - res->data.nosuspend_expr.expr = expr; - return res; - } - - Token *continue_token = eat_token_if(pc, TokenIdKeywordContinue); - if (continue_token != nullptr) { - Token *label = ast_parse_break_label(pc); - AstNode *res = ast_create_node(pc, NodeTypeContinue, continue_token); - res->data.continue_expr.name = token_buf(label); - return res; - } - - Token *resume = eat_token_if(pc, TokenIdKeywordResume); - if (resume != nullptr) { - AstNode *expr = ast_expect(pc, ast_parse_expr); - AstNode *res = ast_create_node(pc, NodeTypeResume, resume); - res->data.resume_expr.expr = expr; - return res; - } - - Token *return_token = eat_token_if(pc, TokenIdKeywordReturn); - if (return_token != nullptr) { - AstNode *expr = ast_parse_expr(pc); - AstNode *res = ast_create_node(pc, NodeTypeReturnExpr, return_token); - res->data.return_expr.expr = expr; - return res; - } - - Token *label = ast_parse_block_label(pc); - AstNode *loop = ast_parse_loop_expr(pc); - if (loop != nullptr) { - switch (loop->type) { - case NodeTypeForExpr: - loop->data.for_expr.name = token_buf(label); - break; - case NodeTypeWhileExpr: - loop->data.while_expr.name = token_buf(label); - break; - default: - zig_unreachable(); - } - return loop; - } else if (label != nullptr) { - // Restore the tokens that we eaten by ast_parse_block_label. - put_back_token(pc); - put_back_token(pc); - } - - AstNode *block = ast_parse_block(pc); - if (block != nullptr) - return block; - - AstNode *curly_suffix = ast_parse_curly_suffix_expr(pc); - if (curly_suffix != nullptr) - return curly_suffix; - - return nullptr; -} - -// IfExpr <- IfPrefix Expr (KEYWORD_else Payload? Expr)? -static AstNode *ast_parse_if_expr(ParseContext *pc) { - return ast_parse_if_expr_helper(pc, ast_parse_expr); -} - -// Block <- LBRACE Statement* RBRACE -static AstNode *ast_parse_block(ParseContext *pc) { - Token *lbrace = eat_token_if(pc, TokenIdLBrace); - if (lbrace == nullptr) - return nullptr; - - ZigList statements = {}; - AstNode *statement; - while ((statement = ast_parse_statement(pc)) != nullptr) - statements.append(statement); - - expect_token(pc, TokenIdRBrace); - - AstNode *res = ast_create_node(pc, NodeTypeBlock, lbrace); - res->data.block.statements = statements; - return res; -} - -// LoopExpr <- KEYWORD_inline? (ForExpr / WhileExpr) -static AstNode *ast_parse_loop_expr(ParseContext *pc) { - return ast_parse_loop_expr_helper( - pc, - ast_parse_for_expr, - ast_parse_while_expr - ); -} - -// ForExpr <- ForPrefix Expr (KEYWORD_else Expr)? -static AstNode *ast_parse_for_expr(ParseContext *pc) { - return ast_parse_for_expr_helper(pc, ast_parse_expr); -} - -// WhileExpr <- WhilePrefix Expr (KEYWORD_else Payload? Expr)? -static AstNode *ast_parse_while_expr(ParseContext *pc) { - return ast_parse_while_expr_helper(pc, ast_parse_expr); -} - -// CurlySuffixExpr <- TypeExpr InitList? -static AstNode *ast_parse_curly_suffix_expr(ParseContext *pc) { - AstNode *type_expr = ast_parse_type_expr(pc); - if (type_expr == nullptr) - return nullptr; - - AstNode *res = ast_parse_init_list(pc); - if (res == nullptr) - return type_expr; - - assert(res->type == NodeTypeContainerInitExpr); - res->data.container_init_expr.type = type_expr; - return res; -} - -// InitList -// <- LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE -// / LBRACE Expr (COMMA Expr)* COMMA? RBRACE -// / LBRACE RBRACE -static AstNode *ast_parse_init_list(ParseContext *pc) { - Token *lbrace = eat_token_if(pc, TokenIdLBrace); - if (lbrace == nullptr) - return nullptr; - - AstNode *first = ast_parse_field_init(pc); - if (first != nullptr) { - AstNode *res = ast_create_node(pc, NodeTypeContainerInitExpr, lbrace); - res->data.container_init_expr.kind = ContainerInitKindStruct; - res->data.container_init_expr.entries.append(first); - - while (eat_token_if(pc, TokenIdComma) != nullptr) { - AstNode *field_init = ast_parse_field_init(pc); - if (field_init == nullptr) - break; - res->data.container_init_expr.entries.append(field_init); - } - - expect_token(pc, TokenIdRBrace); - return res; - } - - AstNode *res = ast_create_node(pc, NodeTypeContainerInitExpr, lbrace); - res->data.container_init_expr.kind = ContainerInitKindArray; - - first = ast_parse_expr(pc); - if (first != nullptr) { - res->data.container_init_expr.entries.append(first); - - while (eat_token_if(pc, TokenIdComma) != nullptr) { - AstNode *expr = ast_parse_expr(pc); - if (expr == nullptr) - break; - res->data.container_init_expr.entries.append(expr); - } - - expect_token(pc, TokenIdRBrace); - return res; - } - - expect_token(pc, TokenIdRBrace); - return res; -} - -// TypeExpr <- PrefixTypeOp* ErrorUnionExpr -static AstNode *ast_parse_type_expr(ParseContext *pc) { - return ast_parse_prefix_op_expr( - pc, - ast_parse_prefix_type_op, - ast_parse_error_union_expr - ); -} - -// ErrorUnionExpr <- SuffixExpr (EXCLAMATIONMARK TypeExpr)? -static AstNode *ast_parse_error_union_expr(ParseContext *pc) { - AstNode *res = ast_parse_suffix_expr(pc); - if (res == nullptr) - return nullptr; - - AstNode *op = ast_parse_bin_op_simple(pc); - if (op == nullptr) - return res; - - AstNode *right = ast_expect(pc, ast_parse_type_expr); - assert(op->type == NodeTypeBinOpExpr); - op->data.bin_op_expr.op1 = res; - op->data.bin_op_expr.op2 = right; - return op; -} - -// SuffixExpr -// <- KEYWORD_async PrimaryTypeExpr SuffixOp* FnCallArguments -// / PrimaryTypeExpr (SuffixOp / FnCallArguments)* -static AstNode *ast_parse_suffix_expr(ParseContext *pc) { - Token *async_token = eat_token_if(pc, TokenIdKeywordAsync); - if (async_token) { - AstNode *child = ast_expect(pc, ast_parse_primary_type_expr); - while (true) { - AstNode *suffix = ast_parse_suffix_op(pc); - if (suffix == nullptr) - break; - - switch (suffix->type) { - case NodeTypeSliceExpr: - suffix->data.slice_expr.array_ref_expr = child; - break; - case NodeTypeArrayAccessExpr: - suffix->data.array_access_expr.array_ref_expr = child; - break; - case NodeTypeFieldAccessExpr: - suffix->data.field_access_expr.struct_expr = child; - break; - case NodeTypeUnwrapOptional: - suffix->data.unwrap_optional.expr = child; - break; - case NodeTypePtrDeref: - suffix->data.ptr_deref_expr.target = child; - break; - default: - zig_unreachable(); - } - child = suffix; - } - - // TODO: Both *_async_prefix and *_fn_call_arguments returns an - // AstNode *. All we really want here is the arguments of - // the call we parse. We therefor "leak" the node for now. - // Wait till we get async rework to fix this. - AstNode *args = ast_parse_fn_call_arguments(pc); - if (args == nullptr) - ast_invalid_token_error(pc, peek_token(pc)); - - assert(args->type == NodeTypeFnCallExpr); - - AstNode *res = ast_create_node(pc, NodeTypeFnCallExpr, async_token); - res->data.fn_call_expr.modifier = CallModifierAsync; - res->data.fn_call_expr.seen = false; - res->data.fn_call_expr.fn_ref_expr = child; - res->data.fn_call_expr.params = args->data.fn_call_expr.params; - return res; - } - - AstNode *res = ast_parse_primary_type_expr(pc); - if (res == nullptr) - return nullptr; - - while (true) { - AstNode *suffix = ast_parse_suffix_op(pc); - if (suffix != nullptr) { - switch (suffix->type) { - case NodeTypeSliceExpr: - suffix->data.slice_expr.array_ref_expr = res; - break; - case NodeTypeArrayAccessExpr: - suffix->data.array_access_expr.array_ref_expr = res; - break; - case NodeTypeFieldAccessExpr: - suffix->data.field_access_expr.struct_expr = res; - break; - case NodeTypeUnwrapOptional: - suffix->data.unwrap_optional.expr = res; - break; - case NodeTypePtrDeref: - suffix->data.ptr_deref_expr.target = res; - break; - default: - zig_unreachable(); - } - res = suffix; - continue; - } - - AstNode * call = ast_parse_fn_call_arguments(pc); - if (call != nullptr) { - assert(call->type == NodeTypeFnCallExpr); - call->data.fn_call_expr.fn_ref_expr = res; - res = call; - continue; - } - - break; - } - - return res; - -} - -// PrimaryTypeExpr -// <- BUILTINIDENTIFIER FnCallArguments -// / CHAR_LITERAL -// / ContainerDecl -// / DOT IDENTIFIER -// / ErrorSetDecl -// / FLOAT -// / FnProto -// / GroupedExpr -// / LabeledTypeExpr -// / IDENTIFIER -// / IfTypeExpr -// / INTEGER -// / KEYWORD_comptime TypeExpr -// / KEYWORD_error DOT IDENTIFIER -// / KEYWORD_false -// / KEYWORD_null -// / KEYWORD_promise -// / KEYWORD_true -// / KEYWORD_undefined -// / KEYWORD_unreachable -// / STRINGLITERAL -// / SwitchExpr -static AstNode *ast_parse_primary_type_expr(ParseContext *pc) { - // TODO: This is not in line with the grammar. - // Because the prev stage 1 tokenizer does not parse - // @[a-zA-Z_][a-zA-Z0-9_] as one token, it has to do a - // hack, where it accepts '@' (IDENTIFIER / KEYWORD_export). - // I'd say that it's better if '@' is part of the builtin - // identifier token. - Token *at_sign = eat_token_if(pc, TokenIdAtSign); - if (at_sign != nullptr) { - Buf *name; - Token *token = eat_token_if(pc, TokenIdKeywordExport); - if (token == nullptr) { - token = expect_token(pc, TokenIdSymbol); - name = token_buf(token); - } else { - name = buf_create_from_str("export"); - } - - AstNode *res = ast_expect(pc, ast_parse_fn_call_arguments); - AstNode *name_sym = ast_create_node(pc, NodeTypeSymbol, token); - name_sym->data.symbol_expr.symbol = name; - - assert(res->type == NodeTypeFnCallExpr); - res->line = at_sign->start_line; - res->column = at_sign->start_column; - res->data.fn_call_expr.fn_ref_expr = name_sym; - res->data.fn_call_expr.modifier = CallModifierBuiltin; - return res; - } - - Token *char_lit = eat_token_if(pc, TokenIdCharLiteral); - if (char_lit != nullptr) { - AstNode *res = ast_create_node(pc, NodeTypeCharLiteral, char_lit); - res->data.char_literal.value = char_lit->data.char_lit.c; - return res; - } - - AstNode *container_decl = ast_parse_container_decl(pc); - if (container_decl != nullptr) - return container_decl; - - AstNode *anon_lit = ast_parse_anon_lit(pc); - if (anon_lit != nullptr) - return anon_lit; - - AstNode *error_set_decl = ast_parse_error_set_decl(pc); - if (error_set_decl != nullptr) - return error_set_decl; - - Token *float_lit = eat_token_if(pc, TokenIdFloatLiteral); - if (float_lit != nullptr) { - AstNode *res = ast_create_node(pc, NodeTypeFloatLiteral, float_lit); - res->data.float_literal.bigfloat = &float_lit->data.float_lit.bigfloat; - res->data.float_literal.overflow = float_lit->data.float_lit.overflow; - return res; - } - - AstNode *fn_proto = ast_parse_fn_proto(pc); - if (fn_proto != nullptr) - return fn_proto; - - AstNode *grouped_expr = ast_parse_grouped_expr(pc); - if (grouped_expr != nullptr) - return grouped_expr; - - AstNode *labeled_type_expr = ast_parse_labeled_type_expr(pc); - if (labeled_type_expr != nullptr) - return labeled_type_expr; - - Token *identifier = eat_token_if(pc, TokenIdSymbol); - if (identifier != nullptr) - return token_symbol(pc, identifier); - - AstNode *if_type_expr = ast_parse_if_type_expr(pc); - if (if_type_expr != nullptr) - return if_type_expr; - - Token *int_lit = eat_token_if(pc, TokenIdIntLiteral); - if (int_lit != nullptr) { - AstNode *res = ast_create_node(pc, NodeTypeIntLiteral, int_lit); - res->data.int_literal.bigint = &int_lit->data.int_lit.bigint; - return res; - } - - Token *comptime = eat_token_if(pc, TokenIdKeywordCompTime); - if (comptime != nullptr) { - AstNode *expr = ast_expect(pc, ast_parse_type_expr); - AstNode *res = ast_create_node(pc, NodeTypeCompTime, comptime); - res->data.comptime_expr.expr = expr; - return res; - } - - Token *error = eat_token_if(pc, TokenIdKeywordError); - if (error != nullptr) { - Token *dot = expect_token(pc, TokenIdDot); - Token *name = expect_token(pc, TokenIdSymbol); - AstNode *left = ast_create_node(pc, NodeTypeErrorType, error); - AstNode *res = ast_create_node(pc, NodeTypeFieldAccessExpr, dot); - res->data.field_access_expr.struct_expr = left; - res->data.field_access_expr.field_name = token_buf(name); - return res; - } - - Token *false_token = eat_token_if(pc, TokenIdKeywordFalse); - if (false_token != nullptr) { - AstNode *res = ast_create_node(pc, NodeTypeBoolLiteral, false_token); - res->data.bool_literal.value = false; - return res; - } - - Token *null = eat_token_if(pc, TokenIdKeywordNull); - if (null != nullptr) - return ast_create_node(pc, NodeTypeNullLiteral, null); - - Token *anyframe = eat_token_if(pc, TokenIdKeywordAnyFrame); - if (anyframe != nullptr) - return ast_create_node(pc, NodeTypeAnyFrameType, anyframe); - - Token *true_token = eat_token_if(pc, TokenIdKeywordTrue); - if (true_token != nullptr) { - AstNode *res = ast_create_node(pc, NodeTypeBoolLiteral, true_token); - res->data.bool_literal.value = true; - return res; - } - - Token *undefined = eat_token_if(pc, TokenIdKeywordUndefined); - if (undefined != nullptr) - return ast_create_node(pc, NodeTypeUndefinedLiteral, undefined); - - Token *unreachable = eat_token_if(pc, TokenIdKeywordUnreachable); - if (unreachable != nullptr) - return ast_create_node(pc, NodeTypeUnreachable, unreachable); - - Token *string_lit = eat_token_if(pc, TokenIdStringLiteral); - if (string_lit == nullptr) - string_lit = eat_token_if(pc, TokenIdMultilineStringLiteral); - if (string_lit != nullptr) { - AstNode *res = ast_create_node(pc, NodeTypeStringLiteral, string_lit); - res->data.string_literal.buf = token_buf(string_lit); - return res; - } - - AstNode *switch_expr = ast_parse_switch_expr(pc); - if (switch_expr != nullptr) - return switch_expr; - - return nullptr; -} - -// ContainerDecl <- (KEYWORD_extern / KEYWORD_packed)? ContainerDeclAuto -static AstNode *ast_parse_container_decl(ParseContext *pc) { - Token *layout_token = eat_token_if(pc, TokenIdKeywordExtern); - if (layout_token == nullptr) - layout_token = eat_token_if(pc, TokenIdKeywordPacked); - - AstNode *res = ast_parse_container_decl_auto(pc); - if (res == nullptr) { - if (layout_token != nullptr) - put_back_token(pc); - return nullptr; - } - - assert(res->type == NodeTypeContainerDecl); - if (layout_token != nullptr) { - res->line = layout_token->start_line; - res->column = layout_token->start_column; - res->data.container_decl.layout = layout_token->id == TokenIdKeywordExtern - ? ContainerLayoutExtern - : ContainerLayoutPacked; - } - return res; -} - -// ErrorSetDecl <- KEYWORD_error LBRACE IdentifierList RBRACE -static AstNode *ast_parse_error_set_decl(ParseContext *pc) { - Token *first = eat_token_if(pc, TokenIdKeywordError); - if (first == nullptr) - return nullptr; - if (eat_token_if(pc, TokenIdLBrace) == nullptr) { - put_back_token(pc); - return nullptr; - } - - ZigList decls = ast_parse_list(pc, TokenIdComma, [](ParseContext *context) { - Buf doc_comment_buf = BUF_INIT; - Token *doc_token = ast_parse_doc_comments(context, &doc_comment_buf); - Token *ident = eat_token_if(context, TokenIdSymbol); - if (ident == nullptr) - return (AstNode*)nullptr; - - AstNode *symbol_node = token_symbol(context, ident); - if (doc_token == nullptr) - return symbol_node; - - AstNode *field_node = ast_create_node(context, NodeTypeErrorSetField, doc_token); - field_node->data.err_set_field.field_name = symbol_node; - field_node->data.err_set_field.doc_comments = doc_comment_buf; - return field_node; - }); - expect_token(pc, TokenIdRBrace); - - AstNode *res = ast_create_node(pc, NodeTypeErrorSetDecl, first); - res->data.err_set_decl.decls = decls; - return res; -} - -// GroupedExpr <- LPAREN Expr RPAREN -static AstNode *ast_parse_grouped_expr(ParseContext *pc) { - Token *lparen = eat_token_if(pc, TokenIdLParen); - if (lparen == nullptr) - return nullptr; - - AstNode *expr = ast_expect(pc, ast_parse_expr); - expect_token(pc, TokenIdRParen); - - AstNode *res = ast_create_node(pc, NodeTypeGroupedExpr, lparen); - res->data.grouped_expr = expr; - return res; -} - -// IfTypeExpr <- IfPrefix TypeExpr (KEYWORD_else Payload? TypeExpr)? -static AstNode *ast_parse_if_type_expr(ParseContext *pc) { - return ast_parse_if_expr_helper(pc, ast_parse_type_expr); -} - -// LabeledTypeExpr -// <- BlockLabel Block -// / BlockLabel? LoopTypeExpr -static AstNode *ast_parse_labeled_type_expr(ParseContext *pc) { - Token *label = ast_parse_block_label(pc); - if (label != nullptr) { - AstNode *block = ast_parse_block(pc); - if (block != nullptr) { - assert(block->type == NodeTypeBlock); - block->data.block.name = token_buf(label); - return block; - } - } - - AstNode *loop = ast_parse_loop_type_expr(pc); - if (loop != nullptr) { - switch (loop->type) { - case NodeTypeForExpr: - loop->data.for_expr.name = token_buf(label); - break; - case NodeTypeWhileExpr: - loop->data.while_expr.name = token_buf(label); - break; - default: - zig_unreachable(); - } - return loop; - } - - if (label != nullptr) { - put_back_token(pc); - put_back_token(pc); - } - return nullptr; -} - -// LoopTypeExpr <- KEYWORD_inline? (ForTypeExpr / WhileTypeExpr) -static AstNode *ast_parse_loop_type_expr(ParseContext *pc) { - return ast_parse_loop_expr_helper( - pc, - ast_parse_for_type_expr, - ast_parse_while_type_expr - ); -} - -// ForTypeExpr <- ForPrefix TypeExpr (KEYWORD_else TypeExpr)? -static AstNode *ast_parse_for_type_expr(ParseContext *pc) { - return ast_parse_for_expr_helper(pc, ast_parse_type_expr); -} - -// WhileTypeExpr <- WhilePrefix TypeExpr (KEYWORD_else Payload? TypeExpr)? -static AstNode *ast_parse_while_type_expr(ParseContext *pc) { - return ast_parse_while_expr_helper(pc, ast_parse_type_expr); -} - -// SwitchExpr <- KEYWORD_switch LPAREN Expr RPAREN LBRACE SwitchProngList RBRACE -static AstNode *ast_parse_switch_expr(ParseContext *pc) { - Token *switch_token = eat_token_if(pc, TokenIdKeywordSwitch); - if (switch_token == nullptr) - return nullptr; - - expect_token(pc, TokenIdLParen); - AstNode *expr = ast_expect(pc, ast_parse_expr); - expect_token(pc, TokenIdRParen); - expect_token(pc, TokenIdLBrace); - ZigList prongs = ast_parse_list(pc, TokenIdComma, ast_parse_switch_prong); - expect_token(pc, TokenIdRBrace); - - AstNode *res = ast_create_node(pc, NodeTypeSwitchExpr, switch_token); - res->data.switch_expr.expr = expr; - res->data.switch_expr.prongs = prongs; - return res; -} - -// AsmExpr <- KEYWORD_asm KEYWORD_volatile? LPAREN STRINGLITERAL AsmOutput? RPAREN -static AstNode *ast_parse_asm_expr(ParseContext *pc) { - Token *asm_token = eat_token_if(pc, TokenIdKeywordAsm); - if (asm_token == nullptr) - return nullptr; - - Token *volatile_token = eat_token_if(pc, TokenIdKeywordVolatile); - expect_token(pc, TokenIdLParen); - AstNode *asm_template = ast_expect(pc, ast_parse_expr); - AstNode *res = ast_parse_asm_output(pc); - if (res == nullptr) - res = ast_create_node_no_line_info(pc, NodeTypeAsmExpr); - expect_token(pc, TokenIdRParen); - - res->line = asm_token->start_line; - res->column = asm_token->start_column; - res->data.asm_expr.volatile_token = volatile_token; - res->data.asm_expr.asm_template = asm_template; - return res; -} - -static AstNode *ast_parse_anon_lit(ParseContext *pc) { - Token *period = eat_token_if(pc, TokenIdDot); - if (period == nullptr) - return nullptr; - - // anon enum literal - Token *identifier = eat_token_if(pc, TokenIdSymbol); - if (identifier != nullptr) { - AstNode *res = ast_create_node(pc, NodeTypeEnumLiteral, period); - res->data.enum_literal.period = period; - res->data.enum_literal.identifier = identifier; - return res; - } - - // anon container literal - AstNode *res = ast_parse_init_list(pc); - if (res != nullptr) - return res; - put_back_token(pc); - return nullptr; -} - -// AsmOutput <- COLON AsmOutputList AsmInput? -static AstNode *ast_parse_asm_output(ParseContext *pc) { - if (eat_token_if(pc, TokenIdColon) == nullptr) - return nullptr; - - ZigList output_list = ast_parse_list(pc, TokenIdComma, ast_parse_asm_output_item); - AstNode *res = ast_parse_asm_input(pc); - if (res == nullptr) - res = ast_create_node_no_line_info(pc, NodeTypeAsmExpr); - - res->data.asm_expr.output_list = output_list; - return res; -} - -// AsmOutputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN (MINUSRARROW TypeExpr / IDENTIFIER) RPAREN -static AsmOutput *ast_parse_asm_output_item(ParseContext *pc) { - if (eat_token_if(pc, TokenIdLBracket) == nullptr) - return nullptr; - - Token *sym_name = expect_token(pc, TokenIdSymbol); - expect_token(pc, TokenIdRBracket); - - Token *str = eat_token_if(pc, TokenIdMultilineStringLiteral); - if (str == nullptr) - str = expect_token(pc, TokenIdStringLiteral); - expect_token(pc, TokenIdLParen); - - Token *var_name = eat_token_if(pc, TokenIdSymbol); - AstNode *return_type = nullptr; - if (var_name == nullptr) { - expect_token(pc, TokenIdArrow); - return_type = ast_expect(pc, ast_parse_type_expr); - } - - expect_token(pc, TokenIdRParen); - - AsmOutput *res = heap::c_allocator.create(); - res->asm_symbolic_name = token_buf(sym_name); - res->constraint = token_buf(str); - res->variable_name = token_buf(var_name); - res->return_type = return_type; - return res; -} - -// AsmInput <- COLON AsmInputList AsmClobbers? -static AstNode *ast_parse_asm_input(ParseContext *pc) { - if (eat_token_if(pc, TokenIdColon) == nullptr) - return nullptr; - - ZigList input_list = ast_parse_list(pc, TokenIdComma, ast_parse_asm_input_item); - AstNode *res = ast_parse_asm_clobbers(pc); - if (res == nullptr) - res = ast_create_node_no_line_info(pc, NodeTypeAsmExpr); - - res->data.asm_expr.input_list = input_list; - return res; -} - -// AsmInputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN Expr RPAREN -static AsmInput *ast_parse_asm_input_item(ParseContext *pc) { - if (eat_token_if(pc, TokenIdLBracket) == nullptr) - return nullptr; - - Token *sym_name = expect_token(pc, TokenIdSymbol); - expect_token(pc, TokenIdRBracket); - - Token *constraint = eat_token_if(pc, TokenIdMultilineStringLiteral); - if (constraint == nullptr) - constraint = expect_token(pc, TokenIdStringLiteral); - expect_token(pc, TokenIdLParen); - AstNode *expr = ast_expect(pc, ast_parse_expr); - expect_token(pc, TokenIdRParen); - - AsmInput *res = heap::c_allocator.create(); - res->asm_symbolic_name = token_buf(sym_name); - res->constraint = token_buf(constraint); - res->expr = expr; - return res; -} - -// AsmClobbers <- COLON StringList -static AstNode *ast_parse_asm_clobbers(ParseContext *pc) { - if (eat_token_if(pc, TokenIdColon) == nullptr) - return nullptr; - - ZigList clobber_list = ast_parse_list(pc, TokenIdComma, [](ParseContext *context) { - Token *str = eat_token_if(context, TokenIdStringLiteral); - if (str == nullptr) - str = eat_token_if(context, TokenIdMultilineStringLiteral); - if (str != nullptr) - return token_buf(str); - return (Buf*)nullptr; - }); - - AstNode *res = ast_create_node_no_line_info(pc, NodeTypeAsmExpr); - res->data.asm_expr.clobber_list = clobber_list; - return res; -} - -// BreakLabel <- COLON IDENTIFIER -static Token *ast_parse_break_label(ParseContext *pc) { - if (eat_token_if(pc, TokenIdColon) == nullptr) - return nullptr; - - return expect_token(pc, TokenIdSymbol); -} - -// BlockLabel <- IDENTIFIER COLON -static Token *ast_parse_block_label(ParseContext *pc) { - Token *ident = eat_token_if(pc, TokenIdSymbol); - if (ident == nullptr) - return nullptr; - - // We do 2 token lookahead here, as we don't want to error when - // parsing identifiers. - if (eat_token_if(pc, TokenIdColon) == nullptr) { - put_back_token(pc); - return nullptr; - } - - return ident; -} - -// FieldInit <- DOT IDENTIFIER EQUAL Expr -static AstNode *ast_parse_field_init(ParseContext *pc) { - Token *first = eat_token_if(pc, TokenIdDot); - if (first == nullptr) - return nullptr; - - Token *name = eat_token_if(pc, TokenIdSymbol); - if (name == nullptr) { - // Because of anon literals ".{" is also valid. - put_back_token(pc); - return nullptr; - } - if (eat_token_if(pc, TokenIdEq) == nullptr) { - // Because ".Name" can also be intepreted as an enum literal, we should put back - // those two tokens again so that the parser can try to parse them as the enum - // literal later. - put_back_token(pc); - put_back_token(pc); - return nullptr; - } - AstNode *expr = ast_expect(pc, ast_parse_expr); - - AstNode *res = ast_create_node(pc, NodeTypeStructValueField, first); - res->data.struct_val_field.name = token_buf(name); - res->data.struct_val_field.expr = expr; - return res; -} - -// WhileContinueExpr <- COLON LPAREN AssignExpr RPAREN -static AstNode *ast_parse_while_continue_expr(ParseContext *pc) { - Token *first = eat_token_if(pc, TokenIdColon); - if (first == nullptr) - return nullptr; - - expect_token(pc, TokenIdLParen); - AstNode *expr = ast_expect(pc, ast_parse_assign_expr); - expect_token(pc, TokenIdRParen); - return expr; -} - -// LinkSection <- KEYWORD_linksection LPAREN Expr RPAREN -static AstNode *ast_parse_link_section(ParseContext *pc) { - Token *first = eat_token_if(pc, TokenIdKeywordLinkSection); - if (first == nullptr) - return nullptr; - - expect_token(pc, TokenIdLParen); - AstNode *res = ast_expect(pc, ast_parse_expr); - expect_token(pc, TokenIdRParen); - return res; -} - -// CallConv <- KEYWORD_callconv LPAREN Expr RPAREN -static AstNode *ast_parse_callconv(ParseContext *pc) { - Token *first = eat_token_if(pc, TokenIdKeywordCallconv); - if (first == nullptr) - return nullptr; - - expect_token(pc, TokenIdLParen); - AstNode *res = ast_expect(pc, ast_parse_expr); - expect_token(pc, TokenIdRParen); - return res; -} - -// ParamDecl <- (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType -static AstNode *ast_parse_param_decl(ParseContext *pc) { - Buf doc_comments = BUF_INIT; - ast_parse_doc_comments(pc, &doc_comments); - - Token *first = eat_token_if(pc, TokenIdKeywordNoAlias); - if (first == nullptr) - first = eat_token_if(pc, TokenIdKeywordCompTime); - - Token *name = eat_token_if(pc, TokenIdSymbol); - if (name != nullptr) { - if (eat_token_if(pc, TokenIdColon) != nullptr) { - if (first == nullptr) - first = name; - } else { - // We put back the ident, so it can be parsed as a ParamType - // later. - put_back_token(pc); - name = nullptr; - } - } - - AstNode *res; - if (first == nullptr) { - first = peek_token(pc); - res = ast_parse_param_type(pc); - } else { - res = ast_expect(pc, ast_parse_param_type); - } - - if (res == nullptr) - return nullptr; - - assert(res->type == NodeTypeParamDecl); - res->line = first->start_line; - res->column = first->start_column; - res->data.param_decl.name = token_buf(name); - res->data.param_decl.doc_comments = doc_comments; - res->data.param_decl.is_noalias = first->id == TokenIdKeywordNoAlias; - res->data.param_decl.is_comptime = first->id == TokenIdKeywordCompTime; - return res; -} - -// ParamType -// <- KEYWORD_anytype -// / DOT3 -// / TypeExpr -static AstNode *ast_parse_param_type(ParseContext *pc) { - Token *anytype_token = eat_token_if(pc, TokenIdKeywordAnyType); - if (anytype_token != nullptr) { - AstNode *res = ast_create_node(pc, NodeTypeParamDecl, anytype_token); - res->data.param_decl.anytype_token = anytype_token; - return res; - } - - Token *dots = eat_token_if(pc, TokenIdEllipsis3); - if (dots != nullptr) { - AstNode *res = ast_create_node(pc, NodeTypeParamDecl, dots); - res->data.param_decl.is_var_args = true; - return res; - } - - AstNode *type_expr = ast_parse_type_expr(pc); - if (type_expr != nullptr) { - AstNode *res = ast_create_node_copy_line_info(pc, NodeTypeParamDecl, type_expr); - res->data.param_decl.type = type_expr; - return res; - } - - return nullptr; -} - -// IfPrefix <- KEYWORD_if LPAREN Expr RPAREN PtrPayload? -static AstNode *ast_parse_if_prefix(ParseContext *pc) { - Token *first = eat_token_if(pc, TokenIdKeywordIf); - if (first == nullptr) - return nullptr; - - expect_token(pc, TokenIdLParen); - AstNode *condition = ast_expect(pc, ast_parse_expr); - expect_token(pc, TokenIdRParen); - Optional opt_payload = ast_parse_ptr_payload(pc); - - PtrPayload payload; - AstNode *res = ast_create_node(pc, NodeTypeIfOptional, first); - res->data.test_expr.target_node = condition; - if (opt_payload.unwrap(&payload)) { - res->data.test_expr.var_symbol = token_buf(payload.payload); - res->data.test_expr.var_is_ptr = payload.asterisk != nullptr; - } - return res; -} - -// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr? -static AstNode *ast_parse_while_prefix(ParseContext *pc) { - Token *while_token = eat_token_if(pc, TokenIdKeywordWhile); - if (while_token == nullptr) - return nullptr; - - expect_token(pc, TokenIdLParen); - AstNode *condition = ast_expect(pc, ast_parse_expr); - expect_token(pc, TokenIdRParen); - Optional opt_payload = ast_parse_ptr_payload(pc); - AstNode *continue_expr = ast_parse_while_continue_expr(pc); - - PtrPayload payload; - AstNode *res = ast_create_node(pc, NodeTypeWhileExpr, while_token); - res->data.while_expr.condition = condition; - res->data.while_expr.continue_expr = continue_expr; - if (opt_payload.unwrap(&payload)) { - res->data.while_expr.var_symbol = token_buf(payload.payload); - res->data.while_expr.var_is_ptr = payload.asterisk != nullptr; - } - - return res; -} - -// ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload -static AstNode *ast_parse_for_prefix(ParseContext *pc) { - Token *for_token = eat_token_if(pc, TokenIdKeywordFor); - if (for_token == nullptr) - return nullptr; - - expect_token(pc, TokenIdLParen); - AstNode *array_expr = ast_expect(pc, ast_parse_expr); - expect_token(pc, TokenIdRParen); - PtrIndexPayload payload; - if (!ast_parse_ptr_index_payload(pc).unwrap(&payload)) - ast_invalid_token_error(pc, peek_token(pc)); - - AstNode *res = ast_create_node(pc, NodeTypeForExpr, for_token); - res->data.for_expr.array_expr = array_expr; - res->data.for_expr.elem_node = token_symbol(pc, payload.payload); - res->data.for_expr.elem_is_ptr = payload.asterisk != nullptr; - if (payload.index != nullptr) - res->data.for_expr.index_node = token_symbol(pc, payload.index); - - return res; -} - -// Payload <- PIPE IDENTIFIER PIPE -static Token *ast_parse_payload(ParseContext *pc) { - if (eat_token_if(pc, TokenIdBinOr) == nullptr) - return nullptr; - - Token *res = expect_token(pc, TokenIdSymbol); - expect_token(pc, TokenIdBinOr); - return res; -} - -// PtrPayload <- PIPE ASTERISK? IDENTIFIER PIPE -static Optional ast_parse_ptr_payload(ParseContext *pc) { - if (eat_token_if(pc, TokenIdBinOr) == nullptr) - return Optional::none(); - - Token *asterisk = eat_token_if(pc, TokenIdStar); - Token *payload = expect_token(pc, TokenIdSymbol); - expect_token(pc, TokenIdBinOr); - - PtrPayload res; - res.asterisk = asterisk; - res.payload = payload; - return Optional::some(res); -} - -// PtrIndexPayload <- PIPE ASTERISK? IDENTIFIER (COMMA IDENTIFIER)? PIPE -static Optional ast_parse_ptr_index_payload(ParseContext *pc) { - if (eat_token_if(pc, TokenIdBinOr) == nullptr) - return Optional::none(); - - Token *asterisk = eat_token_if(pc, TokenIdStar); - Token *payload = expect_token(pc, TokenIdSymbol); - Token *index = nullptr; - if (eat_token_if(pc, TokenIdComma) != nullptr) - index = expect_token(pc, TokenIdSymbol); - expect_token(pc, TokenIdBinOr); - - PtrIndexPayload res; - res.asterisk = asterisk; - res.payload = payload; - res.index = index; - return Optional::some(res); -} - -// SwitchProng <- SwitchCase EQUALRARROW PtrPayload? AssignExpr -static AstNode *ast_parse_switch_prong(ParseContext *pc) { - AstNode *res = ast_parse_switch_case(pc); - if (res == nullptr) - return nullptr; - - expect_token(pc, TokenIdFatArrow); - Optional opt_payload = ast_parse_ptr_payload(pc); - AstNode *expr = ast_expect(pc, ast_parse_assign_expr); - - PtrPayload payload; - assert(res->type == NodeTypeSwitchProng); - res->data.switch_prong.expr = expr; - if (opt_payload.unwrap(&payload)) { - res->data.switch_prong.var_symbol = token_symbol(pc, payload.payload); - res->data.switch_prong.var_is_ptr = payload.asterisk != nullptr; - } - - return res; -} - -// SwitchCase -// <- SwitchItem (COMMA SwitchItem)* COMMA? -// / KEYWORD_else -static AstNode *ast_parse_switch_case(ParseContext *pc) { - AstNode *first = ast_parse_switch_item(pc); - if (first != nullptr) { - AstNode *res = ast_create_node_copy_line_info(pc, NodeTypeSwitchProng, first); - res->data.switch_prong.items.append(first); - res->data.switch_prong.any_items_are_range = first->type == NodeTypeSwitchRange; - - while (eat_token_if(pc, TokenIdComma) != nullptr) { - AstNode *item = ast_parse_switch_item(pc); - if (item == nullptr) - break; - - res->data.switch_prong.items.append(item); - res->data.switch_prong.any_items_are_range |= item->type == NodeTypeSwitchRange; - } - - return res; - } - - Token *else_token = eat_token_if(pc, TokenIdKeywordElse); - if (else_token != nullptr) - return ast_create_node(pc, NodeTypeSwitchProng, else_token); - - return nullptr; -} - -// SwitchItem <- Expr (DOT3 Expr)? -static AstNode *ast_parse_switch_item(ParseContext *pc) { - AstNode *expr = ast_parse_expr(pc); - if (expr == nullptr) - return nullptr; - - Token *dots = eat_token_if(pc, TokenIdEllipsis3); - if (dots != nullptr) { - AstNode *expr2 = ast_expect(pc, ast_parse_expr); - AstNode *res = ast_create_node(pc, NodeTypeSwitchRange, dots); - res->data.switch_range.start = expr; - res->data.switch_range.end = expr2; - return res; - } - - return expr; -} - -// AssignOp -// <- ASTERISKEQUAL -// / SLASHEQUAL -// / PERCENTEQUAL -// / PLUSEQUAL -// / MINUSEQUAL -// / LARROW2EQUAL -// / RARROW2EQUAL -// / AMPERSANDEQUAL -// / CARETEQUAL -// / PIPEEQUAL -// / ASTERISKPERCENTEQUAL -// / PLUSPERCENTEQUAL -// / MINUSPERCENTEQUAL -// / EQUAL -static AstNode *ast_parse_assign_op(ParseContext *pc) { - // In C, we have `T arr[N] = {[i] = T{}};` but it doesn't - // seem to work in C++... - BinOpType table[TokenIdCount] = {}; - table[TokenIdBarBarEq] = BinOpTypeAssignMergeErrorSets; - table[TokenIdBitAndEq] = BinOpTypeAssignBitAnd; - table[TokenIdBitOrEq] = BinOpTypeAssignBitOr; - table[TokenIdBitShiftLeftEq] = BinOpTypeAssignBitShiftLeft; - table[TokenIdBitShiftRightEq] = BinOpTypeAssignBitShiftRight; - table[TokenIdBitXorEq] = BinOpTypeAssignBitXor; - table[TokenIdDivEq] = BinOpTypeAssignDiv; - table[TokenIdEq] = BinOpTypeAssign; - table[TokenIdMinusEq] = BinOpTypeAssignMinus; - table[TokenIdMinusPercentEq] = BinOpTypeAssignMinusWrap; - table[TokenIdModEq] = BinOpTypeAssignMod; - table[TokenIdPlusEq] = BinOpTypeAssignPlus; - table[TokenIdPlusPercentEq] = BinOpTypeAssignPlusWrap; - table[TokenIdTimesEq] = BinOpTypeAssignTimes; - table[TokenIdTimesPercentEq] = BinOpTypeAssignTimesWrap; - - BinOpType op = table[peek_token(pc)->id]; - if (op != BinOpTypeInvalid) { - Token *op_token = eat_token(pc); - AstNode *res = ast_create_node(pc, NodeTypeBinOpExpr, op_token); - res->data.bin_op_expr.bin_op = op; - return res; - } - - return nullptr; - -} - -// CompareOp -// <- EQUALEQUAL -// / EXCLAMATIONMARKEQUAL -// / LARROW -// / RARROW -// / LARROWEQUAL -// / RARROWEQUAL -static AstNode *ast_parse_compare_op(ParseContext *pc) { - BinOpType table[TokenIdCount] = {}; - table[TokenIdCmpEq] = BinOpTypeCmpEq; - table[TokenIdCmpNotEq] = BinOpTypeCmpNotEq; - table[TokenIdCmpLessThan] = BinOpTypeCmpLessThan; - table[TokenIdCmpGreaterThan] = BinOpTypeCmpGreaterThan; - table[TokenIdCmpLessOrEq] = BinOpTypeCmpLessOrEq; - table[TokenIdCmpGreaterOrEq] = BinOpTypeCmpGreaterOrEq; - - BinOpType op = table[peek_token(pc)->id]; - if (op != BinOpTypeInvalid) { - Token *op_token = eat_token(pc); - AstNode *res = ast_create_node(pc, NodeTypeBinOpExpr, op_token); - res->data.bin_op_expr.bin_op = op; - return res; - } - - return nullptr; -} - -// BitwiseOp -// <- AMPERSAND -// / CARET -// / PIPE -// / KEYWORD_orelse -// / KEYWORD_catch Payload? -static AstNode *ast_parse_bitwise_op(ParseContext *pc) { - BinOpType table[TokenIdCount] = {}; - table[TokenIdAmpersand] = BinOpTypeBinAnd; - table[TokenIdBinXor] = BinOpTypeBinXor; - table[TokenIdBinOr] = BinOpTypeBinOr; - table[TokenIdKeywordOrElse] = BinOpTypeUnwrapOptional; - - BinOpType op = table[peek_token(pc)->id]; - if (op != BinOpTypeInvalid) { - Token *op_token = eat_token(pc); - AstNode *res = ast_create_node(pc, NodeTypeBinOpExpr, op_token); - res->data.bin_op_expr.bin_op = op; - return res; - } - - Token *catch_token = eat_token_if(pc, TokenIdKeywordCatch); - if (catch_token != nullptr) { - Token *payload = ast_parse_payload(pc); - AstNode *res = ast_create_node(pc, NodeTypeCatchExpr, catch_token); - if (payload != nullptr) - res->data.unwrap_err_expr.symbol = token_symbol(pc, payload); - - return res; - } - - return nullptr; -} - -// BitShiftOp -// <- LARROW2 -// / RARROW2 -static AstNode *ast_parse_bit_shift_op(ParseContext *pc) { - BinOpType table[TokenIdCount] = {}; - table[TokenIdBitShiftLeft] = BinOpTypeBitShiftLeft; - table[TokenIdBitShiftRight] = BinOpTypeBitShiftRight; - - BinOpType op = table[peek_token(pc)->id]; - if (op != BinOpTypeInvalid) { - Token *op_token = eat_token(pc); - AstNode *res = ast_create_node(pc, NodeTypeBinOpExpr, op_token); - res->data.bin_op_expr.bin_op = op; - return res; - } - - return nullptr; -} - -// AdditionOp -// <- PLUS -// / MINUS -// / PLUS2 -// / PLUSPERCENT -// / MINUSPERCENT -static AstNode *ast_parse_addition_op(ParseContext *pc) { - BinOpType table[TokenIdCount] = {}; - table[TokenIdPlus] = BinOpTypeAdd; - table[TokenIdDash] = BinOpTypeSub; - table[TokenIdPlusPlus] = BinOpTypeArrayCat; - table[TokenIdPlusPercent] = BinOpTypeAddWrap; - table[TokenIdMinusPercent] = BinOpTypeSubWrap; - - BinOpType op = table[peek_token(pc)->id]; - if (op != BinOpTypeInvalid) { - Token *op_token = eat_token(pc); - AstNode *res = ast_create_node(pc, NodeTypeBinOpExpr, op_token); - res->data.bin_op_expr.bin_op = op; - return res; - } - - return nullptr; -} - -// MultiplyOp -// <- PIPE2 -// / ASTERISK -// / SLASH -// / PERCENT -// / ASTERISK2 -// / ASTERISKPERCENT -static AstNode *ast_parse_multiply_op(ParseContext *pc) { - BinOpType table[TokenIdCount] = {}; - table[TokenIdBarBar] = BinOpTypeMergeErrorSets; - table[TokenIdStar] = BinOpTypeMult; - table[TokenIdSlash] = BinOpTypeDiv; - table[TokenIdPercent] = BinOpTypeMod; - table[TokenIdStarStar] = BinOpTypeArrayMult; - table[TokenIdTimesPercent] = BinOpTypeMultWrap; - - BinOpType op = table[peek_token(pc)->id]; - if (op != BinOpTypeInvalid) { - Token *op_token = eat_token(pc); - AstNode *res = ast_create_node(pc, NodeTypeBinOpExpr, op_token); - res->data.bin_op_expr.bin_op = op; - return res; - } - - return nullptr; -} - -// PrefixOp -// <- EXCLAMATIONMARK -// / MINUS -// / TILDE -// / MINUSPERCENT -// / AMPERSAND -// / KEYWORD_try -// / KEYWORD_await -static AstNode *ast_parse_prefix_op(ParseContext *pc) { - PrefixOp table[TokenIdCount] = {}; - table[TokenIdBang] = PrefixOpBoolNot; - table[TokenIdDash] = PrefixOpNegation; - table[TokenIdTilde] = PrefixOpBinNot; - table[TokenIdMinusPercent] = PrefixOpNegationWrap; - table[TokenIdAmpersand] = PrefixOpAddrOf; - - PrefixOp op = table[peek_token(pc)->id]; - if (op != PrefixOpInvalid) { - Token *op_token = eat_token(pc); - AstNode *res = ast_create_node(pc, NodeTypePrefixOpExpr, op_token); - res->data.prefix_op_expr.prefix_op = op; - return res; - } - - Token *try_token = eat_token_if(pc, TokenIdKeywordTry); - if (try_token != nullptr) { - AstNode *res = ast_create_node(pc, NodeTypeReturnExpr, try_token); - res->data.return_expr.kind = ReturnKindError; - return res; - } - - Token *await = eat_token_if(pc, TokenIdKeywordAwait); - if (await != nullptr) { - AstNode *res = ast_create_node(pc, NodeTypeAwaitExpr, await); - return res; - } - - return nullptr; -} - -// PrefixTypeOp -// <- QUESTIONMARK -// / KEYWORD_anyframe MINUSRARROW -// / ArrayTypeStart (ByteAlign / KEYWORD_const / KEYWORD_volatile)* -// / PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile)* -static AstNode *ast_parse_prefix_type_op(ParseContext *pc) { - Token *questionmark = eat_token_if(pc, TokenIdQuestion); - if (questionmark != nullptr) { - AstNode *res = ast_create_node(pc, NodeTypePrefixOpExpr, questionmark); - res->data.prefix_op_expr.prefix_op = PrefixOpOptional; - return res; - } - - Token *anyframe = eat_token_if(pc, TokenIdKeywordAnyFrame); - if (anyframe != nullptr) { - if (eat_token_if(pc, TokenIdArrow) != nullptr) { - AstNode *res = ast_create_node(pc, NodeTypeAnyFrameType, anyframe); - return res; - } - - put_back_token(pc); - } - - Token *arr_init_lbracket = eat_token_if(pc, TokenIdLBracket); - if (arr_init_lbracket != nullptr) { - Token *underscore = eat_token_if(pc, TokenIdSymbol); - if (underscore == nullptr) { - put_back_token(pc); - } else if (!buf_eql_str(token_buf(underscore), "_")) { - put_back_token(pc); - put_back_token(pc); - } else { - AstNode *sentinel = nullptr; - Token *colon = eat_token_if(pc, TokenIdColon); - if (colon != nullptr) { - sentinel = ast_expect(pc, ast_parse_expr); - } - expect_token(pc, TokenIdRBracket); - AstNode *node = ast_create_node(pc, NodeTypeInferredArrayType, arr_init_lbracket); - node->data.inferred_array_type.sentinel = sentinel; - return node; - } - } - - - AstNode *ptr = ast_parse_ptr_type_start(pc); - if (ptr != nullptr) { - assert(ptr->type == NodeTypePointerType); - // We might get two pointers from *_ptr_type_start - AstNode *child = ptr->data.pointer_type.op_expr; - if (child == nullptr) - child = ptr; - while (true) { - Token *allowzero_token = eat_token_if(pc, TokenIdKeywordAllowZero); - if (allowzero_token != nullptr) { - child->data.pointer_type.allow_zero_token = allowzero_token; - continue; - } - - if (eat_token_if(pc, TokenIdKeywordAlign) != nullptr) { - expect_token(pc, TokenIdLParen); - AstNode *align_expr = ast_expect(pc, ast_parse_expr); - child->data.pointer_type.align_expr = align_expr; - if (eat_token_if(pc, TokenIdColon) != nullptr) { - Token *bit_offset_start = expect_token(pc, TokenIdIntLiteral); - expect_token(pc, TokenIdColon); - Token *host_int_bytes = expect_token(pc, TokenIdIntLiteral); - child->data.pointer_type.bit_offset_start = token_bigint(bit_offset_start); - child->data.pointer_type.host_int_bytes = token_bigint(host_int_bytes); - } - expect_token(pc, TokenIdRParen); - continue; - } - - if (eat_token_if(pc, TokenIdKeywordConst) != nullptr) { - child->data.pointer_type.is_const = true; - continue; - } - - if (eat_token_if(pc, TokenIdKeywordVolatile) != nullptr) { - child->data.pointer_type.is_volatile = true; - continue; - } - - break; - } - - return ptr; - } - - AstNode *array = ast_parse_array_type_start(pc); - if (array != nullptr) { - assert(array->type == NodeTypeArrayType); - while (true) { - Token *allowzero_token = eat_token_if(pc, TokenIdKeywordAllowZero); - if (allowzero_token != nullptr) { - array->data.array_type.allow_zero_token = allowzero_token; - continue; - } - - AstNode *align_expr = ast_parse_byte_align(pc); - if (align_expr != nullptr) { - array->data.array_type.align_expr = align_expr; - continue; - } - - if (eat_token_if(pc, TokenIdKeywordConst) != nullptr) { - array->data.array_type.is_const = true; - continue; - } - - if (eat_token_if(pc, TokenIdKeywordVolatile) != nullptr) { - array->data.array_type.is_volatile = true; - continue; - } - break; - } - - return array; - } - - - return nullptr; -} - -// SuffixOp -// <- LBRACKET Expr (DOT2 (Expr (COLON Expr)?)?)? RBRACKET -// / DOT IDENTIFIER -// / DOTASTERISK -// / DOTQUESTIONMARK -static AstNode *ast_parse_suffix_op(ParseContext *pc) { - Token *lbracket = eat_token_if(pc, TokenIdLBracket); - if (lbracket != nullptr) { - AstNode *start = ast_expect(pc, ast_parse_expr); - AstNode *end = nullptr; - if (eat_token_if(pc, TokenIdEllipsis2) != nullptr) { - AstNode *sentinel = nullptr; - end = ast_parse_expr(pc); - if (eat_token_if(pc, TokenIdColon) != nullptr) { - sentinel = ast_parse_expr(pc); - } - expect_token(pc, TokenIdRBracket); - - AstNode *res = ast_create_node(pc, NodeTypeSliceExpr, lbracket); - res->data.slice_expr.start = start; - res->data.slice_expr.end = end; - res->data.slice_expr.sentinel = sentinel; - return res; - } - - expect_token(pc, TokenIdRBracket); - - AstNode *res = ast_create_node(pc, NodeTypeArrayAccessExpr, lbracket); - res->data.array_access_expr.subscript = start; - return res; - } - - Token *dot_asterisk = eat_token_if(pc, TokenIdDotStar); - if (dot_asterisk != nullptr) - return ast_create_node(pc, NodeTypePtrDeref, dot_asterisk); - - Token *dot = eat_token_if(pc, TokenIdDot); - if (dot != nullptr) { - if (eat_token_if(pc, TokenIdQuestion) != nullptr) - return ast_create_node(pc, NodeTypeUnwrapOptional, dot); - - Token *ident = expect_token(pc, TokenIdSymbol); - AstNode *res = ast_create_node(pc, NodeTypeFieldAccessExpr, dot); - res->data.field_access_expr.field_name = token_buf(ident); - return res; - } - - return nullptr; -} - -// FnCallArguments <- LPAREN ExprList RPAREN -static AstNode *ast_parse_fn_call_arguments(ParseContext *pc) { - Token *paren = eat_token_if(pc, TokenIdLParen); - if (paren == nullptr) - return nullptr; - - ZigList params = ast_parse_list(pc, TokenIdComma, ast_parse_expr); - expect_token(pc, TokenIdRParen); - - AstNode *res = ast_create_node(pc, NodeTypeFnCallExpr, paren); - res->data.fn_call_expr.params = params; - res->data.fn_call_expr.seen = false; - return res; -} - -// ArrayTypeStart <- LBRACKET Expr? RBRACKET -static AstNode *ast_parse_array_type_start(ParseContext *pc) { - Token *lbracket = eat_token_if(pc, TokenIdLBracket); - if (lbracket == nullptr) - return nullptr; - - AstNode *size = ast_parse_expr(pc); - AstNode *sentinel = nullptr; - Token *colon = eat_token_if(pc, TokenIdColon); - if (colon != nullptr) { - sentinel = ast_expect(pc, ast_parse_expr); - } - expect_token(pc, TokenIdRBracket); - AstNode *res = ast_create_node(pc, NodeTypeArrayType, lbracket); - res->data.array_type.size = size; - res->data.array_type.sentinel = sentinel; - return res; -} - -// PtrTypeStart -// <- ASTERISK -// / ASTERISK2 -// / PTRUNKNOWN -// / PTRC -static AstNode *ast_parse_ptr_type_start(ParseContext *pc) { - AstNode *sentinel = nullptr; - - Token *asterisk = eat_token_if(pc, TokenIdStar); - if (asterisk != nullptr) { - Token *colon = eat_token_if(pc, TokenIdColon); - if (colon != nullptr) { - sentinel = ast_expect(pc, ast_parse_expr); - } - AstNode *res = ast_create_node(pc, NodeTypePointerType, asterisk); - res->data.pointer_type.star_token = asterisk; - res->data.pointer_type.sentinel = sentinel; - return res; - } - - Token *asterisk2 = eat_token_if(pc, TokenIdStarStar); - if (asterisk2 != nullptr) { - Token *colon = eat_token_if(pc, TokenIdColon); - if (colon != nullptr) { - sentinel = ast_expect(pc, ast_parse_expr); - } - AstNode *res = ast_create_node(pc, NodeTypePointerType, asterisk2); - AstNode *res2 = ast_create_node(pc, NodeTypePointerType, asterisk2); - res->data.pointer_type.star_token = asterisk2; - res2->data.pointer_type.star_token = asterisk2; - res2->data.pointer_type.sentinel = sentinel; - res->data.pointer_type.op_expr = res2; - return res; - } - - Token *lbracket = eat_token_if(pc, TokenIdLBracket); - if (lbracket != nullptr) { - Token *star = eat_token_if(pc, TokenIdStar); - if (star == nullptr) { - put_back_token(pc); - } else { - Token *c_tok = eat_token_if(pc, TokenIdSymbol); - if (c_tok != nullptr) { - if (!buf_eql_str(token_buf(c_tok), "c")) { - put_back_token(pc); // c symbol - } else { - expect_token(pc, TokenIdRBracket); - AstNode *res = ast_create_node(pc, NodeTypePointerType, lbracket); - res->data.pointer_type.star_token = c_tok; - return res; - } - } - - Token *colon = eat_token_if(pc, TokenIdColon); - if (colon != nullptr) { - sentinel = ast_expect(pc, ast_parse_expr); - } - expect_token(pc, TokenIdRBracket); - AstNode *res = ast_create_node(pc, NodeTypePointerType, lbracket); - res->data.pointer_type.star_token = lbracket; - res->data.pointer_type.sentinel = sentinel; - return res; - } - } - - return nullptr; -} - -// ContainerDeclAuto <- ContainerDeclType LBRACE ContainerMembers RBRACE -static AstNode *ast_parse_container_decl_auto(ParseContext *pc) { - AstNode *res = ast_parse_container_decl_type(pc); - if (res == nullptr) - return nullptr; - - expect_token(pc, TokenIdLBrace); - AstNodeContainerDecl members = ast_parse_container_members(pc); - expect_token(pc, TokenIdRBrace); - - res->data.container_decl.fields = members.fields; - res->data.container_decl.decls = members.decls; - if (buf_len(&members.doc_comments) != 0) { - res->data.container_decl.doc_comments = members.doc_comments; - } - return res; -} - -// ContainerDeclType -// <- KEYWORD_struct -// / KEYWORD_enum (LPAREN Expr RPAREN)? -// / KEYWORD_union (LPAREN (KEYWORD_enum (LPAREN Expr RPAREN)? / Expr) RPAREN)? -static AstNode *ast_parse_container_decl_type(ParseContext *pc) { - Token *first = eat_token_if(pc, TokenIdKeywordStruct); - if (first != nullptr) { - AstNode *res = ast_create_node(pc, NodeTypeContainerDecl, first); - res->data.container_decl.init_arg_expr = nullptr; - res->data.container_decl.kind = ContainerKindStruct; - return res; - } - - first = eat_token_if(pc, TokenIdKeywordEnum); - if (first != nullptr) { - AstNode *init_arg_expr = nullptr; - if (eat_token_if(pc, TokenIdLParen) != nullptr) { - init_arg_expr = ast_expect(pc, ast_parse_expr); - expect_token(pc, TokenIdRParen); - } - AstNode *res = ast_create_node(pc, NodeTypeContainerDecl, first); - res->data.container_decl.init_arg_expr = init_arg_expr; - res->data.container_decl.kind = ContainerKindEnum; - return res; - } - - first = eat_token_if(pc, TokenIdKeywordUnion); - if (first != nullptr) { - AstNode *init_arg_expr = nullptr; - bool auto_enum = false; - if (eat_token_if(pc, TokenIdLParen) != nullptr) { - if (eat_token_if(pc, TokenIdKeywordEnum) != nullptr) { - auto_enum = true; - if (eat_token_if(pc, TokenIdLParen) != nullptr) { - init_arg_expr = ast_expect(pc, ast_parse_expr); - expect_token(pc, TokenIdRParen); - } - } else { - init_arg_expr = ast_expect(pc, ast_parse_expr); - } - - expect_token(pc, TokenIdRParen); - } - - AstNode *res = ast_create_node(pc, NodeTypeContainerDecl, first); - res->data.container_decl.init_arg_expr = init_arg_expr; - res->data.container_decl.auto_enum = auto_enum; - res->data.container_decl.kind = ContainerKindUnion; - return res; - } - - return nullptr; -} - -// ByteAlign <- KEYWORD_align LPAREN Expr RPAREN -static AstNode *ast_parse_byte_align(ParseContext *pc) { - if (eat_token_if(pc, TokenIdKeywordAlign) == nullptr) - return nullptr; - - expect_token(pc, TokenIdLParen); - AstNode *res = ast_expect(pc, ast_parse_expr); - expect_token(pc, TokenIdRParen); - return res; -} - -static void visit_field(AstNode **node, void (*visit)(AstNode **, void *context), void *context) { - if (*node) { - visit(node, context); - } -} - -static void visit_node_list(ZigList *list, void (*visit)(AstNode **, void *context), void *context) { - if (list) { - for (size_t i = 0; i < list->length; i += 1) { - visit(&list->at(i), context); - } - } -} - -void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *context), void *context) { - switch (node->type) { - case NodeTypeFnProto: - visit_field(&node->data.fn_proto.return_type, visit, context); - visit_node_list(&node->data.fn_proto.params, visit, context); - visit_field(&node->data.fn_proto.align_expr, visit, context); - visit_field(&node->data.fn_proto.section_expr, visit, context); - break; - case NodeTypeFnDef: - visit_field(&node->data.fn_def.fn_proto, visit, context); - visit_field(&node->data.fn_def.body, visit, context); - break; - case NodeTypeParamDecl: - visit_field(&node->data.param_decl.type, visit, context); - break; - case NodeTypeBlock: - visit_node_list(&node->data.block.statements, visit, context); - break; - case NodeTypeGroupedExpr: - visit_field(&node->data.grouped_expr, visit, context); - break; - case NodeTypeReturnExpr: - visit_field(&node->data.return_expr.expr, visit, context); - break; - case NodeTypeDefer: - visit_field(&node->data.defer.expr, visit, context); - visit_field(&node->data.defer.err_payload, visit, context); - break; - case NodeTypeVariableDeclaration: - visit_field(&node->data.variable_declaration.type, visit, context); - visit_field(&node->data.variable_declaration.expr, visit, context); - visit_field(&node->data.variable_declaration.align_expr, visit, context); - visit_field(&node->data.variable_declaration.section_expr, visit, context); - break; - case NodeTypeTestDecl: - visit_field(&node->data.test_decl.body, visit, context); - break; - case NodeTypeBinOpExpr: - visit_field(&node->data.bin_op_expr.op1, visit, context); - visit_field(&node->data.bin_op_expr.op2, visit, context); - break; - case NodeTypeCatchExpr: - visit_field(&node->data.unwrap_err_expr.op1, visit, context); - visit_field(&node->data.unwrap_err_expr.symbol, visit, context); - visit_field(&node->data.unwrap_err_expr.op2, visit, context); - break; - case NodeTypeIntLiteral: - // none - break; - case NodeTypeFloatLiteral: - // none - break; - case NodeTypeStringLiteral: - // none - break; - case NodeTypeCharLiteral: - // none - break; - case NodeTypeSymbol: - // none - break; - case NodeTypePrefixOpExpr: - visit_field(&node->data.prefix_op_expr.primary_expr, visit, context); - break; - case NodeTypeFnCallExpr: - visit_field(&node->data.fn_call_expr.fn_ref_expr, visit, context); - visit_node_list(&node->data.fn_call_expr.params, visit, context); - break; - case NodeTypeArrayAccessExpr: - visit_field(&node->data.array_access_expr.array_ref_expr, visit, context); - visit_field(&node->data.array_access_expr.subscript, visit, context); - break; - case NodeTypeSliceExpr: - visit_field(&node->data.slice_expr.array_ref_expr, visit, context); - visit_field(&node->data.slice_expr.start, visit, context); - visit_field(&node->data.slice_expr.end, visit, context); - visit_field(&node->data.slice_expr.sentinel, visit, context); - break; - case NodeTypeFieldAccessExpr: - visit_field(&node->data.field_access_expr.struct_expr, visit, context); - break; - case NodeTypePtrDeref: - visit_field(&node->data.ptr_deref_expr.target, visit, context); - break; - case NodeTypeUnwrapOptional: - visit_field(&node->data.unwrap_optional.expr, visit, context); - break; - case NodeTypeUsingNamespace: - visit_field(&node->data.using_namespace.expr, visit, context); - break; - case NodeTypeBoolLiteral: - // none - break; - case NodeTypeNullLiteral: - // none - break; - case NodeTypeUndefinedLiteral: - // none - break; - case NodeTypeIfBoolExpr: - visit_field(&node->data.if_bool_expr.condition, visit, context); - visit_field(&node->data.if_bool_expr.then_block, visit, context); - visit_field(&node->data.if_bool_expr.else_node, visit, context); - break; - case NodeTypeIfErrorExpr: - visit_field(&node->data.if_err_expr.target_node, visit, context); - visit_field(&node->data.if_err_expr.then_node, visit, context); - visit_field(&node->data.if_err_expr.else_node, visit, context); - break; - case NodeTypeIfOptional: - visit_field(&node->data.test_expr.target_node, visit, context); - visit_field(&node->data.test_expr.then_node, visit, context); - visit_field(&node->data.test_expr.else_node, visit, context); - break; - case NodeTypeWhileExpr: - visit_field(&node->data.while_expr.condition, visit, context); - visit_field(&node->data.while_expr.body, visit, context); - break; - case NodeTypeForExpr: - visit_field(&node->data.for_expr.elem_node, visit, context); - visit_field(&node->data.for_expr.array_expr, visit, context); - visit_field(&node->data.for_expr.index_node, visit, context); - visit_field(&node->data.for_expr.body, visit, context); - break; - case NodeTypeSwitchExpr: - visit_field(&node->data.switch_expr.expr, visit, context); - visit_node_list(&node->data.switch_expr.prongs, visit, context); - break; - case NodeTypeSwitchProng: - visit_node_list(&node->data.switch_prong.items, visit, context); - visit_field(&node->data.switch_prong.var_symbol, visit, context); - visit_field(&node->data.switch_prong.expr, visit, context); - break; - case NodeTypeSwitchRange: - visit_field(&node->data.switch_range.start, visit, context); - visit_field(&node->data.switch_range.end, visit, context); - break; - case NodeTypeCompTime: - visit_field(&node->data.comptime_expr.expr, visit, context); - break; - case NodeTypeNoSuspend: - visit_field(&node->data.comptime_expr.expr, visit, context); - break; - case NodeTypeBreak: - // none - break; - case NodeTypeContinue: - // none - break; - case NodeTypeUnreachable: - // none - break; - case NodeTypeAsmExpr: - for (size_t i = 0; i < node->data.asm_expr.input_list.length; i += 1) { - AsmInput *asm_input = node->data.asm_expr.input_list.at(i); - visit_field(&asm_input->expr, visit, context); - } - for (size_t i = 0; i < node->data.asm_expr.output_list.length; i += 1) { - AsmOutput *asm_output = node->data.asm_expr.output_list.at(i); - visit_field(&asm_output->return_type, visit, context); - } - break; - case NodeTypeContainerDecl: - visit_node_list(&node->data.container_decl.fields, visit, context); - visit_node_list(&node->data.container_decl.decls, visit, context); - visit_field(&node->data.container_decl.init_arg_expr, visit, context); - break; - case NodeTypeStructField: - visit_field(&node->data.struct_field.type, visit, context); - visit_field(&node->data.struct_field.value, visit, context); - break; - case NodeTypeContainerInitExpr: - visit_field(&node->data.container_init_expr.type, visit, context); - visit_node_list(&node->data.container_init_expr.entries, visit, context); - break; - case NodeTypeStructValueField: - visit_field(&node->data.struct_val_field.expr, visit, context); - break; - case NodeTypeArrayType: - visit_field(&node->data.array_type.size, visit, context); - visit_field(&node->data.array_type.sentinel, visit, context); - visit_field(&node->data.array_type.child_type, visit, context); - visit_field(&node->data.array_type.align_expr, visit, context); - break; - case NodeTypeInferredArrayType: - visit_field(&node->data.array_type.sentinel, visit, context); - visit_field(&node->data.array_type.child_type, visit, context); - break; - case NodeTypeAnyFrameType: - visit_field(&node->data.anyframe_type.payload_type, visit, context); - break; - case NodeTypeErrorType: - // none - break; - case NodeTypePointerType: - visit_field(&node->data.pointer_type.sentinel, visit, context); - visit_field(&node->data.pointer_type.align_expr, visit, context); - visit_field(&node->data.pointer_type.op_expr, visit, context); - break; - case NodeTypeErrorSetDecl: - visit_node_list(&node->data.err_set_decl.decls, visit, context); - break; - case NodeTypeErrorSetField: - visit_field(&node->data.err_set_field.field_name, visit, context); - break; - case NodeTypeResume: - visit_field(&node->data.resume_expr.expr, visit, context); - break; - case NodeTypeAwaitExpr: - visit_field(&node->data.await_expr.expr, visit, context); - break; - case NodeTypeSuspend: - visit_field(&node->data.suspend.block, visit, context); - break; - case NodeTypeEnumLiteral: - case NodeTypeAnyTypeField: - break; - } -} diff --git a/src/parser.hpp b/src/parser.hpp deleted file mode 100644 index 73950993f39421b0d80a5a3afaac7d38a91e07c2..0000000000000000000000000000000000000000 --- a/src/parser.hpp +++ /dev/null @@ -1,25 +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_PARSER_HPP -#define ZIG_PARSER_HPP - -#include "all_types.hpp" -#include "tokenizer.hpp" -#include "errmsg.hpp" - -ATTRIBUTE_PRINTF(2, 3) -void ast_token_error(Token *token, const char *format, ...); - - -AstNode * ast_parse(Buf *buf, ZigList *tokens, ZigType *owner, ErrColor err_color); - -void ast_print(AstNode *node, int indent); - -void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *context), void *context); - -#endif diff --git a/src/print_env.zig b/src/print_env.zig new file mode 100644 index 0000000000000000000000000000000000000000..d1956911e9300625e6da291cefc165f11ef46863 --- /dev/null +++ b/src/print_env.zig @@ -0,0 +1,47 @@ +const std = @import("std"); +const build_options = @import("build_options"); +const introspect = @import("introspect.zig"); +const Allocator = std.mem.Allocator; +const fatal = @import("main.zig").fatal; + +pub fn cmdEnv(gpa: *Allocator, args: []const []const u8, stdout: anytype) !void { + const self_exe_path = try std.fs.selfExePathAlloc(gpa); + defer gpa.free(self_exe_path); + + var zig_lib_directory = introspect.findZigLibDirFromSelfExe(gpa, self_exe_path) catch |err| { + fatal("unable to find zig installation directory: {}\n", .{@errorName(err)}); + }; + defer gpa.free(zig_lib_directory.path.?); + defer zig_lib_directory.handle.close(); + + const zig_std_dir = try std.fs.path.join(gpa, &[_][]const u8{ zig_lib_directory.path.?, "std" }); + defer gpa.free(zig_std_dir); + + const global_cache_dir = try introspect.resolveGlobalCacheDir(gpa); + defer gpa.free(global_cache_dir); + + var bos = std.io.bufferedOutStream(stdout); + const bos_stream = bos.outStream(); + + var jws = std.json.WriteStream(@TypeOf(bos_stream), 6).init(bos_stream); + try jws.beginObject(); + + try jws.objectField("zig_exe"); + try jws.emitString(self_exe_path); + + try jws.objectField("lib_dir"); + try jws.emitString(zig_lib_directory.path.?); + + try jws.objectField("std_dir"); + try jws.emitString(zig_std_dir); + + 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/print_targets.zig b/src/print_targets.zig new file mode 100644 index 0000000000000000000000000000000000000000..724cb7a9ac394bfeb17563d51041a6944b386e42 --- /dev/null +++ b/src/print_targets.zig @@ -0,0 +1,161 @@ +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 target = @import("target.zig"); +const assert = std.debug.assert; +const glibc = @import("glibc.zig"); +const introspect = @import("introspect.zig"); +const fatal = @import("main.zig").fatal; + +pub fn cmdTargets( + allocator: *Allocator, + args: []const []const u8, + /// Output stream + stdout: anytype, + native_target: Target, +) !void { + var zig_lib_directory = introspect.findZigLibDir(allocator) catch |err| { + fatal("unable to find zig installation directory: {}\n", .{@errorName(err)}); + }; + defer zig_lib_directory.handle.close(); + defer allocator.free(zig_lib_directory.path.?); + + const glibc_abi = try glibc.loadMetaData(allocator, zig_lib_directory.handle); + defer glibc_abi.destroy(allocator); + + var bos = io.bufferedOutStream(stdout); + const bos_stream = bos.outStream(); + 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 (target.available_libcs) |libc| { + const tmp = try std.fmt.allocPrint(allocator, "{}-{}-{}", .{ + @tagName(libc.arch), @tagName(libc.os), @tagName(libc.abi), + }); + defer allocator.free(tmp); + try jws.arrayElem(); + try jws.emitString(tmp); + } + try jws.endArray(); + + try jws.objectField("glibc"); + try jws.beginArray(); + for (glibc_abi.all_versions) |ver| { + try jws.arrayElem(); + + const tmp = try std.fmt.allocPrint(allocator, "{}", .{ver}); + 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)); + try jws.endObject(); + + try jws.endObject(); + + try bos_stream.writeByte('\n'); + return bos.flush(); +} diff --git a/src/range_set.cpp b/src/range_set.cpp deleted file mode 100644 index 9e621d2f1305fd0b9b37dd149ceb0c6542fc1830..0000000000000000000000000000000000000000 --- a/src/range_set.cpp +++ /dev/null @@ -1,74 +0,0 @@ -#include "range_set.hpp" - -AstNode *rangeset_add_range(RangeSet *rs, BigInt *first, BigInt *last, AstNode *source_node) { - for (size_t i = 0; i < rs->src_range_list.length; i += 1) { - RangeWithSrc *range_with_src = &rs->src_range_list.at(i); - Range *range = &range_with_src->range; - if ((bigint_cmp(first, &range->first) == CmpLT && bigint_cmp(last, &range->first) == CmpLT) || - (bigint_cmp(first, &range->last) == CmpGT && bigint_cmp(last, &range->last) == CmpGT)) - { - // first...last is completely before/after `range` - } - else - { - return range_with_src->source_node; - } - } - rs->src_range_list.append({{*first, *last}, source_node}); - - return nullptr; - -} - -static int compare_rangeset(const void *a, const void *b) { - const Range *r1 = &static_cast(a)->range; - const Range *r2 = &static_cast(b)->range; - // Assume no two ranges overlap - switch (bigint_cmp(&r1->first, &r2->first)) { - case CmpLT: return -1; - case CmpGT: return 1; - case CmpEQ: return 0; - } - zig_unreachable(); -} - -void rangeset_sort(RangeSet *rs) { - if (rs->src_range_list.length > 1) { - qsort(rs->src_range_list.items, rs->src_range_list.length, - sizeof(RangeWithSrc), compare_rangeset); - } -} - -bool rangeset_spans(RangeSet *rs, BigInt *first, BigInt *last) { - if (rs->src_range_list.length == 0) - return false; - - rangeset_sort(rs); - - const Range *first_range = &rs->src_range_list.at(0).range; - if (bigint_cmp(&first_range->first, first) != CmpEQ) - return false; - - const Range *last_range = &rs->src_range_list.last().range; - if (bigint_cmp(&last_range->last, last) != CmpEQ) - return false; - - BigInt one; - bigint_init_unsigned(&one, 1); - - // Make sure there are no holes in the first...last range - for (size_t i = 1; i < rs->src_range_list.length; i++) { - const Range *range = &rs->src_range_list.at(i).range; - const Range *prev_range = &rs->src_range_list.at(i - 1).range; - - assert(bigint_cmp(&prev_range->last, &range->first) == CmpLT); - - BigInt last_plus_one; - bigint_add(&last_plus_one, &prev_range->last, &one); - - if (bigint_cmp(&last_plus_one, &range->first) != CmpEQ) - return false; - } - - return true; -} diff --git a/src/range_set.hpp b/src/range_set.hpp deleted file mode 100644 index 9164a8b5c0a50fbc22feb602771ce84ad7071cf1..0000000000000000000000000000000000000000 --- a/src/range_set.hpp +++ /dev/null @@ -1,30 +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_RANGE_SET_HPP -#define ZIG_RANGE_SET_HPP - -#include "all_types.hpp" - -struct Range { - BigInt first; - BigInt last; -}; - -struct RangeWithSrc { - Range range; - AstNode *source_node; -}; - -struct RangeSet { - ZigList src_range_list; -}; - -AstNode *rangeset_add_range(RangeSet *rs, BigInt *first, BigInt *last, AstNode *source_node); -bool rangeset_spans(RangeSet *rs, BigInt *first, BigInt *last); - -#endif diff --git a/src/softfloat.hpp b/src/softfloat.hpp deleted file mode 100644 index a1173690b549623b974397f00856483907e4f2a1..0000000000000000000000000000000000000000 --- a/src/softfloat.hpp +++ /dev/null @@ -1,32 +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_SOFTFLOAT_HPP -#define ZIG_SOFTFLOAT_HPP - -extern "C" { -#include "softfloat.h" -} - -static inline float16_t zig_double_to_f16(double x) { - float64_t y; - static_assert(sizeof(x) == sizeof(y), ""); - memcpy(&y, &x, sizeof(x)); - return f64_to_f16(y); -} - - -// Return value is safe to coerce to float even when |x| is NaN or Infinity. -static inline double zig_f16_to_double(float16_t x) { - float64_t y = f16_to_f64(x); - double z; - static_assert(sizeof(y) == sizeof(z), ""); - memcpy(&z, &y, sizeof(y)); - return z; -} - -#endif diff --git a/src/softfloat_ext.cpp b/src/softfloat_ext.cpp deleted file mode 100644 index 8408a1511682fdc6384870a8cef7ee6501ce5118..0000000000000000000000000000000000000000 --- a/src/softfloat_ext.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include "softfloat_ext.hpp" - -extern "C" { - #include "softfloat.h" -} - -void f128M_abs(const float128_t *aPtr, float128_t *zPtr) { - float128_t zero_float; - ui32_to_f128M(0, &zero_float); - if (f128M_lt(aPtr, &zero_float)) { - f128M_sub(&zero_float, aPtr, zPtr); - } else { - *zPtr = *aPtr; - } -} - -void f128M_trunc(const float128_t *aPtr, float128_t *zPtr) { - float128_t zero_float; - ui32_to_f128M(0, &zero_float); - if (f128M_lt(aPtr, &zero_float)) { - f128M_roundToInt(aPtr, softfloat_round_max, false, zPtr); - } else { - f128M_roundToInt(aPtr, softfloat_round_min, false, zPtr); - } -} \ No newline at end of file diff --git a/src/softfloat_ext.hpp b/src/softfloat_ext.hpp deleted file mode 100644 index 0a1f9589334ac5d1af151affd3765bc366598c26..0000000000000000000000000000000000000000 --- a/src/softfloat_ext.hpp +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef ZIG_SOFTFLOAT_EXT_HPP -#define ZIG_SOFTFLOAT_EXT_HPP - -#include "softfloat_types.h" - -void f128M_abs(const float128_t *aPtr, float128_t *zPtr); -void f128M_trunc(const float128_t *aPtr, float128_t *zPtr); - -#endif \ No newline at end of file diff --git a/src/stage1.zig b/src/stage1.zig new file mode 100644 index 0000000000000000000000000000000000000000..a989ad4be388f662e928d8a311ad97837d3712df --- /dev/null +++ b/src/stage1.zig @@ -0,0 +1,426 @@ +//! This is the main entry point for the Zig/C++ hybrid compiler (stage1). +//! It has the functions exported from Zig, called in C++, and bindings for +//! the functions exported from C++, called from Zig. + +const std = @import("std"); +const assert = std.debug.assert; +const mem = std.mem; +const CrossTarget = std.zig.CrossTarget; +const Target = std.Target; + +const build_options = @import("build_options"); +const stage2 = @import("main.zig"); +const fatal = stage2.fatal; +const Compilation = @import("Compilation.zig"); +const translate_c = @import("translate_c.zig"); +const target_util = @import("target.zig"); + +comptime { + assert(std.builtin.link_libc); + assert(build_options.is_stage1); + assert(build_options.have_llvm); + _ = @import("compiler_rt"); +} + +pub const log = stage2.log; +pub const log_level = stage2.log_level; + +pub export fn main(argc: c_int, argv: [*]const [*:0]const u8) c_int { + std.debug.maybeEnableSegfaultHandler(); + + zig_stage1_os_init(); + + const gpa = std.heap.c_allocator; + var arena_instance = std.heap.ArenaAllocator.init(gpa); + defer arena_instance.deinit(); + const arena = &arena_instance.allocator; + + const args = arena.alloc([]const u8, @intCast(usize, argc)) catch fatal("{}", .{"OutOfMemory"}); + for (args) |*arg, i| { + arg.* = mem.spanZ(argv[i]); + } + stage2.mainArgs(gpa, arena, args) catch |err| fatal("{}", .{@errorName(err)}); + return 0; +} + +/// Matches stage2.Color; +pub const ErrColor = c_int; +/// Matches std.builtin.CodeModel +pub const CodeModel = c_int; +/// Matches std.Target.Os.Tag +pub const OS = c_int; +/// Matches std.builtin.BuildMode +pub const BuildMode = c_int; + +pub const TargetSubsystem = extern enum(c_int) { + Console, + Windows, + Posix, + Native, + EfiApplication, + EfiBootServiceDriver, + EfiRom, + EfiRuntimeDriver, + Auto, +}; + +pub const Pkg = extern struct { + name_ptr: [*]const u8, + name_len: usize, + path_ptr: [*]const u8, + path_len: usize, + children_ptr: [*]*Pkg, + children_len: usize, + parent: ?*Pkg, +}; + +pub const Module = extern struct { + root_name_ptr: [*]const u8, + root_name_len: usize, + emit_o_ptr: [*]const u8, + emit_o_len: usize, + emit_h_ptr: [*]const u8, + emit_h_len: usize, + emit_asm_ptr: [*]const u8, + emit_asm_len: usize, + emit_llvm_ir_ptr: [*]const u8, + emit_llvm_ir_len: usize, + emit_analysis_json_ptr: [*]const u8, + emit_analysis_json_len: usize, + emit_docs_ptr: [*]const u8, + emit_docs_len: usize, + builtin_zig_path_ptr: [*]const u8, + builtin_zig_path_len: usize, + test_filter_ptr: [*]const u8, + test_filter_len: usize, + test_name_prefix_ptr: [*]const u8, + test_name_prefix_len: usize, + userdata: usize, + root_pkg: *Pkg, + main_progress_node: ?*std.Progress.Node, + code_model: CodeModel, + subsystem: TargetSubsystem, + err_color: ErrColor, + pic: bool, + link_libc: bool, + link_libcpp: bool, + strip: bool, + is_single_threaded: bool, + dll_export_fns: bool, + link_mode_dynamic: bool, + valgrind_enabled: bool, + function_sections: bool, + enable_stack_probing: bool, + enable_time_report: bool, + enable_stack_report: bool, + test_is_evented: bool, + verbose_tokenize: bool, + verbose_ast: bool, + verbose_ir: bool, + verbose_llvm_ir: bool, + verbose_cimport: bool, + verbose_llvm_cpu_features: bool, + + // Set by stage1 + have_c_main: bool, + have_winmain: bool, + have_wwinmain: bool, + have_winmain_crt_startup: bool, + have_wwinmain_crt_startup: bool, + have_dllmain_crt_startup: bool, + + pub fn build_object(mod: *Module) void { + zig_stage1_build_object(mod); + } + + pub fn destroy(mod: *Module) void { + zig_stage1_destroy(mod); + } +}; + +extern fn zig_stage1_os_init() void; + +pub const create = zig_stage1_create; +extern fn zig_stage1_create( + optimize_mode: BuildMode, + main_pkg_path_ptr: [*]const u8, + main_pkg_path_len: usize, + root_src_path_ptr: [*]const u8, + root_src_path_len: usize, + zig_lib_dir_ptr: [*c]const u8, + zig_lib_dir_len: usize, + target: [*c]const Stage2Target, + is_test_build: bool, +) ?*Module; + +extern fn zig_stage1_build_object(*Module) void; +extern fn zig_stage1_destroy(*Module) void; + +// ABI warning +export fn stage2_panic(ptr: [*]const u8, len: usize) void { + @panic(ptr[0..len]); +} + +// ABI warning +const Error = extern enum { + None, + OutOfMemory, + InvalidFormat, + SemanticAnalyzeFail, + AccessDenied, + Interrupted, + SystemResources, + FileNotFound, + FileSystem, + FileTooBig, + DivByZero, + Overflow, + PathAlreadyExists, + Unexpected, + ExactDivRemainder, + NegativeDenominator, + ShiftedOutOneBits, + CCompileErrors, + EndOfFile, + IsDir, + NotDir, + UnsupportedOperatingSystem, + SharingViolation, + PipeBusy, + PrimitiveTypeNotFound, + CacheUnavailable, + PathTooLong, + CCompilerCannotFindFile, + NoCCompilerInstalled, + ReadingDepFile, + InvalidDepFile, + MissingArchitecture, + MissingOperatingSystem, + UnknownArchitecture, + UnknownOperatingSystem, + UnknownABI, + InvalidFilename, + DiskQuota, + DiskSpace, + UnexpectedWriteFailure, + UnexpectedSeekFailure, + UnexpectedFileTruncationFailure, + Unimplemented, + OperationAborted, + BrokenPipe, + NoSpaceLeft, + NotLazy, + IsAsync, + ImportOutsidePkgPath, + UnknownCpuModel, + UnknownCpuFeature, + InvalidCpuFeatures, + InvalidLlvmCpuFeaturesFormat, + UnknownApplicationBinaryInterface, + ASTUnitFailure, + BadPathName, + SymLinkLoop, + ProcessFdQuotaExceeded, + SystemFdQuotaExceeded, + NoDevice, + DeviceBusy, + UnableToSpawnCCompiler, + CCompilerExitCode, + CCompilerCrashed, + CCompilerCannotFindHeaders, + LibCRuntimeNotFound, + LibCStdLibHeaderNotFound, + LibCKernel32LibNotFound, + UnsupportedArchitecture, + WindowsSdkNotFound, + UnknownDynamicLinkerPath, + TargetHasNoDynamicLinker, + InvalidAbiVersion, + InvalidOperatingSystemVersion, + UnknownClangOption, + NestedResponseFile, + ZigIsTheCCompiler, + FileBusy, + Locked, +}; + +// ABI warning +export fn stage2_attach_segfault_handler() void { + if (std.debug.runtime_safety and std.debug.have_segfault_handling_support) { + std.debug.attachSegfaultHandler(); + } +} + +// ABI warning +export fn stage2_progress_create() *std.Progress { + const ptr = std.heap.c_allocator.create(std.Progress) catch @panic("out of memory"); + ptr.* = std.Progress{}; + return ptr; +} + +// ABI warning +export fn stage2_progress_destroy(progress: *std.Progress) void { + std.heap.c_allocator.destroy(progress); +} + +// ABI warning +export fn stage2_progress_start_root( + progress: *std.Progress, + name_ptr: [*]const u8, + name_len: usize, + estimated_total_items: usize, +) *std.Progress.Node { + return progress.start( + name_ptr[0..name_len], + if (estimated_total_items == 0) null else estimated_total_items, + ) catch @panic("timer unsupported"); +} + +// ABI warning +export fn stage2_progress_disable_tty(progress: *std.Progress) void { + progress.terminal = null; +} + +// ABI warning +export fn stage2_progress_start( + node: *std.Progress.Node, + name_ptr: [*]const u8, + name_len: usize, + estimated_total_items: usize, +) *std.Progress.Node { + const child_node = std.heap.c_allocator.create(std.Progress.Node) catch @panic("out of memory"); + child_node.* = node.start( + name_ptr[0..name_len], + if (estimated_total_items == 0) null else estimated_total_items, + ); + child_node.activate(); + return child_node; +} + +// ABI warning +export fn stage2_progress_end(node: *std.Progress.Node) void { + node.end(); + if (&node.context.root != node) { + std.heap.c_allocator.destroy(node); + } +} + +// ABI warning +export fn stage2_progress_complete_one(node: *std.Progress.Node) void { + node.completeOne(); +} + +// ABI warning +export fn stage2_progress_update_node(node: *std.Progress.Node, done_count: usize, total_count: usize) void { + node.completed_items = done_count; + node.estimated_total_items = total_count; + node.activate(); + node.context.maybeRefresh(); +} + +// ABI warning +pub const Stage2Target = extern struct { + arch: c_int, + os: OS, + abi: c_int, + + is_native_os: bool, + is_native_cpu: bool, + + llvm_cpu_name: ?[*:0]const u8, + llvm_cpu_features: ?[*:0]const u8, +}; + +// ABI warning +const Stage2SemVer = extern struct { + major: u32, + minor: u32, + patch: u32, +}; + +// ABI warning +export fn stage2_cimport( + stage1: *Module, + c_src_ptr: [*]const u8, + c_src_len: usize, + out_zig_path_ptr: *[*]const u8, + out_zig_path_len: *usize, + out_errors_ptr: *[*]translate_c.ClangErrMsg, + out_errors_len: *usize, +) Error { + const comp = @intToPtr(*Compilation, stage1.userdata); + const c_src = c_src_ptr[0..c_src_len]; + const result = comp.cImport(c_src) catch |err| switch (err) { + error.SystemResources => return .SystemResources, + error.OperationAborted => return .OperationAborted, + error.BrokenPipe => return .BrokenPipe, + error.DiskQuota => return .DiskQuota, + error.FileTooBig => return .FileTooBig, + error.NoSpaceLeft => return .NoSpaceLeft, + error.AccessDenied => return .AccessDenied, + error.OutOfMemory => return .OutOfMemory, + error.Unexpected => return .Unexpected, + error.InputOutput => return .FileSystem, + error.ASTUnitFailure => return .ASTUnitFailure, + error.CacheUnavailable => return .CacheUnavailable, + else => return .Unexpected, + }; + out_zig_path_ptr.* = result.out_zig_path.ptr; + out_zig_path_len.* = result.out_zig_path.len; + out_errors_ptr.* = result.errors.ptr; + out_errors_len.* = result.errors.len; + if (result.errors.len != 0) return .CCompileErrors; + return Error.None; +} + +export fn stage2_add_link_lib( + stage1: *Module, + lib_name_ptr: [*c]const u8, + lib_name_len: usize, + symbol_name_ptr: [*c]const u8, + symbol_name_len: usize, +) ?[*:0]const u8 { + const comp = @intToPtr(*Compilation, stage1.userdata); + const lib_name = std.ascii.allocLowerString(comp.gpa, lib_name_ptr[0..lib_name_len]) catch return "out of memory"; + const target = comp.getTarget(); + const is_libc = target_util.is_libc_lib_name(target, lib_name); + if (is_libc) { + if (!comp.bin_file.options.link_libc) { + return "dependency on libc must be explicitly specified in the build command"; + } + return null; + } + if (target_util.is_libcpp_lib_name(target, lib_name)) { + if (!comp.bin_file.options.link_libcpp) { + return "dependency on libc++ must be explicitly specified in the build command"; + } + return null; + } + if (!target.isWasm() and !comp.bin_file.options.pic) { + return std.fmt.allocPrint0( + comp.gpa, + "dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by `-l{s}` or `-fPIC`.", + .{ lib_name, lib_name }, + ) catch "out of memory"; + } + comp.stage1AddLinkLib(lib_name) catch |err| { + return std.fmt.allocPrint0(comp.gpa, "unable to add link lib '{s}': {s}", .{ + lib_name, @errorName(err), + }) catch "out of memory"; + }; + return null; +} + +export fn stage2_fetch_file( + stage1: *Module, + path_ptr: [*]const u8, + path_len: usize, + result_len: *usize, +) ?[*]const u8 { + const comp = @intToPtr(*Compilation, stage1.userdata); + const file_path = path_ptr[0..path_len]; + const max_file_size = std.math.maxInt(u32); + const contents = comp.stage1_cache_manifest.addFilePostFetch(file_path, max_file_size) catch return null; + result_len.* = contents.len; + return contents.ptr; +} diff --git a/src/stage1/all_types.hpp b/src/stage1/all_types.hpp new file mode 100644 index 0000000000000000000000000000000000000000..7a5016d004d753b5b12b9c5045c92716d3beb10b --- /dev/null +++ b/src/stage1/all_types.hpp @@ -0,0 +1,4642 @@ +/* + * 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 "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; +}; + +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; +}; + +struct CFile { + ZigList args; + const char *source_path; + const char *preprocessor_only_basename; +}; + +struct CodeGen { + // Other code depends on this being first. + ZigStage1 stage1; + + // 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; + 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; + 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 h_file_output_path; + Buf asm_file_output_path; + Buf llvm_ir_file_output_path; + Buf analysis_json_output_path; + Buf docs_output_path; + Buf *cache_dir; + Buf *c_artifact_dir; + const char **libc_include_dir_list; + size_t libc_include_dir_len; + + Buf *builtin_zig_path; + 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; + + ErrColor err_color; + uint32_t next_unresolved_index; + unsigned pointer_size_bytes; + bool is_big_endian; + bool have_err_ret_tracing; + bool verbose_tokenize; + bool verbose_ast; + bool verbose_ir; + bool verbose_llvm_ir; + bool verbose_cimport; + bool verbose_llvm_cpu_features; + bool error_during_imports; + bool generate_error_name_table; + bool enable_time_report; + bool enable_stack_report; + bool reported_bad_link_libc_error; + bool need_frame_size_prefix_data; + bool link_libc; + bool link_libcpp; + + BuildMode build_mode; + const ZigTarget *zig_target; + TargetSubsystem subsystem; // careful using this directly; see detect_subsystem + CodeModel code_model; + bool strip_debug_symbols; + bool is_test_build; + bool is_single_threaded; + bool have_pic; + bool link_mode_dynamic; + bool dll_export_fns; + bool have_stack_probing; + bool function_sections; + bool test_is_evented; + bool valgrind_enabled; + + Buf *root_out_name; + Buf *test_filter; + Buf *test_name_prefix; + Buf *zig_lib_dir; + Buf *zig_std_dir; +}; + +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/stage1/analyze.cpp b/src/stage1/analyze.cpp new file mode 100644 index 0000000000000000000000000000000000000000..369c28468453e67bf2d44da181ea858406658694 --- /dev/null +++ b/src/stage1/analyze.cpp @@ -0,0 +1,9940 @@ +/* + * 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) { + Error err; + 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; + } + + // We use the pointer type's abi size below, so we have to resolve it now. + if ((err = type_resolve(g, ptr_type, ResolveStatusSizeKnown))) { + codegen_report_errors_and_exit(g); + } + + ZigType *entry = new_type_table_entry(ZigTypeIdStruct); + + buf_resize(&entry->name, 0); + 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->link_libc) { + g->stage1.have_c_main = true; + } else if (cc == CallingConventionStdcall && g->zig_target->os == OsWindows) { + if (strcmp(symbol_name, "WinMain") == 0) { + g->stage1.have_winmain = true; + } else if (strcmp(symbol_name, "wWinMain") == 0) { + g->stage1.have_wwinmain = true; + } else if (strcmp(symbol_name, "WinMainCRTStartup") == 0) { + g->stage1.have_winmain_crt_startup = true; + } else if (strcmp(symbol_name, "wWinMainCRTStartup") == 0) { + g->stage1.have_wwinmain_crt_startup = true; + } else if (strcmp(symbol_name, "DllMainCRTStartup") == 0) { + g->stage1.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(); +} + +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_buf) { + size_t len; + const char *contents = stage2_fetch_file(&g->stage1, buf_ptr(resolved_path), buf_len(resolved_path), &len); + if (contents == nullptr) + return ErrorFileNotFound; + buf_init_from_mem(contents_buf, contents, len); + return ErrorNone; +} + +static X64CABIClass type_windows_abi_x86_64_class(CodeGen *g, ZigType *ty, size_t ty_size) { + // 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/stage1/analyze.hpp b/src/stage1/analyze.hpp new file mode 100644 index 0000000000000000000000000000000000000000..07601e6dea0c7bc5c68885c1c95bcb7f37d04349 --- /dev/null +++ b/src/stage1/analyze.hpp @@ -0,0 +1,297 @@ +/* + * 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); +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); + +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/stage1/ast_render.cpp b/src/stage1/ast_render.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ad308bf416a900d791892d94dbb3d09bb6fa0039 --- /dev/null +++ b/src/stage1/ast_render.cpp @@ -0,0 +1,1246 @@ +/* + * 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/stage1/ast_render.hpp b/src/stage1/ast_render.hpp new file mode 100644 index 0000000000000000000000000000000000000000..cf70b04694403b2d508df7d22a8f8fe0f1eb7752 --- /dev/null +++ b/src/stage1/ast_render.hpp @@ -0,0 +1,20 @@ +/* + * 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/stage1/bigfloat.cpp b/src/stage1/bigfloat.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a2a3a3b69cbbfbc91b80d85db97d1d7b256058c3 --- /dev/null +++ b/src/stage1/bigfloat.cpp @@ -0,0 +1,196 @@ +/* + * 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/stage1/bigfloat.hpp b/src/stage1/bigfloat.hpp new file mode 100644 index 0000000000000000000000000000000000000000..3ed6624fdcbff7d57629792599320558db71542a --- /dev/null +++ b/src/stage1/bigfloat.hpp @@ -0,0 +1,57 @@ +/* + * 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/stage1/bigint.cpp b/src/stage1/bigint.cpp new file mode 100644 index 0000000000000000000000000000000000000000..79a05e95a52a862c8728be21318642cf2fecda45 --- /dev/null +++ b/src/stage1/bigint.cpp @@ -0,0 +1,1786 @@ +/* + * 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(dest->digit_count); + + size_t i = 0; + for (; i < op1->digit_count && i < op2->digit_count; i += 1) { + dest->data.digits[i] = op1_digits[i] & op2_digits[i]; + } + for (; i < dest->digit_count; i += 1) { + dest->data.digits[i] = 0; + } + bigint_normalize(dest); + } +} + +void bigint_xor(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_xor(&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); + + assert(op1->digit_count > 0 && op2->digit_count > 0); + 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); + size_t i = 0; + for (; i < op1->digit_count && i < op2->digit_count; i += 1) { + dest->data.digits[i] = op1_digits[i] ^ op2_digits[i]; + } + for (; i < dest->digit_count; i += 1) { + if (i < op1->digit_count) { + dest->data.digits[i] = op1_digits[i]; + } else if (i < op2->digit_count) { + dest->data.digits[i] = op2_digits[i]; + } else { + zig_unreachable(); + } + } + bigint_normalize(dest); + } +} + +void bigint_shl(BigInt *dest, const BigInt *op1, const BigInt *op2) { + assert(!op2->is_negative); + + if (op2->digit_count == 0) { + bigint_init_bigint(dest, op1); + return; + } + + if (op1->digit_count == 0) { + bigint_init_unsigned(dest, 0); + return; + } + + if (op2->digit_count != 1) { + zig_panic("TODO shift left by amount greater than 64 bit integer"); + } + + const uint64_t *op1_digits = bigint_ptr(op1); + uint64_t shift_amt = bigint_as_unsigned(op2); + + if (op1->digit_count == 1 && shift_amt < 64) { + dest->data.digit = op1_digits[0] << shift_amt; + if (dest->data.digit > op1_digits[0]) { + dest->digit_count = 1; + dest->is_negative = op1->is_negative; + return; + } + } + + uint64_t digit_shift_count = shift_amt / 64; + uint64_t leftover_shift_count = shift_amt % 64; + + dest->data.digits = heap::c_allocator.allocate(op1->digit_count + digit_shift_count + 1); + dest->digit_count = digit_shift_count; + uint64_t carry = 0; + for (size_t i = 0; i < op1->digit_count; i += 1) { + uint64_t digit = op1_digits[i]; + dest->data.digits[dest->digit_count] = carry | (digit << leftover_shift_count); + dest->digit_count += 1; + if (leftover_shift_count > 0) { + carry = digit >> (64 - leftover_shift_count); + } else { + carry = 0; + } + } + dest->data.digits[dest->digit_count] = carry; + dest->digit_count += 1; + dest->is_negative = op1->is_negative; + bigint_normalize(dest); +} + +void bigint_shl_trunc(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed) { + BigInt unwrapped = {0}; + bigint_shl(&unwrapped, op1, op2); + bigint_truncate(dest, &unwrapped, bit_count, is_signed); +} + +void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2) { + assert(!op2->is_negative); + + if (op1->digit_count == 0) { + return bigint_init_unsigned(dest, 0); + } + + if (op2->digit_count == 0) { + return bigint_init_bigint(dest, op1); + } + + if (op2->digit_count != 1) { + zig_panic("TODO shift right by amount greater than 64 bit integer"); + } + + const uint64_t *op1_digits = bigint_ptr(op1); + uint64_t shift_amt = bigint_as_unsigned(op2); + + if (op1->digit_count == 1) { + dest->data.digit = (shift_amt < 64) ? op1_digits[0] >> shift_amt : 0; + dest->digit_count = 1; + dest->is_negative = op1->is_negative; + bigint_normalize(dest); + return; + } + + size_t digit_shift_count = shift_amt / 64; + size_t leftover_shift_count = shift_amt % 64; + + if (digit_shift_count >= op1->digit_count) { + return bigint_init_unsigned(dest, 0); + } + + dest->digit_count = op1->digit_count - digit_shift_count; + uint64_t *digits; + if (dest->digit_count == 1) { + digits = &dest->data.digit; + } else { + digits = heap::c_allocator.allocate(dest->digit_count); + dest->data.digits = digits; + } + + uint64_t carry = 0; + for (size_t op_digit_index = op1->digit_count - 1;;) { + uint64_t digit = op1_digits[op_digit_index]; + size_t dest_digit_index = op_digit_index - digit_shift_count; + digits[dest_digit_index] = carry | (digit >> leftover_shift_count); + carry = (leftover_shift_count != 0) ? (digit << (64 - leftover_shift_count)) : 0; + + if (dest_digit_index == 0) { break; } + op_digit_index -= 1; + } + dest->is_negative = op1->is_negative; + bigint_normalize(dest); +} + +void bigint_negate(BigInt *dest, const BigInt *op) { + bigint_init_bigint(dest, op); + dest->is_negative = !dest->is_negative; + bigint_normalize(dest); +} + +void bigint_negate_wrap(BigInt *dest, const BigInt *op, size_t bit_count) { + BigInt zero; + bigint_init_unsigned(&zero, 0); + bigint_sub_wrap(dest, &zero, op, bit_count, true); +} + +void bigint_not(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed) { + if (bit_count == 0) { + bigint_init_unsigned(dest, 0); + return; + } + + if (is_signed) { + BigInt twos_comp = {0}; + to_twos_complement(&twos_comp, op, bit_count); + + BigInt inverted = {0}; + bigint_not(&inverted, &twos_comp, bit_count, false); + + from_twos_complement(dest, &inverted, bit_count, true); + return; + } + + assert(!op->is_negative); + + dest->is_negative = false; + const uint64_t *op_digits = bigint_ptr(op); + if (bit_count <= 64) { + dest->digit_count = 1; + if (op->digit_count == 0) { + if (bit_count == 64) { + dest->data.digit = UINT64_MAX; + } else { + dest->data.digit = (1ULL << bit_count) - 1; + } + } else if (op->digit_count == 1) { + dest->data.digit = ~op_digits[0]; + if (bit_count != 64) { + uint64_t mask = (1ULL << bit_count) - 1; + dest->data.digit &= mask; + } + } + bigint_normalize(dest); + return; + } + dest->digit_count = (bit_count + 63) / 64; + assert(dest->digit_count >= op->digit_count); + dest->data.digits = heap::c_allocator.allocate_nonzero(dest->digit_count); + size_t i = 0; + for (; i < op->digit_count; i += 1) { + dest->data.digits[i] = ~op_digits[i]; + } + for (; i < dest->digit_count; i += 1) { + dest->data.digits[i] = 0xffffffffffffffffULL; + } + size_t digit_index = dest->digit_count - 1; + size_t digit_bit_index = bit_count % 64; + if (digit_bit_index != 0) { + uint64_t mask = (1ULL << digit_bit_index) - 1; + dest->data.digits[digit_index] &= mask; + } + bigint_normalize(dest); +} + +void bigint_truncate(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed) { + BigInt twos_comp; + to_twos_complement(&twos_comp, op, bit_count); + from_twos_complement(dest, &twos_comp, bit_count, is_signed); +} + +Cmp bigint_cmp(const BigInt *op1, const BigInt *op2) { + if (op1->is_negative && !op2->is_negative) { + return CmpLT; + } else if (!op1->is_negative && op2->is_negative) { + return CmpGT; + } else if (op1->digit_count > op2->digit_count) { + return op1->is_negative ? CmpLT : CmpGT; + } else if (op2->digit_count > op1->digit_count) { + return op1->is_negative ? CmpGT : CmpLT; + } else if (op1->digit_count == 0) { + return CmpEQ; + } + const uint64_t *op1_digits = bigint_ptr(op1); + const uint64_t *op2_digits = bigint_ptr(op2); + for (size_t i = op1->digit_count - 1; ;) { + uint64_t op1_digit = op1_digits[i]; + uint64_t op2_digit = op2_digits[i]; + + if (op1_digit > op2_digit) { + return op1->is_negative ? CmpLT : CmpGT; + } + if (op1_digit < op2_digit) { + return op1->is_negative ? CmpGT : CmpLT; + } + + if (i == 0) { + return CmpEQ; + } + i -= 1; + } +} + +void bigint_append_buf(Buf *buf, const BigInt *op, uint64_t base) { + if (op->digit_count == 0) { + buf_append_char(buf, '0'); + return; + } + if (op->is_negative) { + buf_append_char(buf, '-'); + } + if (op->digit_count == 1 && base == 10) { + buf_appendf(buf, "%" ZIG_PRI_u64, op->data.digit); + return; + } + if (op->digit_count == 1 && base == 16) { + buf_appendf(buf, "%" ZIG_PRI_x64, op->data.digit); + return; + } + size_t first_digit_index = buf_len(buf); + + BigInt digit_bi = {0}; + BigInt a1 = {0}; + BigInt a2 = {0}; + + BigInt *a = &a1; + BigInt *other_a = &a2; + bigint_init_bigint(a, op); + + BigInt base_bi = {0}; + bigint_init_unsigned(&base_bi, base); + + for (;;) { + bigint_rem(&digit_bi, a, &base_bi); + uint8_t digit = bigint_as_unsigned(&digit_bi); + buf_append_char(buf, digit_to_char(digit, false)); + bigint_div_trunc(other_a, a, &base_bi); + { + BigInt *tmp = a; + a = other_a; + other_a = tmp; + } + if (bigint_cmp_zero(a) == CmpEQ) { + break; + } + } + + // reverse + for (size_t i = first_digit_index; i < buf_len(buf) / 2; i += 1) { + size_t other_i = buf_len(buf) + first_digit_index - i - 1; + uint8_t tmp = buf_ptr(buf)[i]; + buf_ptr(buf)[i] = buf_ptr(buf)[other_i]; + buf_ptr(buf)[other_i] = tmp; + } +} + +size_t bigint_popcount_unsigned(const BigInt *bi) { + assert(!bi->is_negative); + if (bi->digit_count == 0) + return 0; + + size_t count = 0; + size_t bit_count = bi->digit_count * 64; + for (size_t i = 0; i < bit_count; i += 1) { + if (bit_at_index(bi, i)) + count += 1; + } + return count; +} + +size_t bigint_popcount_signed(const BigInt *bi, size_t bit_count) { + if (bit_count == 0) + return 0; + if (bi->digit_count == 0) + return 0; + + BigInt twos_comp = {0}; + to_twos_complement(&twos_comp, bi, bit_count); + + size_t count = 0; + for (size_t i = 0; i < bit_count; i += 1) { + if (bit_at_index(&twos_comp, i)) + count += 1; + } + return count; +} + +size_t bigint_ctz(const BigInt *bi, size_t bit_count) { + if (bit_count == 0) + return 0; + if (bi->digit_count == 0) + return bit_count; + + BigInt twos_comp = {0}; + to_twos_complement(&twos_comp, bi, bit_count); + + size_t count = 0; + for (size_t i = 0; i < bit_count; i += 1) { + if (bit_at_index(&twos_comp, i)) + return count; + count += 1; + } + return count; +} + +size_t bigint_clz(const BigInt *bi, size_t bit_count) { + if (bi->is_negative || bit_count == 0) + return 0; + if (bi->digit_count == 0) + return bit_count; + + size_t count = 0; + for (size_t i = bit_count - 1;;) { + if (bit_at_index(bi, i)) + return count; + count += 1; + + if (i == 0) break; + i -= 1; + } + return count; +} + +static uint64_t bigint_as_unsigned(const BigInt *bigint) { + assert(!bigint->is_negative); + if (bigint->digit_count == 0) { + return 0; + } else if (bigint->digit_count == 1) { + return bigint->data.digit; + } else { + zig_unreachable(); + } +} + +uint64_t bigint_as_u64(const BigInt *bigint) +{ + return bigint_as_unsigned(bigint); +} + +uint32_t bigint_as_u32(const BigInt *bigint) { + uint64_t value64 = bigint_as_unsigned(bigint); + uint32_t value32 = (uint32_t)value64; + assert (value64 == value32); + return value32; +} + +size_t bigint_as_usize(const BigInt *bigint) { + uint64_t value64 = bigint_as_unsigned(bigint); + size_t valueUsize = (size_t)value64; + assert (value64 == valueUsize); + return valueUsize; +} + +int64_t bigint_as_signed(const BigInt *bigint) { + if (bigint->digit_count == 0) { + return 0; + } else if (bigint->digit_count == 1) { + if (bigint->is_negative) { + if (bigint->data.digit <= 9223372036854775808ULL) { + return (-((int64_t)(bigint->data.digit - 1))) - 1; + } else { + zig_unreachable(); + } + } else { + return bigint->data.digit; + } + } else { + zig_unreachable(); + } +} + +Cmp bigint_cmp_zero(const BigInt *op) { + if (op->digit_count == 0) { + return CmpEQ; + } + return op->is_negative ? CmpLT : CmpGT; +} + +uint32_t bigint_hash(BigInt x) { + if (x.digit_count == 0) { + return 0; + } else { + return bigint_ptr(&x)[0]; + } +} + +bool bigint_eql(BigInt a, BigInt b) { + return bigint_cmp(&a, &b) == CmpEQ; +} + +void bigint_incr(BigInt *x) { + if (x->digit_count == 0) { + bigint_init_unsigned(x, 1); + return; + } + + if (x->digit_count == 1) { + if (x->is_negative && x->data.digit != 0) { + x->data.digit -= 1; + return; + } else if (!x->is_negative && x->data.digit != UINT64_MAX) { + x->data.digit += 1; + return; + } + } + + BigInt copy; + bigint_init_bigint(©, x); + + BigInt one; + bigint_init_unsigned(&one, 1); + + bigint_add(x, ©, &one); +} + +void bigint_decr(BigInt *x) { + if (x->digit_count == 0) { + bigint_init_signed(x, -1); + return; + } + + if (x->digit_count == 1) { + if (x->is_negative && x->data.digit != UINT64_MAX) { + x->data.digit += 1; + return; + } else if (!x->is_negative && x->data.digit != 0) { + x->data.digit -= 1; + return; + } + } + + BigInt copy; + bigint_init_bigint(©, x); + + BigInt neg_one; + bigint_init_signed(&neg_one, -1); + + bigint_add(x, ©, &neg_one); +} diff --git a/src/stage1/bigint.hpp b/src/stage1/bigint.hpp new file mode 100644 index 0000000000000000000000000000000000000000..044ea6642370e69e92ac3e291ed81c14d12dc360 --- /dev/null +++ b/src/stage1/bigint.hpp @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2017 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef ZIG_BIGINT_HPP +#define ZIG_BIGINT_HPP + +#include +#include + +struct BigInt { + size_t digit_count; + union { + uint64_t digit; + uint64_t *digits; // Least significant digit first + } data; + bool is_negative; +}; + +struct Buf; +struct BigFloat; + +enum Cmp { + CmpLT, + CmpGT, + CmpEQ, +}; + +void bigint_init_unsigned(BigInt *dest, uint64_t x); +void bigint_init_signed(BigInt *dest, int64_t x); +void bigint_init_bigint(BigInt *dest, const BigInt *src); +void bigint_init_bigfloat(BigInt *dest, const BigFloat *op); +void bigint_init_data(BigInt *dest, const uint64_t *digits, size_t digit_count, bool is_negative); +void bigint_deinit(BigInt *bi); + +// panics if number won't fit +uint64_t bigint_as_u64(const BigInt *bigint); +uint32_t bigint_as_u32(const BigInt *bigint); +size_t bigint_as_usize(const BigInt *bigint); + +int64_t bigint_as_signed(const BigInt *bigint); + +static inline const uint64_t *bigint_ptr(const BigInt *bigint) { + if (bigint->digit_count == 1) { + return &bigint->data.digit; + } else { + return bigint->data.digits; + } +} + +bool bigint_fits_in_bits(const BigInt *bn, size_t bit_count, bool is_signed); +void bigint_write_twos_complement(const BigInt *big_int, uint8_t *buf, size_t bit_count, bool is_big_endian); +void bigint_read_twos_complement(BigInt *dest, const uint8_t *buf, size_t bit_count, bool is_big_endian, + bool is_signed); +void bigint_add(BigInt *dest, const BigInt *op1, const BigInt *op2); +void bigint_add_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed); +void bigint_sub(BigInt *dest, const BigInt *op1, const BigInt *op2); +void bigint_sub_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed); +void bigint_mul(BigInt *dest, const BigInt *op1, const BigInt *op2); +void bigint_mul_wrap(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed); +void bigint_div_trunc(BigInt *dest, const BigInt *op1, const BigInt *op2); +void bigint_div_floor(BigInt *dest, const BigInt *op1, const BigInt *op2); +void bigint_rem(BigInt *dest, const BigInt *op1, const BigInt *op2); +void bigint_mod(BigInt *dest, const BigInt *op1, const BigInt *op2); + +void bigint_or(BigInt *dest, const BigInt *op1, const BigInt *op2); +void bigint_and(BigInt *dest, const BigInt *op1, const BigInt *op2); +void bigint_xor(BigInt *dest, const BigInt *op1, const BigInt *op2); + +void bigint_shl(BigInt *dest, const BigInt *op1, const BigInt *op2); +void bigint_shl_trunc(BigInt *dest, const BigInt *op1, const BigInt *op2, size_t bit_count, bool is_signed); +void bigint_shr(BigInt *dest, const BigInt *op1, const BigInt *op2); + +void bigint_negate(BigInt *dest, const BigInt *op); +void bigint_negate_wrap(BigInt *dest, const BigInt *op, size_t bit_count); +void bigint_not(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed); +void bigint_truncate(BigInt *dest, const BigInt *op, size_t bit_count, bool is_signed); + +Cmp bigint_cmp(const BigInt *op1, const BigInt *op2); + +void bigint_append_buf(Buf *buf, const BigInt *op, uint64_t base); + +size_t bigint_ctz(const BigInt *bi, size_t bit_count); +size_t bigint_clz(const BigInt *bi, size_t bit_count); +size_t bigint_popcount_signed(const BigInt *bi, size_t bit_count); +size_t bigint_popcount_unsigned(const BigInt *bi); + +size_t bigint_bits_needed(const BigInt *op); + + +// convenience functions +Cmp bigint_cmp_zero(const BigInt *op); + +void bigint_incr(BigInt *value); +void bigint_decr(BigInt *value); + +bool mul_u64_overflow(uint64_t op1, uint64_t op2, uint64_t *result); + +uint32_t bigint_hash(BigInt x); +bool bigint_eql(BigInt a, BigInt b); + +#endif diff --git a/src/stage1/buffer.cpp b/src/stage1/buffer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..86435e0f1496fa19f080bf442cd37ba9e348701c --- /dev/null +++ b/src/stage1/buffer.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2016 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#include "buffer.hpp" +#include +#include +#include + +Buf *buf_vprintf(const char *format, va_list ap) { + va_list ap2; + va_copy(ap2, ap); + + int len1 = vsnprintf(nullptr, 0, format, ap); + assert(len1 >= 0); + + size_t required_size = len1 + 1; + + Buf *buf = buf_alloc_fixed(len1); + + int len2 = vsnprintf(buf_ptr(buf), required_size, format, ap2); + assert(len2 == len1); + + va_end(ap2); + + return buf; +} + +Buf *buf_sprintf(const char *format, ...) { + va_list ap; + va_start(ap, format); + Buf *result = buf_vprintf(format, ap); + va_end(ap); + return result; +} + +void buf_appendf(Buf *buf, const char *format, ...) { + assert(buf->list.length); + va_list ap, ap2; + va_start(ap, format); + va_copy(ap2, ap); + + int len1 = vsnprintf(nullptr, 0, format, ap); + assert(len1 >= 0); + + size_t required_size = len1 + 1; + + size_t orig_len = buf_len(buf); + + buf_resize(buf, orig_len + len1); + + int len2 = vsnprintf(buf_ptr(buf) + orig_len, required_size, format, ap2); + assert(len2 == len1); + + va_end(ap2); + va_end(ap); +} + +// these functions are not static inline so they can be better used as template parameters +bool buf_eql_buf(Buf *buf, Buf *other) { + return buf_eql_mem(buf, buf_ptr(other), buf_len(other)); +} + +uint32_t buf_hash(Buf *buf) { + assert(buf->list.length); + size_t interval = buf->list.length / 256; + if (interval == 0) + interval = 1; + // FNV 32-bit hash + uint32_t h = 2166136261; + for (size_t i = 0; i < buf_len(buf); i += interval) { + h = h ^ ((uint8_t)buf->list.at(i)); + h = h * 16777619; + } + return h; +} diff --git a/src/stage1/buffer.hpp b/src/stage1/buffer.hpp new file mode 100644 index 0000000000000000000000000000000000000000..8876316589e76c8a069609d0a375c73acaf4a322 --- /dev/null +++ b/src/stage1/buffer.hpp @@ -0,0 +1,211 @@ +/* + * Copyright (c) 2015 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef ZIG_BUFFER_HPP +#define ZIG_BUFFER_HPP + +#include "list.hpp" + +#include +#include +#include + +#define BUF_INIT {{0}} + +// Note, you must call one of the alloc, init, or resize functions to have an +// initialized buffer. The assertions should help with this. +struct Buf { + ZigList list; +}; + +Buf *buf_sprintf(const char *format, ...) + ATTRIBUTE_PRINTF(1, 2); +Buf *buf_vprintf(const char *format, va_list ap); + +static inline size_t buf_len(Buf *buf) { + assert(buf); + assert(buf->list.length); + return buf->list.length - 1; +} + +static inline char *buf_ptr(Buf *buf) { + assert(buf); + assert(buf->list.length); + return buf->list.items; +} + +static inline const char *buf_ptr(const Buf *buf) { + assert(buf); + assert(buf->list.length); + return buf->list.items; +} + +static inline void buf_resize(Buf *buf, size_t new_len) { + buf->list.resize(new_len + 1); + buf->list.at(buf_len(buf)) = 0; +} + +static inline Buf *buf_alloc_fixed(size_t size) { + Buf *buf = heap::c_allocator.create(); + buf_resize(buf, size); + return buf; +} + +static inline Buf *buf_alloc(void) { + return buf_alloc_fixed(0); +} + +static inline void buf_deinit(Buf *buf) { + buf->list.deinit(); +} + +static inline void buf_destroy(Buf *buf) { + buf_deinit(buf); + heap::c_allocator.destroy(buf); +} + +static inline void buf_init_from_mem(Buf *buf, const char *ptr, size_t len) { + assert(len != SIZE_MAX); + buf->list.resize(len + 1); + memcpy(buf_ptr(buf), ptr, len); + buf->list.at(buf_len(buf)) = 0; +} + +static inline void buf_init_from_str(Buf *buf, const char *str) { + buf_init_from_mem(buf, str, strlen(str)); +} + +static inline void buf_init_from_buf(Buf *buf, Buf *other) { + buf_init_from_mem(buf, buf_ptr(other), buf_len(other)); +} + +static inline Buf *buf_create_from_mem(const char *ptr, size_t len) { + assert(len != SIZE_MAX); + Buf *buf = heap::c_allocator.create(); + buf_init_from_mem(buf, ptr, len); + return buf; +} + +static inline Buf *buf_create_from_slice(Slice slice) { + return buf_create_from_mem((const char *)slice.ptr, slice.len); +} + +static inline Buf *buf_create_from_str(const char *str) { + return buf_create_from_mem(str, strlen(str)); +} + +static inline Buf *buf_create_from_buf(Buf *buf) { + return buf_create_from_mem(buf_ptr(buf), buf_len(buf)); +} + +static inline Buf *buf_slice(Buf *in_buf, size_t start, size_t end) { + assert(in_buf->list.length); + assert(start != SIZE_MAX); + assert(end != SIZE_MAX); + assert(start < buf_len(in_buf)); + assert(end <= buf_len(in_buf)); + Buf *out_buf = heap::c_allocator.create(); + out_buf->list.resize(end - start + 1); + memcpy(buf_ptr(out_buf), buf_ptr(in_buf) + start, end - start); + out_buf->list.at(buf_len(out_buf)) = 0; + return out_buf; +} + +static inline void buf_append_mem(Buf *buf, const char *mem, size_t mem_len) { + assert(buf->list.length); + assert(mem_len != SIZE_MAX); + size_t old_len = buf_len(buf); + buf_resize(buf, old_len + mem_len); + memcpy(buf_ptr(buf) + old_len, mem, mem_len); + buf->list.at(buf_len(buf)) = 0; +} + +static inline void buf_append_str(Buf *buf, const char *str) { + assert(buf->list.length); + buf_append_mem(buf, str, strlen(str)); +} + +static inline void buf_append_buf(Buf *buf, Buf *append_buf) { + assert(buf->list.length); + buf_append_mem(buf, buf_ptr(append_buf), buf_len(append_buf)); +} + +static inline void buf_append_char(Buf *buf, uint8_t c) { + assert(buf->list.length); + buf_append_mem(buf, (const char *)&c, 1); +} + +void buf_appendf(Buf *buf, const char *format, ...) + ATTRIBUTE_PRINTF(2, 3); + +static inline bool buf_eql_mem(Buf *buf, const char *mem, size_t mem_len) { + assert(buf->list.length); + return mem_eql_mem(buf_ptr(buf), buf_len(buf), mem, mem_len); +} + +static inline bool buf_eql_mem_ignore_case(Buf *buf, const char *mem, size_t mem_len) { + assert(buf->list.length); + return mem_eql_mem_ignore_case(buf_ptr(buf), buf_len(buf), mem, mem_len); +} + +static inline bool buf_eql_str(Buf *buf, const char *str) { + assert(buf->list.length); + return buf_eql_mem(buf, str, strlen(str)); +} + +static inline bool buf_eql_str_ignore_case(Buf *buf, const char *str) { + assert(buf->list.length); + return buf_eql_mem_ignore_case(buf, str, strlen(str)); +} + +static inline bool buf_starts_with_mem(Buf *buf, const char *mem, size_t mem_len) { + if (buf_len(buf) < mem_len) { + return false; + } + return memcmp(buf_ptr(buf), mem, mem_len) == 0; +} + +static inline bool buf_starts_with_buf(Buf *buf, Buf *sub) { + return buf_starts_with_mem(buf, buf_ptr(sub), buf_len(sub)); +} + +static inline bool buf_starts_with_str(Buf *buf, const char *str) { + return buf_starts_with_mem(buf, str, strlen(str)); +} + +static inline bool buf_ends_with_mem(Buf *buf, const char *mem, size_t mem_len) { + return mem_ends_with_mem(buf_ptr(buf), buf_len(buf), mem, mem_len); +} + +static inline bool buf_ends_with_str(Buf *buf, const char *str) { + return buf_ends_with_mem(buf, str, strlen(str)); +} + +bool buf_eql_buf(Buf *buf, Buf *other); +uint32_t buf_hash(Buf *buf); + +static inline void buf_upcase(Buf *buf) { + for (size_t i = 0; i < buf_len(buf); i += 1) { + buf_ptr(buf)[i] = (char)toupper(buf_ptr(buf)[i]); + } +} + +static inline Slice buf_to_slice(Buf *buf) { + return Slice{reinterpret_cast(buf_ptr(buf)), buf_len(buf)}; +} + +static inline void buf_replace(Buf* buf, char from, char to) { + const size_t count = buf_len(buf); + char* ptr = buf_ptr(buf); + for (size_t i = 0; i < count; ++i) { + char& l = ptr[i]; + if (l == from) + l = to; + } +} + +#endif diff --git a/src/stage1/codegen.cpp b/src/stage1/codegen.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2fc85b42fe5735a95322be1a3b0348d1ba25ce73 --- /dev/null +++ b/src/stage1/codegen.cpp @@ -0,0 +1,9475 @@ +/* + * 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 "errmsg.hpp" +#include "error.hpp" +#include "hash_map.hpp" +#include "ir.hpp" +#include "os.hpp" +#include "target.hpp" +#include "util.hpp" +#include "zig_llvm.h" +#include "stage2.h" +#include "dump_analysis.hpp" +#include "softfloat.hpp" + +#include +#include + +enum ResumeId { + ResumeIdManual, + ResumeIdReturn, + ResumeIdCall, +}; + +static ZigPackage *new_package(const char *root_src_dir, const char *root_src_path, const char *pkg_path) { + ZigPackage *entry = heap::c_allocator.create(); + entry->package_table.init(4); + buf_init_from_str(&entry->root_src_dir, root_src_dir); + buf_init_from_str(&entry->root_src_path, root_src_path); + buf_init_from_str(&entry->pkg_path, pkg_path); + return entry; +} + +ZigPackage *new_anonymous_package() { + return new_package("", "", ""); +} + +static const char *symbols_that_llvm_depends_on[] = { + "memcpy", + "memset", + "sqrt", + "powi", + "sin", + "cos", + "pow", + "exp", + "exp2", + "log", + "log10", + "log2", + "fma", + "fabs", + "minnum", + "maxnum", + "copysign", + "floor", + "ceil", + "trunc", + "rint", + "nearbyint", + "round", + // TODO probably all of compiler-rt needs to go here +}; + +void codegen_set_strip(CodeGen *g, bool strip) { + g->strip_debug_symbols = strip; + if (!target_has_debug_info(g->zig_target)) { + g->strip_debug_symbols = true; + } +} + +static void render_const_val(CodeGen *g, ZigValue *const_val, const char *name); +static void render_const_val_global(CodeGen *g, ZigValue *const_val, const char *name); +static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *name); +static void generate_error_name_table(CodeGen *g); +static bool value_is_all_undef(CodeGen *g, ZigValue *const_val); +static void gen_undef_init(CodeGen *g, uint32_t ptr_align_bytes, ZigType *value_type, LLVMValueRef ptr); +static LLVMValueRef build_alloca(CodeGen *g, ZigType *type_entry, const char *name, uint32_t alignment); +static LLVMValueRef gen_await_early_return(CodeGen *g, IrInstGen *source_instr, + LLVMValueRef target_frame_ptr, ZigType *result_type, ZigType *ptr_result_type, + LLVMValueRef result_loc, bool non_async); + +static void addLLVMAttr(LLVMValueRef val, LLVMAttributeIndex attr_index, const char *attr_name) { + unsigned kind_id = LLVMGetEnumAttributeKindForName(attr_name, strlen(attr_name)); + assert(kind_id != 0); + LLVMAttributeRef llvm_attr = LLVMCreateEnumAttribute(LLVMGetGlobalContext(), kind_id, 0); + LLVMAddAttributeAtIndex(val, attr_index, llvm_attr); +} + +static void addLLVMAttrStr(LLVMValueRef val, LLVMAttributeIndex attr_index, + const char *attr_name, const char *attr_val) +{ + LLVMAttributeRef llvm_attr = LLVMCreateStringAttribute(LLVMGetGlobalContext(), + attr_name, (unsigned)strlen(attr_name), attr_val, (unsigned)strlen(attr_val)); + LLVMAddAttributeAtIndex(val, attr_index, llvm_attr); +} + +static void addLLVMAttrInt(LLVMValueRef val, LLVMAttributeIndex attr_index, + const char *attr_name, uint64_t attr_val) +{ + unsigned kind_id = LLVMGetEnumAttributeKindForName(attr_name, strlen(attr_name)); + assert(kind_id != 0); + LLVMAttributeRef llvm_attr = LLVMCreateEnumAttribute(LLVMGetGlobalContext(), kind_id, attr_val); + LLVMAddAttributeAtIndex(val, attr_index, llvm_attr); +} + +static void addLLVMFnAttr(LLVMValueRef fn_val, const char *attr_name) { + return addLLVMAttr(fn_val, -1, attr_name); +} + +static void addLLVMFnAttrStr(LLVMValueRef fn_val, const char *attr_name, const char *attr_val) { + return addLLVMAttrStr(fn_val, -1, attr_name, attr_val); +} + +static void addLLVMFnAttrInt(LLVMValueRef fn_val, const char *attr_name, uint64_t attr_val) { + return addLLVMAttrInt(fn_val, -1, attr_name, attr_val); +} + +static void addLLVMArgAttr(LLVMValueRef fn_val, unsigned param_index, const char *attr_name) { + return addLLVMAttr(fn_val, param_index + 1, attr_name); +} + +static void addLLVMArgAttrInt(LLVMValueRef fn_val, unsigned param_index, const char *attr_name, uint64_t attr_val) { + return addLLVMAttrInt(fn_val, param_index + 1, attr_name, attr_val); +} + +static bool is_symbol_available(CodeGen *g, const char *name) { + Buf *buf_name = buf_create_from_str(name); + bool result = + g->exported_symbol_names.maybe_get(buf_name) == nullptr && + g->external_symbol_names.maybe_get(buf_name) == nullptr; + buf_destroy(buf_name); + return result; +} + +static const char *get_mangled_name(CodeGen *g, const char *original_name) { + if (is_symbol_available(g, original_name)) + return original_name; + + int n = 0; + for (;; n += 1) { + const char *new_name = buf_ptr(buf_sprintf("%s.%d", original_name, n)); + if (is_symbol_available(g, new_name)) { + return new_name; + } + } +} + +static ZigLLVM_CallingConv get_llvm_cc(CodeGen *g, CallingConvention cc) { + switch (cc) { + case CallingConventionUnspecified: + return ZigLLVM_Fast; + case CallingConventionC: + return ZigLLVM_C; + case CallingConventionCold: + if ((g->zig_target->arch == ZigLLVM_x86 || + g->zig_target->arch == ZigLLVM_x86_64) && + g->zig_target->os != OsWindows) + return ZigLLVM_Cold; + return ZigLLVM_C; + case CallingConventionNaked: + zig_unreachable(); + case CallingConventionStdcall: + if (g->zig_target->arch == ZigLLVM_x86) + return ZigLLVM_X86_StdCall; + return ZigLLVM_C; + case CallingConventionFastcall: + if (g->zig_target->arch == ZigLLVM_x86) + return ZigLLVM_X86_FastCall; + return ZigLLVM_C; + case CallingConventionVectorcall: + if (g->zig_target->arch == ZigLLVM_x86) + return ZigLLVM_X86_VectorCall; + if (target_is_arm(g->zig_target) && + target_arch_pointer_bit_width(g->zig_target->arch) == 64) + return ZigLLVM_AArch64_VectorCall; + return ZigLLVM_C; + case CallingConventionThiscall: + if (g->zig_target->arch == ZigLLVM_x86) + return ZigLLVM_X86_ThisCall; + return ZigLLVM_C; + case CallingConventionAsync: + return ZigLLVM_Fast; + case CallingConventionAPCS: + if (target_is_arm(g->zig_target)) + return ZigLLVM_ARM_APCS; + return ZigLLVM_C; + case CallingConventionAAPCS: + if (target_is_arm(g->zig_target)) + return ZigLLVM_ARM_AAPCS; + return ZigLLVM_C; + case CallingConventionAAPCSVFP: + if (target_is_arm(g->zig_target)) + return ZigLLVM_ARM_AAPCS_VFP; + return ZigLLVM_C; + case CallingConventionInterrupt: + if (g->zig_target->arch == ZigLLVM_x86 || + g->zig_target->arch == ZigLLVM_x86_64) + return ZigLLVM_X86_INTR; + if (g->zig_target->arch == ZigLLVM_avr) + return ZigLLVM_AVR_INTR; + if (g->zig_target->arch == ZigLLVM_msp430) + return ZigLLVM_MSP430_INTR; + return ZigLLVM_C; + case CallingConventionSignal: + if (g->zig_target->arch == ZigLLVM_avr) + return ZigLLVM_AVR_SIGNAL; + return ZigLLVM_C; + } + zig_unreachable(); +} + +static void add_uwtable_attr(CodeGen *g, LLVMValueRef fn_val) { + if (g->zig_target->os == OsWindows) { + addLLVMFnAttr(fn_val, "uwtable"); + } +} + +static LLVMLinkage to_llvm_linkage(GlobalLinkageId id, bool is_extern) { + switch (id) { + case GlobalLinkageIdInternal: + return LLVMInternalLinkage; + case GlobalLinkageIdStrong: + return LLVMExternalLinkage; + case GlobalLinkageIdWeak: + if (is_extern) return LLVMExternalWeakLinkage; + return LLVMWeakODRLinkage; + case GlobalLinkageIdLinkOnce: + return LLVMLinkOnceODRLinkage; + } + zig_unreachable(); +} + +struct CalcLLVMFieldIndex { + uint32_t offset; + uint32_t field_index; +}; + +static void calc_llvm_field_index_add(CodeGen *g, CalcLLVMFieldIndex *calc, ZigType *ty) { + if (!type_has_bits(g, ty)) return; + uint32_t ty_align = get_abi_alignment(g, ty); + if (calc->offset % ty_align != 0) { + uint32_t llvm_align = LLVMABIAlignmentOfType(g->target_data_ref, get_llvm_type(g, ty)); + if (llvm_align >= ty_align) { + ty_align = llvm_align; // llvm's padding is sufficient + } else if (calc->offset) { + calc->field_index += 1; // zig will insert an extra padding field here + } + calc->offset += ty_align - (calc->offset % ty_align); // padding bytes + } + calc->offset += ty->abi_size; + calc->field_index += 1; +} + +// label (grep this): [fn_frame_struct_layout] +static void frame_index_trace_arg_calc(CodeGen *g, CalcLLVMFieldIndex *calc, ZigType *return_type) { + calc_llvm_field_index_add(g, calc, g->builtin_types.entry_usize); // function pointer + calc_llvm_field_index_add(g, calc, g->builtin_types.entry_usize); // resume index + calc_llvm_field_index_add(g, calc, g->builtin_types.entry_usize); // awaiter index + + if (type_has_bits(g, return_type)) { + calc_llvm_field_index_add(g, calc, g->builtin_types.entry_usize); // *ReturnType (callee's) + calc_llvm_field_index_add(g, calc, g->builtin_types.entry_usize); // *ReturnType (awaiter's) + calc_llvm_field_index_add(g, calc, return_type); // ReturnType + } +} + +static uint32_t frame_index_trace_arg(CodeGen *g, ZigType *return_type) { + CalcLLVMFieldIndex calc = {0}; + frame_index_trace_arg_calc(g, &calc, return_type); + return calc.field_index; +} + +// label (grep this): [fn_frame_struct_layout] +static void frame_index_arg_calc(CodeGen *g, CalcLLVMFieldIndex *calc, ZigType *return_type) { + frame_index_trace_arg_calc(g, calc, return_type); + + if (codegen_fn_has_err_ret_tracing_arg(g, return_type)) { + calc_llvm_field_index_add(g, calc, g->builtin_types.entry_usize); // *StackTrace (callee's) + calc_llvm_field_index_add(g, calc, g->builtin_types.entry_usize); // *StackTrace (awaiter's) + } +} + +// label (grep this): [fn_frame_struct_layout] +static uint32_t frame_index_trace_stack(CodeGen *g, ZigFn *fn) { + size_t field_index = 6; + bool have_stack_trace = codegen_fn_has_err_ret_tracing_arg(g, fn->type_entry->data.fn.fn_type_id.return_type); + if (have_stack_trace) { + field_index += 2; + } + field_index += fn->type_entry->data.fn.fn_type_id.param_count; + ZigType *locals_struct = fn->frame_type->data.frame.locals_struct; + TypeStructField *field = locals_struct->data.structure.fields[field_index]; + return field->gen_index; +} + + +static uint32_t get_err_ret_trace_arg_index(CodeGen *g, ZigFn *fn_table_entry) { + if (!g->have_err_ret_tracing) { + return UINT32_MAX; + } + if (fn_is_async(fn_table_entry)) { + return UINT32_MAX; + } + ZigType *fn_type = fn_table_entry->type_entry; + if (!fn_type_can_fail(&fn_type->data.fn.fn_type_id)) { + return UINT32_MAX; + } + ZigType *return_type = fn_type->data.fn.fn_type_id.return_type; + bool first_arg_ret = type_has_bits(g, return_type) && handle_is_ptr(g, return_type); + return first_arg_ret ? 1 : 0; +} + +static void maybe_export_dll(CodeGen *g, LLVMValueRef global_value, GlobalLinkageId linkage) { + if (linkage != GlobalLinkageIdInternal && g->zig_target->os == OsWindows && g->dll_export_fns) { + LLVMSetDLLStorageClass(global_value, LLVMDLLExportStorageClass); + } +} + +static void maybe_import_dll(CodeGen *g, LLVMValueRef global_value, GlobalLinkageId linkage) { + if (linkage != GlobalLinkageIdInternal && g->zig_target->os == OsWindows) { + // TODO come up with a good explanation/understanding for why we never do + // DLLImportStorageClass. Empirically it only causes problems. But let's have + // this documented and then clean up the code accordingly. + //LLVMSetDLLStorageClass(global_value, LLVMDLLImportStorageClass); + } +} + +static bool cc_want_sret_attr(CallingConvention cc) { + switch (cc) { + case CallingConventionNaked: + zig_unreachable(); + case CallingConventionC: + case CallingConventionCold: + case CallingConventionInterrupt: + case CallingConventionSignal: + case CallingConventionStdcall: + case CallingConventionFastcall: + case CallingConventionVectorcall: + case CallingConventionThiscall: + case CallingConventionAPCS: + case CallingConventionAAPCS: + case CallingConventionAAPCSVFP: + return true; + case CallingConventionAsync: + case CallingConventionUnspecified: + return false; + } + zig_unreachable(); +} + +static bool codegen_have_frame_pointer(CodeGen *g) { + return g->build_mode == BuildModeDebug; +} + +static LLVMValueRef make_fn_llvm_value(CodeGen *g, ZigFn *fn) { + const char *unmangled_name = buf_ptr(&fn->symbol_name); + const char *symbol_name; + GlobalLinkageId linkage; + if (fn->body_node == nullptr) { + symbol_name = unmangled_name; + linkage = GlobalLinkageIdStrong; + } else if (fn->export_list.length == 0) { + symbol_name = get_mangled_name(g, unmangled_name); + linkage = GlobalLinkageIdInternal; + } else { + GlobalExport *fn_export = &fn->export_list.items[0]; + symbol_name = buf_ptr(&fn_export->name); + linkage = fn_export->linkage; + } + + CallingConvention cc = fn->type_entry->data.fn.fn_type_id.cc; + bool is_async = fn_is_async(fn); + + ZigType *fn_type = fn->type_entry; + // Make the raw_type_ref populated + resolve_llvm_types_fn(g, fn); + LLVMTypeRef fn_llvm_type = fn->raw_type_ref; + LLVMValueRef llvm_fn = nullptr; + if (fn->body_node == nullptr) { + const unsigned fn_addrspace = ZigLLVMDataLayoutGetProgramAddressSpace(g->target_data_ref); + LLVMValueRef existing_llvm_fn = LLVMGetNamedFunction(g->module, symbol_name); + if (existing_llvm_fn) { + return LLVMConstBitCast(existing_llvm_fn, LLVMPointerType(fn_llvm_type, fn_addrspace)); + } else { + Buf *buf_symbol_name = buf_create_from_str(symbol_name); + auto entry = g->exported_symbol_names.maybe_get(buf_symbol_name); + buf_destroy(buf_symbol_name); + + if (entry == nullptr) { + llvm_fn = LLVMAddFunction(g->module, symbol_name, fn_llvm_type); + + if (target_is_wasm(g->zig_target)) { + assert(fn->proto_node->type == NodeTypeFnProto); + AstNodeFnProto *fn_proto = &fn->proto_node->data.fn_proto; + if (fn_proto-> is_extern && fn_proto->lib_name != nullptr ) { + addLLVMFnAttrStr(llvm_fn, "wasm-import-module", buf_ptr(fn_proto->lib_name)); + } + } + } else { + assert(entry->value->id == TldIdFn); + TldFn *tld_fn = reinterpret_cast(entry->value); + // Make the raw_type_ref populated + resolve_llvm_types_fn(g, tld_fn->fn_entry); + tld_fn->fn_entry->llvm_value = LLVMAddFunction(g->module, symbol_name, + tld_fn->fn_entry->raw_type_ref); + llvm_fn = LLVMConstBitCast(tld_fn->fn_entry->llvm_value, LLVMPointerType(fn_llvm_type, fn_addrspace)); + return llvm_fn; + } + } + } else { + if (llvm_fn == nullptr) { + llvm_fn = LLVMAddFunction(g->module, symbol_name, fn_llvm_type); + } + + for (size_t i = 1; i < fn->export_list.length; i += 1) { + GlobalExport *fn_export = &fn->export_list.items[i]; + LLVMAddAlias(g->module, LLVMTypeOf(llvm_fn), llvm_fn, buf_ptr(&fn_export->name)); + } + } + + switch (fn->fn_inline) { + case FnInlineAlways: + addLLVMFnAttr(llvm_fn, "alwaysinline"); + g->inline_fns.append(fn); + break; + case FnInlineNever: + addLLVMFnAttr(llvm_fn, "noinline"); + break; + case FnInlineAuto: + if (fn->alignstack_value != 0) { + addLLVMFnAttr(llvm_fn, "noinline"); + } + break; + } + + if (cc == CallingConventionNaked) { + addLLVMFnAttr(llvm_fn, "naked"); + } else { + ZigLLVMFunctionSetCallingConv(llvm_fn, get_llvm_cc(g, cc)); + } + + bool want_cold = fn->is_cold || cc == CallingConventionCold; + if (want_cold) { + ZigLLVMAddFunctionAttrCold(llvm_fn); + } + + + LLVMSetLinkage(llvm_fn, to_llvm_linkage(linkage, fn->body_node == nullptr)); + + if (linkage == GlobalLinkageIdInternal) { + LLVMSetUnnamedAddr(llvm_fn, true); + } + + ZigType *return_type = fn_type->data.fn.fn_type_id.return_type; + if (return_type->id == ZigTypeIdUnreachable) { + addLLVMFnAttr(llvm_fn, "noreturn"); + } + + if (fn->body_node != nullptr) { + maybe_export_dll(g, llvm_fn, linkage); + + bool want_fn_safety = g->build_mode != BuildModeFastRelease && + g->build_mode != BuildModeSmallRelease && + !fn->def_scope->safety_off; + if (want_fn_safety) { + if (g->link_libc) { + addLLVMFnAttr(llvm_fn, "sspstrong"); + addLLVMFnAttrStr(llvm_fn, "stack-protector-buffer-size", "4"); + } + } + if (g->have_stack_probing && !fn->def_scope->safety_off) { + addLLVMFnAttrStr(llvm_fn, "probe-stack", "__zig_probe_stack"); + } else if (g->zig_target->os == OsUefi) { + addLLVMFnAttrStr(llvm_fn, "no-stack-arg-probe", ""); + } + } else { + maybe_import_dll(g, llvm_fn, linkage); + } + + if (fn->alignstack_value != 0) { + addLLVMFnAttrInt(llvm_fn, "alignstack", fn->alignstack_value); + } + + addLLVMFnAttr(llvm_fn, "nounwind"); + add_uwtable_attr(g, llvm_fn); + addLLVMFnAttr(llvm_fn, "nobuiltin"); + if (codegen_have_frame_pointer(g) && fn->fn_inline != FnInlineAlways) { + ZigLLVMAddFunctionAttr(llvm_fn, "frame-pointer", "all"); + } + if (fn->section_name) { + LLVMSetSection(llvm_fn, buf_ptr(fn->section_name)); + } + if (fn->align_bytes > 0) { + LLVMSetAlignment(llvm_fn, (unsigned)fn->align_bytes); + } else { + // We'd like to set the best alignment for the function here, but on Darwin LLVM gives + // "Cannot getTypeInfo() on a type that is unsized!" assertion failure when calling + // any of the functions for getting alignment. Not specifying the alignment should + // use the ABI alignment, which is fine. + } + + if (is_async) { + addLLVMArgAttr(llvm_fn, 0, "nonnull"); + } else { + unsigned init_gen_i = 0; + if (!type_has_bits(g, return_type)) { + // nothing to do + } else if (type_is_nonnull_ptr(g, return_type)) { + addLLVMAttr(llvm_fn, 0, "nonnull"); + } else if (want_first_arg_sret(g, &fn_type->data.fn.fn_type_id)) { + // Sret pointers must not be address 0 + addLLVMArgAttr(llvm_fn, 0, "nonnull"); + addLLVMArgAttr(llvm_fn, 0, "sret"); + if (cc_want_sret_attr(cc)) { + addLLVMArgAttr(llvm_fn, 0, "noalias"); + } + init_gen_i = 1; + } + + // set parameter attributes + FnWalk fn_walk = {}; + fn_walk.id = FnWalkIdAttrs; + fn_walk.data.attrs.fn = fn; + fn_walk.data.attrs.llvm_fn = llvm_fn; + fn_walk.data.attrs.gen_i = init_gen_i; + walk_function_params(g, fn_type, &fn_walk); + + uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, fn); + if (err_ret_trace_arg_index != UINT32_MAX) { + // Error return trace memory is in the stack, which is impossible to be at address 0 + // on any architecture. + addLLVMArgAttr(llvm_fn, (unsigned)err_ret_trace_arg_index, "nonnull"); + } + } + + return llvm_fn; +} + +static LLVMValueRef fn_llvm_value(CodeGen *g, ZigFn *fn) { + if (fn->llvm_value) + return fn->llvm_value; + + fn->llvm_value = make_fn_llvm_value(g, fn); + fn->llvm_name = strdup(LLVMGetValueName(fn->llvm_value)); + return fn->llvm_value; +} + +static ZigLLVMDIScope *get_di_scope(CodeGen *g, Scope *scope) { + if (scope->di_scope) + return scope->di_scope; + + ZigType *import = get_scope_import(scope); + switch (scope->id) { + case ScopeIdCImport: + zig_unreachable(); + case ScopeIdFnDef: + { + assert(scope->parent); + ScopeFnDef *fn_scope = (ScopeFnDef *)scope; + ZigFn *fn_table_entry = fn_scope->fn_entry; + if (!fn_table_entry->proto_node) + return get_di_scope(g, scope->parent); + unsigned line_number = (unsigned)(fn_table_entry->proto_node->line == 0) ? + 0 : (fn_table_entry->proto_node->line + 1); + unsigned scope_line = line_number; + bool is_definition = fn_table_entry->body_node != nullptr; + bool is_optimized = g->build_mode != BuildModeDebug; + bool is_internal_linkage = (fn_table_entry->body_node != nullptr && + fn_table_entry->export_list.length == 0); + unsigned flags = ZigLLVM_DIFlags_StaticMember; + ZigLLVMDIScope *fn_di_scope = get_di_scope(g, scope->parent); + assert(fn_di_scope != nullptr); + assert(fn_table_entry->raw_di_type != nullptr); + ZigLLVMDISubprogram *subprogram = ZigLLVMCreateFunction(g->dbuilder, + fn_di_scope, buf_ptr(&fn_table_entry->symbol_name), "", + import->data.structure.root_struct->di_file, line_number, + fn_table_entry->raw_di_type, is_internal_linkage, + is_definition, scope_line, flags, is_optimized, nullptr); + + scope->di_scope = ZigLLVMSubprogramToScope(subprogram); + if (!g->strip_debug_symbols) { + ZigLLVMFnSetSubprogram(fn_llvm_value(g, fn_table_entry), subprogram); + } + return scope->di_scope; + } + case ScopeIdDecls: + if (scope->parent) { + ScopeDecls *decls_scope = (ScopeDecls *)scope; + assert(decls_scope->container_type); + scope->di_scope = ZigLLVMTypeToScope(get_llvm_di_type(g, decls_scope->container_type)); + } else { + scope->di_scope = ZigLLVMFileToScope(import->data.structure.root_struct->di_file); + } + return scope->di_scope; + case ScopeIdBlock: + case ScopeIdDefer: + { + assert(scope->parent); + ZigLLVMDILexicalBlock *di_block = ZigLLVMCreateLexicalBlock(g->dbuilder, + get_di_scope(g, scope->parent), + import->data.structure.root_struct->di_file, + (unsigned)scope->source_node->line + 1, + (unsigned)scope->source_node->column + 1); + scope->di_scope = ZigLLVMLexicalBlockToScope(di_block); + return scope->di_scope; + } + case ScopeIdVarDecl: + case ScopeIdDeferExpr: + case ScopeIdLoop: + case ScopeIdSuspend: + case ScopeIdCompTime: + case ScopeIdNoSuspend: + case ScopeIdRuntime: + case ScopeIdTypeOf: + case ScopeIdExpr: + return get_di_scope(g, scope->parent); + } + zig_unreachable(); +} + +static void clear_debug_source_node(CodeGen *g) { + ZigLLVMClearCurrentDebugLocation(g->builder); +} + +static LLVMValueRef get_arithmetic_overflow_fn(CodeGen *g, ZigType *operand_type, + const char *signed_name, const char *unsigned_name) +{ + ZigType *int_type = (operand_type->id == ZigTypeIdVector) ? operand_type->data.vector.elem_type : operand_type; + char fn_name[64]; + + assert(int_type->id == ZigTypeIdInt); + const char *signed_str = int_type->data.integral.is_signed ? signed_name : unsigned_name; + + LLVMTypeRef param_types[] = { + get_llvm_type(g, operand_type), + get_llvm_type(g, operand_type), + }; + + if (operand_type->id == ZigTypeIdVector) { + sprintf(fn_name, "llvm.%s.with.overflow.v%" PRIu64 "i%" PRIu32, signed_str, + operand_type->data.vector.len, int_type->data.integral.bit_count); + + LLVMTypeRef return_elem_types[] = { + get_llvm_type(g, operand_type), + LLVMVectorType(LLVMInt1Type(), operand_type->data.vector.len), + }; + LLVMTypeRef return_struct_type = LLVMStructType(return_elem_types, 2, false); + LLVMTypeRef fn_type = LLVMFunctionType(return_struct_type, param_types, 2, false); + LLVMValueRef fn_val = LLVMAddFunction(g->module, fn_name, fn_type); + assert(LLVMGetIntrinsicID(fn_val)); + return fn_val; + } else { + sprintf(fn_name, "llvm.%s.with.overflow.i%" PRIu32, signed_str, int_type->data.integral.bit_count); + + LLVMTypeRef return_elem_types[] = { + get_llvm_type(g, operand_type), + LLVMInt1Type(), + }; + LLVMTypeRef return_struct_type = LLVMStructType(return_elem_types, 2, false); + LLVMTypeRef fn_type = LLVMFunctionType(return_struct_type, param_types, 2, false); + LLVMValueRef fn_val = LLVMAddFunction(g->module, fn_name, fn_type); + assert(LLVMGetIntrinsicID(fn_val)); + return fn_val; + } +} + +static LLVMValueRef get_int_overflow_fn(CodeGen *g, ZigType *operand_type, AddSubMul add_sub_mul) { + ZigType *int_type = (operand_type->id == ZigTypeIdVector) ? operand_type->data.vector.elem_type : operand_type; + assert(int_type->id == ZigTypeIdInt); + + ZigLLVMFnKey key = {}; + key.id = ZigLLVMFnIdOverflowArithmetic; + key.data.overflow_arithmetic.is_signed = int_type->data.integral.is_signed; + key.data.overflow_arithmetic.add_sub_mul = add_sub_mul; + key.data.overflow_arithmetic.bit_count = (uint32_t)int_type->data.integral.bit_count; + key.data.overflow_arithmetic.vector_len = (operand_type->id == ZigTypeIdVector) ? + operand_type->data.vector.len : 0; + + auto existing_entry = g->llvm_fn_table.maybe_get(key); + if (existing_entry) + return existing_entry->value; + + LLVMValueRef fn_val; + switch (add_sub_mul) { + case AddSubMulAdd: + fn_val = get_arithmetic_overflow_fn(g, operand_type, "sadd", "uadd"); + break; + case AddSubMulSub: + fn_val = get_arithmetic_overflow_fn(g, operand_type, "ssub", "usub"); + break; + case AddSubMulMul: + fn_val = get_arithmetic_overflow_fn(g, operand_type, "smul", "umul"); + break; + } + + g->llvm_fn_table.put(key, fn_val); + return fn_val; +} + +static LLVMValueRef get_float_fn(CodeGen *g, ZigType *type_entry, ZigLLVMFnId fn_id, BuiltinFnId op) { + assert(type_entry->id == ZigTypeIdFloat || + type_entry->id == ZigTypeIdVector); + + bool is_vector = (type_entry->id == ZigTypeIdVector); + ZigType *float_type = is_vector ? type_entry->data.vector.elem_type : type_entry; + + ZigLLVMFnKey key = {}; + key.id = fn_id; + key.data.floating.bit_count = (uint32_t)float_type->data.floating.bit_count; + key.data.floating.vector_len = is_vector ? (uint32_t)type_entry->data.vector.len : 0; + key.data.floating.op = op; + + auto existing_entry = g->llvm_fn_table.maybe_get(key); + if (existing_entry) + return existing_entry->value; + + const char *name; + uint32_t num_args; + if (fn_id == ZigLLVMFnIdFMA) { + name = "fma"; + num_args = 3; + } else if (fn_id == ZigLLVMFnIdFloatOp) { + name = float_op_to_name(op); + num_args = 1; + } else { + zig_unreachable(); + } + + char fn_name[64]; + if (is_vector) + sprintf(fn_name, "llvm.%s.v%" PRIu32 "f%" PRIu32, name, key.data.floating.vector_len, key.data.floating.bit_count); + else + sprintf(fn_name, "llvm.%s.f%" PRIu32, name, key.data.floating.bit_count); + LLVMTypeRef float_type_ref = get_llvm_type(g, type_entry); + LLVMTypeRef return_elem_types[3] = { + float_type_ref, + float_type_ref, + float_type_ref, + }; + LLVMTypeRef fn_type = LLVMFunctionType(float_type_ref, return_elem_types, num_args, false); + LLVMValueRef fn_val = LLVMAddFunction(g->module, fn_name, fn_type); + assert(LLVMGetIntrinsicID(fn_val)); + + g->llvm_fn_table.put(key, fn_val); + return fn_val; +} + +static LLVMValueRef gen_store_untyped(CodeGen *g, LLVMValueRef value, LLVMValueRef ptr, + uint32_t alignment, bool is_volatile) +{ + LLVMValueRef instruction = LLVMBuildStore(g->builder, value, ptr); + if (is_volatile) LLVMSetVolatile(instruction, true); + if (alignment != 0) { + LLVMSetAlignment(instruction, alignment); + } + return instruction; +} + +static LLVMValueRef gen_store(CodeGen *g, LLVMValueRef value, LLVMValueRef ptr, ZigType *ptr_type) { + assert(ptr_type->id == ZigTypeIdPointer); + uint32_t alignment = get_ptr_align(g, ptr_type); + return gen_store_untyped(g, value, ptr, alignment, ptr_type->data.pointer.is_volatile); +} + +static LLVMValueRef gen_load_untyped(CodeGen *g, LLVMValueRef ptr, uint32_t alignment, bool is_volatile, + const char *name) +{ + LLVMValueRef result = LLVMBuildLoad(g->builder, ptr, name); + if (is_volatile) LLVMSetVolatile(result, true); + if (alignment == 0) { + LLVMSetAlignment(result, LLVMABIAlignmentOfType(g->target_data_ref, LLVMGetElementType(LLVMTypeOf(ptr)))); + } else { + LLVMSetAlignment(result, alignment); + } + return result; +} + +static LLVMValueRef gen_load(CodeGen *g, LLVMValueRef ptr, ZigType *ptr_type, const char *name) { + assert(ptr_type->id == ZigTypeIdPointer); + uint32_t alignment = get_ptr_align(g, ptr_type); + return gen_load_untyped(g, ptr, alignment, ptr_type->data.pointer.is_volatile, name); +} + +static LLVMValueRef get_handle_value(CodeGen *g, LLVMValueRef ptr, ZigType *type, ZigType *ptr_type) { + if (type_has_bits(g, type)) { + if (handle_is_ptr(g, type)) { + return ptr; + } else { + assert(ptr_type->id == ZigTypeIdPointer); + return gen_load(g, ptr, ptr_type, ""); + } + } else { + return nullptr; + } +} + +static void ir_assert_impl(bool ok, IrInstGen *source_instruction, const char *file, unsigned int line) { + if (ok) return; + src_assert_impl(ok, source_instruction->base.source_node, file, line); +} + +#define ir_assert(OK, SOURCE_INSTRUCTION) ir_assert_impl((OK), (SOURCE_INSTRUCTION), __FILE__, __LINE__) + +static bool ir_want_fast_math(CodeGen *g, IrInstGen *instruction) { + // TODO memoize + Scope *scope = instruction->base.scope; + while (scope) { + if (scope->id == ScopeIdBlock) { + ScopeBlock *block_scope = (ScopeBlock *)scope; + if (block_scope->fast_math_set_node) + return block_scope->fast_math_on; + } else if (scope->id == ScopeIdDecls) { + ScopeDecls *decls_scope = (ScopeDecls *)scope; + if (decls_scope->fast_math_set_node) + return decls_scope->fast_math_on; + } + scope = scope->parent; + } + return false; +} + +static bool ir_want_runtime_safety_scope(CodeGen *g, Scope *scope) { + // TODO memoize + while (scope) { + if (scope->id == ScopeIdBlock) { + ScopeBlock *block_scope = (ScopeBlock *)scope; + if (block_scope->safety_set_node) + return !block_scope->safety_off; + } else if (scope->id == ScopeIdDecls) { + ScopeDecls *decls_scope = (ScopeDecls *)scope; + if (decls_scope->safety_set_node) + return !decls_scope->safety_off; + } + scope = scope->parent; + } + + return (g->build_mode != BuildModeFastRelease && + g->build_mode != BuildModeSmallRelease); +} + +static bool ir_want_runtime_safety(CodeGen *g, IrInstGen *instruction) { + return ir_want_runtime_safety_scope(g, instruction->base.scope); +} + +static Buf *panic_msg_buf(PanicMsgId msg_id) { + switch (msg_id) { + case PanicMsgIdCount: + zig_unreachable(); + case PanicMsgIdBoundsCheckFailure: + return buf_create_from_str("index out of bounds"); + case PanicMsgIdCastNegativeToUnsigned: + return buf_create_from_str("attempt to cast negative value to unsigned integer"); + case PanicMsgIdCastTruncatedData: + return buf_create_from_str("integer cast truncated bits"); + case PanicMsgIdIntegerOverflow: + return buf_create_from_str("integer overflow"); + case PanicMsgIdShlOverflowedBits: + return buf_create_from_str("left shift overflowed bits"); + case PanicMsgIdShrOverflowedBits: + return buf_create_from_str("right shift overflowed bits"); + case PanicMsgIdDivisionByZero: + return buf_create_from_str("division by zero"); + case PanicMsgIdRemainderDivisionByZero: + return buf_create_from_str("remainder division by zero or negative value"); + case PanicMsgIdExactDivisionRemainder: + return buf_create_from_str("exact division produced remainder"); + case PanicMsgIdUnwrapOptionalFail: + return buf_create_from_str("attempt to use null value"); + case PanicMsgIdUnreachable: + return buf_create_from_str("reached unreachable code"); + case PanicMsgIdInvalidErrorCode: + return buf_create_from_str("invalid error code"); + case PanicMsgIdIncorrectAlignment: + return buf_create_from_str("incorrect alignment"); + case PanicMsgIdBadUnionField: + return buf_create_from_str("access of inactive union field"); + case PanicMsgIdBadEnumValue: + return buf_create_from_str("invalid enum value"); + case PanicMsgIdFloatToInt: + return buf_create_from_str("integer part of floating point value out of bounds"); + case PanicMsgIdPtrCastNull: + return buf_create_from_str("cast causes pointer to be null"); + case PanicMsgIdBadResume: + return buf_create_from_str("resumed an async function which already returned"); + case PanicMsgIdBadAwait: + return buf_create_from_str("async function awaited twice"); + case PanicMsgIdBadReturn: + return buf_create_from_str("async function returned twice"); + case PanicMsgIdResumedAnAwaitingFn: + return buf_create_from_str("awaiting function resumed"); + case PanicMsgIdFrameTooSmall: + return buf_create_from_str("frame too small"); + case PanicMsgIdResumedFnPendingAwait: + return buf_create_from_str("resumed an async function which can only be awaited"); + case PanicMsgIdBadNoSuspendCall: + return buf_create_from_str("async function called in nosuspend scope suspended"); + case PanicMsgIdResumeNotSuspendedFn: + return buf_create_from_str("resumed a non-suspended function"); + case PanicMsgIdBadSentinel: + return buf_create_from_str("sentinel mismatch"); + case PanicMsgIdShxTooBigRhs: + return buf_create_from_str("shift amount is greater than the type size"); + } + zig_unreachable(); +} + +static LLVMValueRef get_panic_msg_ptr_val(CodeGen *g, PanicMsgId msg_id) { + ZigValue *val = &g->panic_msg_vals[msg_id]; + if (!val->llvm_global) { + + Buf *buf_msg = panic_msg_buf(msg_id); + ZigValue *array_val = create_const_str_lit(g, buf_msg)->data.x_ptr.data.ref.pointee; + init_const_slice(g, val, array_val, 0, buf_len(buf_msg), true); + + render_const_val(g, val, ""); + render_const_val_global(g, val, ""); + + assert(val->llvm_global); + } + + ZigType *u8_ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false, + PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0, false); + ZigType *str_type = get_slice_type(g, u8_ptr_type); + return LLVMConstBitCast(val->llvm_global, LLVMPointerType(get_llvm_type(g, str_type), 0)); +} + +static ZigType *ptr_to_stack_trace_type(CodeGen *g) { + return get_pointer_to_type(g, get_stack_trace_type(g), false); +} + +static void gen_panic(CodeGen *g, LLVMValueRef msg_arg, LLVMValueRef stack_trace_arg, + bool stack_trace_is_llvm_alloca) +{ + assert(g->panic_fn != nullptr); + LLVMValueRef fn_val = fn_llvm_value(g, g->panic_fn); + ZigLLVM_CallingConv llvm_cc = get_llvm_cc(g, g->panic_fn->type_entry->data.fn.fn_type_id.cc); + if (stack_trace_arg == nullptr) { + stack_trace_arg = LLVMConstNull(get_llvm_type(g, ptr_to_stack_trace_type(g))); + } + LLVMValueRef args[] = { + msg_arg, + stack_trace_arg, + }; + ZigLLVMBuildCall(g->builder, fn_val, args, 2, llvm_cc, ZigLLVM_CallAttrAuto, ""); + if (!stack_trace_is_llvm_alloca) { + // The stack trace argument is not in the stack of the caller, so + // we'd like to set tail call here, but because slices (the type of msg_arg) are + // still passed as pointers (see https://github.com/ziglang/zig/issues/561) we still + // cannot make this a tail call. + //LLVMSetTailCall(call_instruction, true); + } + LLVMBuildUnreachable(g->builder); +} + +// TODO update most callsites to call gen_assertion instead of this +static void gen_safety_crash(CodeGen *g, PanicMsgId msg_id) { + gen_panic(g, get_panic_msg_ptr_val(g, msg_id), nullptr, false); +} + +static void gen_assertion_scope(CodeGen *g, PanicMsgId msg_id, Scope *source_scope) { + if (ir_want_runtime_safety_scope(g, source_scope)) { + gen_safety_crash(g, msg_id); + } else { + LLVMBuildUnreachable(g->builder); + } +} + +static void gen_assertion(CodeGen *g, PanicMsgId msg_id, IrInstGen *source_instruction) { + return gen_assertion_scope(g, msg_id, source_instruction->base.scope); +} + +static LLVMValueRef gen_wasm_memory_size(CodeGen *g) { + if (g->wasm_memory_size) + return g->wasm_memory_size; + + // TODO adjust for wasm64 as well + // declare i32 @llvm.wasm.memory.size.i32(i32) nounwind readonly + LLVMTypeRef param_type = LLVMInt32Type(); + LLVMTypeRef fn_type = LLVMFunctionType(LLVMInt32Type(), ¶m_type, 1, false); + g->wasm_memory_size = LLVMAddFunction(g->module, "llvm.wasm.memory.size.i32", fn_type); + assert(LLVMGetIntrinsicID(g->wasm_memory_size)); + + return g->wasm_memory_size; +} + +static LLVMValueRef gen_wasm_memory_grow(CodeGen *g) { + if (g->wasm_memory_grow) + return g->wasm_memory_grow; + + // TODO adjust for wasm64 as well + // declare i32 @llvm.wasm.memory.grow.i32(i32, i32) nounwind + LLVMTypeRef param_types[] = { + LLVMInt32Type(), + LLVMInt32Type(), + }; + LLVMTypeRef fn_type = LLVMFunctionType(LLVMInt32Type(), param_types, 2, false); + g->wasm_memory_grow = LLVMAddFunction(g->module, "llvm.wasm.memory.grow.i32", fn_type); + assert(LLVMGetIntrinsicID(g->wasm_memory_grow)); + + return g->wasm_memory_grow; +} + +static LLVMValueRef get_stacksave_fn_val(CodeGen *g) { + if (g->stacksave_fn_val) + return g->stacksave_fn_val; + + // declare i8* @llvm.stacksave() + + LLVMTypeRef fn_type = LLVMFunctionType(LLVMPointerType(LLVMInt8Type(), 0), nullptr, 0, false); + g->stacksave_fn_val = LLVMAddFunction(g->module, "llvm.stacksave", fn_type); + assert(LLVMGetIntrinsicID(g->stacksave_fn_val)); + + return g->stacksave_fn_val; +} + +static LLVMValueRef get_stackrestore_fn_val(CodeGen *g) { + if (g->stackrestore_fn_val) + return g->stackrestore_fn_val; + + // declare void @llvm.stackrestore(i8* %ptr) + + LLVMTypeRef param_type = LLVMPointerType(LLVMInt8Type(), 0); + LLVMTypeRef fn_type = LLVMFunctionType(LLVMVoidType(), ¶m_type, 1, false); + g->stackrestore_fn_val = LLVMAddFunction(g->module, "llvm.stackrestore", fn_type); + assert(LLVMGetIntrinsicID(g->stackrestore_fn_val)); + + return g->stackrestore_fn_val; +} + +static LLVMValueRef get_write_register_fn_val(CodeGen *g) { + if (g->write_register_fn_val) + return g->write_register_fn_val; + + // declare void @llvm.write_register.i64(metadata, i64 @value) + // !0 = !{!"sp\00"} + + LLVMTypeRef param_types[] = { + LLVMMetadataTypeInContext(LLVMGetGlobalContext()), + LLVMIntType(g->pointer_size_bytes * 8), + }; + + LLVMTypeRef fn_type = LLVMFunctionType(LLVMVoidType(), param_types, 2, false); + Buf *name = buf_sprintf("llvm.write_register.i%d", g->pointer_size_bytes * 8); + g->write_register_fn_val = LLVMAddFunction(g->module, buf_ptr(name), fn_type); + assert(LLVMGetIntrinsicID(g->write_register_fn_val)); + + return g->write_register_fn_val; +} + +static LLVMValueRef get_return_address_fn_val(CodeGen *g) { + if (g->return_address_fn_val) + return g->return_address_fn_val; + + ZigType *return_type = get_pointer_to_type(g, g->builtin_types.entry_u8, true); + + LLVMTypeRef fn_type = LLVMFunctionType(get_llvm_type(g, return_type), + &g->builtin_types.entry_i32->llvm_type, 1, false); + g->return_address_fn_val = LLVMAddFunction(g->module, "llvm.returnaddress", fn_type); + assert(LLVMGetIntrinsicID(g->return_address_fn_val)); + + return g->return_address_fn_val; +} + +static LLVMValueRef get_add_error_return_trace_addr_fn(CodeGen *g) { + if (g->add_error_return_trace_addr_fn_val != nullptr) + return g->add_error_return_trace_addr_fn_val; + + LLVMTypeRef arg_types[] = { + get_llvm_type(g, ptr_to_stack_trace_type(g)), + g->builtin_types.entry_usize->llvm_type, + }; + LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), arg_types, 2, false); + + const char *fn_name = get_mangled_name(g, "__zig_add_err_ret_trace_addr"); + LLVMValueRef fn_val = LLVMAddFunction(g->module, fn_name, fn_type_ref); + addLLVMFnAttr(fn_val, "alwaysinline"); + LLVMSetLinkage(fn_val, LLVMInternalLinkage); + ZigLLVMFunctionSetCallingConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified)); + addLLVMFnAttr(fn_val, "nounwind"); + add_uwtable_attr(g, fn_val); + // Error return trace memory is in the stack, which is impossible to be at address 0 + // on any architecture. + addLLVMArgAttr(fn_val, (unsigned)0, "nonnull"); + if (codegen_have_frame_pointer(g)) { + ZigLLVMAddFunctionAttr(fn_val, "frame-pointer", "all"); + } + + LLVMBasicBlockRef entry_block = LLVMAppendBasicBlock(fn_val, "Entry"); + LLVMBasicBlockRef prev_block = LLVMGetInsertBlock(g->builder); + LLVMValueRef prev_debug_location = LLVMGetCurrentDebugLocation(g->builder); + LLVMPositionBuilderAtEnd(g->builder, entry_block); + ZigLLVMClearCurrentDebugLocation(g->builder); + + LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; + + // stack_trace.instruction_addresses[stack_trace.index & (stack_trace.instruction_addresses.len - 1)] = return_address; + + LLVMValueRef err_ret_trace_ptr = LLVMGetParam(fn_val, 0); + LLVMValueRef address_value = LLVMGetParam(fn_val, 1); + + size_t index_field_index = g->stack_trace_type->data.structure.fields[0]->gen_index; + LLVMValueRef index_field_ptr = LLVMBuildStructGEP(g->builder, err_ret_trace_ptr, (unsigned)index_field_index, ""); + size_t addresses_field_index = g->stack_trace_type->data.structure.fields[1]->gen_index; + LLVMValueRef addresses_field_ptr = LLVMBuildStructGEP(g->builder, err_ret_trace_ptr, (unsigned)addresses_field_index, ""); + + ZigType *slice_type = g->stack_trace_type->data.structure.fields[1]->type_entry; + size_t ptr_field_index = slice_type->data.structure.fields[slice_ptr_index]->gen_index; + LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)ptr_field_index, ""); + size_t len_field_index = slice_type->data.structure.fields[slice_len_index]->gen_index; + LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)len_field_index, ""); + + LLVMValueRef len_value = gen_load_untyped(g, len_field_ptr, 0, false, ""); + LLVMValueRef index_val = gen_load_untyped(g, index_field_ptr, 0, false, ""); + LLVMValueRef len_val_minus_one = LLVMBuildSub(g->builder, len_value, LLVMConstInt(usize_type_ref, 1, false), ""); + LLVMValueRef masked_val = LLVMBuildAnd(g->builder, index_val, len_val_minus_one, ""); + LLVMValueRef address_indices[] = { + masked_val, + }; + + LLVMValueRef ptr_value = gen_load_untyped(g, ptr_field_ptr, 0, false, ""); + LLVMValueRef address_slot = LLVMBuildInBoundsGEP(g->builder, ptr_value, address_indices, 1, ""); + + gen_store_untyped(g, address_value, address_slot, 0, false); + + // stack_trace.index += 1; + LLVMValueRef index_plus_one_val = LLVMBuildNUWAdd(g->builder, index_val, LLVMConstInt(usize_type_ref, 1, false), ""); + gen_store_untyped(g, index_plus_one_val, index_field_ptr, 0, false); + + // return; + LLVMBuildRetVoid(g->builder); + + LLVMPositionBuilderAtEnd(g->builder, prev_block); + if (!g->strip_debug_symbols) { + LLVMSetCurrentDebugLocation(g->builder, prev_debug_location); + } + + g->add_error_return_trace_addr_fn_val = fn_val; + return fn_val; +} + +static LLVMValueRef get_return_err_fn(CodeGen *g) { + if (g->return_err_fn != nullptr) + return g->return_err_fn; + + assert(g->err_tag_type != nullptr); + + LLVMTypeRef arg_types[] = { + // error return trace pointer + get_llvm_type(g, ptr_to_stack_trace_type(g)), + }; + LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), arg_types, 1, false); + + const char *fn_name = get_mangled_name(g, "__zig_return_error"); + LLVMValueRef fn_val = LLVMAddFunction(g->module, fn_name, fn_type_ref); + addLLVMFnAttr(fn_val, "noinline"); // so that we can look at return address + addLLVMFnAttr(fn_val, "cold"); + LLVMSetLinkage(fn_val, LLVMInternalLinkage); + ZigLLVMFunctionSetCallingConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified)); + addLLVMFnAttr(fn_val, "nounwind"); + add_uwtable_attr(g, fn_val); + if (codegen_have_frame_pointer(g)) { + ZigLLVMAddFunctionAttr(fn_val, "frame-pointer", "all"); + } + + // this is above the ZigLLVMClearCurrentDebugLocation + LLVMValueRef add_error_return_trace_addr_fn_val = get_add_error_return_trace_addr_fn(g); + + LLVMBasicBlockRef entry_block = LLVMAppendBasicBlock(fn_val, "Entry"); + LLVMBasicBlockRef prev_block = LLVMGetInsertBlock(g->builder); + LLVMValueRef prev_debug_location = LLVMGetCurrentDebugLocation(g->builder); + LLVMPositionBuilderAtEnd(g->builder, entry_block); + ZigLLVMClearCurrentDebugLocation(g->builder); + + LLVMValueRef err_ret_trace_ptr = LLVMGetParam(fn_val, 0); + + LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; + LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, g->builtin_types.entry_i32)); + LLVMValueRef return_address_ptr = LLVMBuildCall(g->builder, get_return_address_fn_val(g), &zero, 1, ""); + LLVMValueRef return_address = LLVMBuildPtrToInt(g->builder, return_address_ptr, usize_type_ref, ""); + + LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(fn_val, "Return"); + LLVMBasicBlockRef dest_non_null_block = LLVMAppendBasicBlock(fn_val, "DestNonNull"); + + LLVMValueRef null_dest_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, err_ret_trace_ptr, + LLVMConstNull(LLVMTypeOf(err_ret_trace_ptr)), ""); + LLVMBuildCondBr(g->builder, null_dest_bit, return_block, dest_non_null_block); + + LLVMPositionBuilderAtEnd(g->builder, return_block); + LLVMBuildRetVoid(g->builder); + + LLVMPositionBuilderAtEnd(g->builder, dest_non_null_block); + LLVMValueRef args[] = { err_ret_trace_ptr, return_address }; + ZigLLVMBuildCall(g->builder, add_error_return_trace_addr_fn_val, args, 2, + get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_CallAttrAlwaysInline, ""); + LLVMBuildRetVoid(g->builder); + + LLVMPositionBuilderAtEnd(g->builder, prev_block); + if (!g->strip_debug_symbols) { + LLVMSetCurrentDebugLocation(g->builder, prev_debug_location); + } + + g->return_err_fn = fn_val; + return fn_val; +} + +static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) { + if (g->safety_crash_err_fn != nullptr) + return g->safety_crash_err_fn; + + static const char *unwrap_err_msg_text = "attempt to unwrap error: "; + + g->generate_error_name_table = true; + generate_error_name_table(g); + assert(g->err_name_table != nullptr); + + // Generate the constant part of the error message + LLVMValueRef msg_prefix_init = LLVMConstString(unwrap_err_msg_text, strlen(unwrap_err_msg_text), 1); + LLVMValueRef msg_prefix = LLVMAddGlobal(g->module, LLVMTypeOf(msg_prefix_init), ""); + LLVMSetInitializer(msg_prefix, msg_prefix_init); + LLVMSetLinkage(msg_prefix, LLVMPrivateLinkage); + LLVMSetGlobalConstant(msg_prefix, true); + + const char *fn_name = get_mangled_name(g, "__zig_fail_unwrap"); + LLVMTypeRef fn_type_ref; + if (g->have_err_ret_tracing) { + LLVMTypeRef arg_types[] = { + get_llvm_type(g, get_pointer_to_type(g, get_stack_trace_type(g), false)), + get_llvm_type(g, g->err_tag_type), + }; + fn_type_ref = LLVMFunctionType(LLVMVoidType(), arg_types, 2, false); + } else { + LLVMTypeRef arg_types[] = { + get_llvm_type(g, g->err_tag_type), + }; + fn_type_ref = LLVMFunctionType(LLVMVoidType(), arg_types, 1, false); + } + LLVMValueRef fn_val = LLVMAddFunction(g->module, fn_name, fn_type_ref); + addLLVMFnAttr(fn_val, "noreturn"); + addLLVMFnAttr(fn_val, "cold"); + LLVMSetLinkage(fn_val, LLVMInternalLinkage); + ZigLLVMFunctionSetCallingConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified)); + addLLVMFnAttr(fn_val, "nounwind"); + add_uwtable_attr(g, fn_val); + if (codegen_have_frame_pointer(g)) { + ZigLLVMAddFunctionAttr(fn_val, "frame-pointer", "all"); + } + // Not setting alignment here. See the comment above about + // "Cannot getTypeInfo() on a type that is unsized!" + // assertion failure on Darwin. + + LLVMBasicBlockRef entry_block = LLVMAppendBasicBlock(fn_val, "Entry"); + LLVMBasicBlockRef prev_block = LLVMGetInsertBlock(g->builder); + LLVMValueRef prev_debug_location = LLVMGetCurrentDebugLocation(g->builder); + LLVMPositionBuilderAtEnd(g->builder, entry_block); + ZigLLVMClearCurrentDebugLocation(g->builder); + + ZigType *usize_ty = g->builtin_types.entry_usize; + ZigType *u8_ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false, + PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0, false); + ZigType *str_type = get_slice_type(g, u8_ptr_type); + + // Allocate a buffer to hold the fully-formatted error message + const size_t err_buf_len = strlen(unwrap_err_msg_text) + g->largest_err_name_len; + LLVMValueRef max_msg_len = LLVMConstInt(usize_ty->llvm_type, err_buf_len, 0); + LLVMValueRef msg_buffer = LLVMBuildArrayAlloca(g->builder, LLVMInt8Type(), max_msg_len, "msg_buffer"); + + // Allocate a []u8 slice for the message + LLVMValueRef msg_slice = build_alloca(g, str_type, "msg_slice", 0); + + LLVMValueRef err_ret_trace_arg; + LLVMValueRef err_val; + if (g->have_err_ret_tracing) { + err_ret_trace_arg = LLVMGetParam(fn_val, 0); + err_val = LLVMGetParam(fn_val, 1); + } else { + err_ret_trace_arg = nullptr; + err_val = LLVMGetParam(fn_val, 0); + } + + // Fetch the error name from the global table + LLVMValueRef err_table_indices[] = { + LLVMConstNull(usize_ty->llvm_type), + err_val, + }; + LLVMValueRef err_name_val = LLVMBuildInBoundsGEP(g->builder, g->err_name_table, err_table_indices, 2, ""); + + LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, err_name_val, slice_ptr_index, ""); + LLVMValueRef err_name_ptr = gen_load_untyped(g, ptr_field_ptr, 0, false, ""); + + LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, err_name_val, slice_len_index, ""); + LLVMValueRef err_name_len = gen_load_untyped(g, len_field_ptr, 0, false, ""); + + LLVMValueRef msg_prefix_len = LLVMConstInt(usize_ty->llvm_type, strlen(unwrap_err_msg_text), false); + // Points to the beginning of msg_buffer + LLVMValueRef msg_buffer_ptr_indices[] = { + LLVMConstNull(usize_ty->llvm_type), + }; + LLVMValueRef msg_buffer_ptr = LLVMBuildInBoundsGEP(g->builder, msg_buffer, msg_buffer_ptr_indices, 1, ""); + // Points to the beginning of the constant prefix message + LLVMValueRef msg_prefix_ptr_indices[] = { + LLVMConstNull(usize_ty->llvm_type), + }; + LLVMValueRef msg_prefix_ptr = LLVMConstInBoundsGEP(msg_prefix, msg_prefix_ptr_indices, 1); + + // Build the message using the prefix... + ZigLLVMBuildMemCpy(g->builder, msg_buffer_ptr, 1, msg_prefix_ptr, 1, msg_prefix_len, false); + // ..and append the error name + LLVMValueRef msg_buffer_ptr_after_indices[] = { + msg_prefix_len, + }; + LLVMValueRef msg_buffer_ptr_after = LLVMBuildInBoundsGEP(g->builder, msg_buffer, msg_buffer_ptr_after_indices, 1, ""); + ZigLLVMBuildMemCpy(g->builder, msg_buffer_ptr_after, 1, err_name_ptr, 1, err_name_len, false); + + // Set the slice pointer + LLVMValueRef msg_slice_ptr_field_ptr = LLVMBuildStructGEP(g->builder, msg_slice, slice_ptr_index, ""); + gen_store_untyped(g, msg_buffer_ptr, msg_slice_ptr_field_ptr, 0, false); + + // Set the slice length + LLVMValueRef slice_len = LLVMBuildNUWAdd(g->builder, msg_prefix_len, err_name_len, ""); + LLVMValueRef msg_slice_len_field_ptr = LLVMBuildStructGEP(g->builder, msg_slice, slice_len_index, ""); + gen_store_untyped(g, slice_len, msg_slice_len_field_ptr, 0, false); + + // Call panic() + gen_panic(g, msg_slice, err_ret_trace_arg, false); + + LLVMPositionBuilderAtEnd(g->builder, prev_block); + if (!g->strip_debug_symbols) { + LLVMSetCurrentDebugLocation(g->builder, prev_debug_location); + } + + g->safety_crash_err_fn = fn_val; + return fn_val; +} + +static LLVMValueRef get_cur_err_ret_trace_val(CodeGen *g, Scope *scope, bool *is_llvm_alloca) { + if (!g->have_err_ret_tracing) { + *is_llvm_alloca = false; + return nullptr; + } + if (g->cur_err_ret_trace_val_stack != nullptr) { + *is_llvm_alloca = !fn_is_async(g->cur_fn); + return g->cur_err_ret_trace_val_stack; + } + *is_llvm_alloca = false; + return g->cur_err_ret_trace_val_arg; +} + +static void gen_safety_crash_for_err(CodeGen *g, LLVMValueRef err_val, Scope *scope) { + LLVMValueRef safety_crash_err_fn = get_safety_crash_err_fn(g); + LLVMValueRef call_instruction; + bool is_llvm_alloca = false; + if (g->have_err_ret_tracing) { + LLVMValueRef err_ret_trace_val = get_cur_err_ret_trace_val(g, scope, &is_llvm_alloca); + if (err_ret_trace_val == nullptr) { + err_ret_trace_val = LLVMConstNull(get_llvm_type(g, ptr_to_stack_trace_type(g))); + } + LLVMValueRef args[] = { + err_ret_trace_val, + err_val, + }; + call_instruction = ZigLLVMBuildCall(g->builder, safety_crash_err_fn, args, 2, + get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_CallAttrAuto, ""); + } else { + LLVMValueRef args[] = { + err_val, + }; + call_instruction = ZigLLVMBuildCall(g->builder, safety_crash_err_fn, args, 1, + get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_CallAttrAuto, ""); + } + if (!is_llvm_alloca) { + LLVMSetTailCall(call_instruction, true); + } + LLVMBuildUnreachable(g->builder); +} + +static void add_bounds_check(CodeGen *g, LLVMValueRef target_val, + LLVMIntPredicate lower_pred, LLVMValueRef lower_value, + LLVMIntPredicate upper_pred, LLVMValueRef upper_value) +{ + if (!lower_value && !upper_value) { + return; + } + if (upper_value && !lower_value) { + lower_value = upper_value; + lower_pred = upper_pred; + upper_value = nullptr; + } + + LLVMBasicBlockRef bounds_check_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "BoundsCheckFail"); + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "BoundsCheckOk"); + LLVMBasicBlockRef lower_ok_block = upper_value ? + LLVMAppendBasicBlock(g->cur_fn_val, "FirstBoundsCheckOk") : ok_block; + + LLVMValueRef lower_ok_val = LLVMBuildICmp(g->builder, lower_pred, target_val, lower_value, ""); + LLVMBuildCondBr(g->builder, lower_ok_val, lower_ok_block, bounds_check_fail_block); + + LLVMPositionBuilderAtEnd(g->builder, bounds_check_fail_block); + gen_safety_crash(g, PanicMsgIdBoundsCheckFailure); + + if (upper_value) { + LLVMPositionBuilderAtEnd(g->builder, lower_ok_block); + LLVMValueRef upper_ok_val = LLVMBuildICmp(g->builder, upper_pred, target_val, upper_value, ""); + LLVMBuildCondBr(g->builder, upper_ok_val, ok_block, bounds_check_fail_block); + } + + LLVMPositionBuilderAtEnd(g->builder, ok_block); +} + +static void add_sentinel_check(CodeGen *g, LLVMValueRef sentinel_elem_ptr, ZigValue *sentinel) { + LLVMValueRef expected_sentinel = gen_const_val(g, sentinel, ""); + + LLVMValueRef actual_sentinel = gen_load_untyped(g, sentinel_elem_ptr, 0, false, ""); + LLVMValueRef ok_bit; + if (sentinel->type->id == ZigTypeIdFloat) { + ok_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, actual_sentinel, expected_sentinel, ""); + } else { + ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, actual_sentinel, expected_sentinel, ""); + } + + LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "SentinelFail"); + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "SentinelOk"); + LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); + + LLVMPositionBuilderAtEnd(g->builder, fail_block); + gen_safety_crash(g, PanicMsgIdBadSentinel); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); +} + +static LLVMValueRef gen_assert_zero(CodeGen *g, LLVMValueRef expr_val, ZigType *int_type) { + LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, int_type)); + LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, expr_val, zero, ""); + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "CastShortenOk"); + LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "CastShortenFail"); + LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); + + LLVMPositionBuilderAtEnd(g->builder, fail_block); + gen_safety_crash(g, PanicMsgIdCastTruncatedData); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + return nullptr; +} + +static LLVMValueRef gen_widen_or_shorten(CodeGen *g, bool want_runtime_safety, ZigType *actual_type, + ZigType *wanted_type, LLVMValueRef expr_val) +{ + assert(actual_type->id == wanted_type->id); + assert(expr_val != nullptr); + + uint64_t actual_bits; + uint64_t wanted_bits; + if (actual_type->id == ZigTypeIdFloat) { + actual_bits = actual_type->data.floating.bit_count; + wanted_bits = wanted_type->data.floating.bit_count; + } else if (actual_type->id == ZigTypeIdInt) { + actual_bits = actual_type->data.integral.bit_count; + wanted_bits = wanted_type->data.integral.bit_count; + } else { + zig_unreachable(); + } + + if (actual_type->id == ZigTypeIdInt && want_runtime_safety && ( + // negative to unsigned + (!wanted_type->data.integral.is_signed && actual_type->data.integral.is_signed) || + // unsigned would become negative + (wanted_type->data.integral.is_signed && !actual_type->data.integral.is_signed && actual_bits == wanted_bits))) + { + LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, actual_type)); + LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntSGE, expr_val, zero, ""); + + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "SignCastOk"); + LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "SignCastFail"); + LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); + + LLVMPositionBuilderAtEnd(g->builder, fail_block); + gen_safety_crash(g, actual_type->data.integral.is_signed ? PanicMsgIdCastNegativeToUnsigned : PanicMsgIdCastTruncatedData); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + } + + if (actual_bits == wanted_bits) { + return expr_val; + } else if (actual_bits < wanted_bits) { + if (actual_type->id == ZigTypeIdFloat) { + return LLVMBuildFPExt(g->builder, expr_val, get_llvm_type(g, wanted_type), ""); + } else if (actual_type->id == ZigTypeIdInt) { + if (actual_type->data.integral.is_signed) { + return LLVMBuildSExt(g->builder, expr_val, get_llvm_type(g, wanted_type), ""); + } else { + return LLVMBuildZExt(g->builder, expr_val, get_llvm_type(g, wanted_type), ""); + } + } else { + zig_unreachable(); + } + } else if (actual_bits > wanted_bits) { + if (actual_type->id == ZigTypeIdFloat) { + return LLVMBuildFPTrunc(g->builder, expr_val, get_llvm_type(g, wanted_type), ""); + } else if (actual_type->id == ZigTypeIdInt) { + if (wanted_bits == 0) { + if (!want_runtime_safety) + return nullptr; + + return gen_assert_zero(g, expr_val, actual_type); + } + LLVMValueRef trunc_val = LLVMBuildTrunc(g->builder, expr_val, get_llvm_type(g, wanted_type), ""); + if (!want_runtime_safety) { + return trunc_val; + } + LLVMValueRef orig_val; + if (wanted_type->data.integral.is_signed) { + orig_val = LLVMBuildSExt(g->builder, trunc_val, get_llvm_type(g, actual_type), ""); + } else { + orig_val = LLVMBuildZExt(g->builder, trunc_val, get_llvm_type(g, actual_type), ""); + } + LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, expr_val, orig_val, ""); + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "CastShortenOk"); + LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "CastShortenFail"); + LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); + + LLVMPositionBuilderAtEnd(g->builder, fail_block); + gen_safety_crash(g, PanicMsgIdCastTruncatedData); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + return trunc_val; + } else { + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +typedef LLVMValueRef (*BuildBinOpFunc)(LLVMBuilderRef, LLVMValueRef, LLVMValueRef, const char *); +// These are lookup table using the AddSubMul enum as the lookup. +// If AddSubMul ever changes, then these tables will be out of +// date. +static const BuildBinOpFunc float_op[3] = { LLVMBuildFAdd, LLVMBuildFSub, LLVMBuildFMul }; +static const BuildBinOpFunc wrap_op[3] = { LLVMBuildAdd, LLVMBuildSub, LLVMBuildMul }; +static const BuildBinOpFunc signed_op[3] = { LLVMBuildNSWAdd, LLVMBuildNSWSub, LLVMBuildNSWMul }; +static const BuildBinOpFunc unsigned_op[3] = { LLVMBuildNUWAdd, LLVMBuildNUWSub, LLVMBuildNUWMul }; + +static LLVMValueRef gen_overflow_op(CodeGen *g, ZigType *operand_type, AddSubMul op, + LLVMValueRef val1, LLVMValueRef val2) +{ + LLVMValueRef overflow_bit; + LLVMValueRef result; + + if (operand_type->id == ZigTypeIdVector) { + ZigType *int_type = operand_type->data.vector.elem_type; + assert(int_type->id == ZigTypeIdInt); + LLVMTypeRef one_more_bit_int = LLVMIntType(int_type->data.integral.bit_count + 1); + LLVMTypeRef one_more_bit_int_vector = LLVMVectorType(one_more_bit_int, operand_type->data.vector.len); + const auto buildExtFn = int_type->data.integral.is_signed ? LLVMBuildSExt : LLVMBuildZExt; + LLVMValueRef extended1 = buildExtFn(g->builder, val1, one_more_bit_int_vector, ""); + LLVMValueRef extended2 = buildExtFn(g->builder, val2, one_more_bit_int_vector, ""); + LLVMValueRef extended_result = wrap_op[op](g->builder, extended1, extended2, ""); + result = LLVMBuildTrunc(g->builder, extended_result, get_llvm_type(g, operand_type), ""); + + LLVMValueRef re_extended_result = buildExtFn(g->builder, result, one_more_bit_int_vector, ""); + LLVMValueRef overflow_vector = LLVMBuildICmp(g->builder, LLVMIntNE, extended_result, re_extended_result, ""); + LLVMTypeRef bitcast_int_type = LLVMIntType(operand_type->data.vector.len); + LLVMValueRef bitcasted_overflow = LLVMBuildBitCast(g->builder, overflow_vector, bitcast_int_type, ""); + LLVMValueRef zero = LLVMConstNull(bitcast_int_type); + overflow_bit = LLVMBuildICmp(g->builder, LLVMIntNE, bitcasted_overflow, zero, ""); + } else { + LLVMValueRef fn_val = get_int_overflow_fn(g, operand_type, op); + LLVMValueRef params[] = { + val1, + val2, + }; + LLVMValueRef result_struct = LLVMBuildCall(g->builder, fn_val, params, 2, ""); + result = LLVMBuildExtractValue(g->builder, result_struct, 0, ""); + overflow_bit = LLVMBuildExtractValue(g->builder, result_struct, 1, ""); + } + + LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowFail"); + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowOk"); + LLVMBuildCondBr(g->builder, overflow_bit, fail_block, ok_block); + + LLVMPositionBuilderAtEnd(g->builder, fail_block); + gen_safety_crash(g, PanicMsgIdIntegerOverflow); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + return result; +} + +static LLVMIntPredicate cmp_op_to_int_predicate(IrBinOp cmp_op, bool is_signed) { + switch (cmp_op) { + case IrBinOpCmpEq: + return LLVMIntEQ; + case IrBinOpCmpNotEq: + return LLVMIntNE; + case IrBinOpCmpLessThan: + return is_signed ? LLVMIntSLT : LLVMIntULT; + case IrBinOpCmpGreaterThan: + return is_signed ? LLVMIntSGT : LLVMIntUGT; + case IrBinOpCmpLessOrEq: + return is_signed ? LLVMIntSLE : LLVMIntULE; + case IrBinOpCmpGreaterOrEq: + return is_signed ? LLVMIntSGE : LLVMIntUGE; + default: + zig_unreachable(); + } +} + +static LLVMRealPredicate cmp_op_to_real_predicate(IrBinOp cmp_op) { + switch (cmp_op) { + case IrBinOpCmpEq: + return LLVMRealOEQ; + case IrBinOpCmpNotEq: + return LLVMRealUNE; + case IrBinOpCmpLessThan: + return LLVMRealOLT; + case IrBinOpCmpGreaterThan: + return LLVMRealOGT; + case IrBinOpCmpLessOrEq: + return LLVMRealOLE; + case IrBinOpCmpGreaterOrEq: + return LLVMRealOGE; + default: + zig_unreachable(); + } +} + +static void gen_assign_raw(CodeGen *g, LLVMValueRef ptr, ZigType *ptr_type, + LLVMValueRef value) +{ + assert(ptr_type->id == ZigTypeIdPointer); + ZigType *child_type = ptr_type->data.pointer.child_type; + + if (!type_has_bits(g, child_type)) + return; + + if (handle_is_ptr(g, child_type)) { + assert(LLVMGetTypeKind(LLVMTypeOf(value)) == LLVMPointerTypeKind); + assert(LLVMGetTypeKind(LLVMTypeOf(ptr)) == LLVMPointerTypeKind); + + LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0); + + LLVMValueRef src_ptr = LLVMBuildBitCast(g->builder, value, ptr_u8, ""); + LLVMValueRef dest_ptr = LLVMBuildBitCast(g->builder, ptr, ptr_u8, ""); + + ZigType *usize = g->builtin_types.entry_usize; + uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, get_llvm_type(g, child_type)); + uint64_t align_bytes = get_ptr_align(g, ptr_type); + assert(size_bytes > 0); + assert(align_bytes > 0); + + ZigLLVMBuildMemCpy(g->builder, dest_ptr, align_bytes, src_ptr, align_bytes, + LLVMConstInt(usize->llvm_type, size_bytes, false), + ptr_type->data.pointer.is_volatile); + return; + } + + assert(ptr_type->data.pointer.vector_index != VECTOR_INDEX_RUNTIME); + if (ptr_type->data.pointer.vector_index != VECTOR_INDEX_NONE) { + LLVMValueRef index_val = LLVMConstInt(LLVMInt32Type(), + ptr_type->data.pointer.vector_index, false); + LLVMValueRef loaded_vector = gen_load(g, ptr, ptr_type, ""); + LLVMValueRef new_vector = LLVMBuildInsertElement(g->builder, loaded_vector, value, + index_val, ""); + gen_store(g, new_vector, ptr, ptr_type); + return; + } + + uint32_t host_int_bytes = ptr_type->data.pointer.host_int_bytes; + if (host_int_bytes == 0) { + gen_store(g, value, ptr, ptr_type); + return; + } + + bool big_endian = g->is_big_endian; + + LLVMTypeRef int_ptr_ty = LLVMPointerType(LLVMIntType(host_int_bytes * 8), 0); + LLVMValueRef int_ptr = LLVMBuildBitCast(g->builder, ptr, int_ptr_ty, ""); + LLVMValueRef containing_int = gen_load(g, int_ptr, ptr_type, ""); + uint32_t host_bit_count = LLVMGetIntTypeWidth(LLVMTypeOf(containing_int)); + assert(host_bit_count == host_int_bytes * 8); + uint32_t size_in_bits = type_size_bits(g, child_type); + + uint32_t bit_offset = ptr_type->data.pointer.bit_offset_in_host; + uint32_t shift_amt = big_endian ? host_bit_count - bit_offset - size_in_bits : bit_offset; + LLVMValueRef shift_amt_val = LLVMConstInt(LLVMTypeOf(containing_int), shift_amt, false); + + // Convert to equally-sized integer type in order to perform the bit + // operations on the value to store + LLVMTypeRef value_bits_type = LLVMIntType(size_in_bits); + LLVMValueRef value_bits = LLVMBuildBitCast(g->builder, value, value_bits_type, ""); + + LLVMValueRef mask_val = LLVMConstAllOnes(value_bits_type); + mask_val = LLVMConstZExt(mask_val, LLVMTypeOf(containing_int)); + mask_val = LLVMConstShl(mask_val, shift_amt_val); + mask_val = LLVMConstNot(mask_val); + + LLVMValueRef anded_containing_int = LLVMBuildAnd(g->builder, containing_int, mask_val, ""); + LLVMValueRef extended_value = LLVMBuildZExt(g->builder, value_bits, LLVMTypeOf(containing_int), ""); + LLVMValueRef shifted_value = LLVMBuildShl(g->builder, extended_value, shift_amt_val, ""); + LLVMValueRef ored_value = LLVMBuildOr(g->builder, shifted_value, anded_containing_int, ""); + + gen_store(g, ored_value, int_ptr, ptr_type); +} + +static void gen_var_debug_decl(CodeGen *g, ZigVar *var) { + if (g->strip_debug_symbols) return; + assert(var->di_loc_var != nullptr); + AstNode *source_node = var->decl_node; + ZigLLVMDILocation *debug_loc = ZigLLVMGetDebugLoc((unsigned)source_node->line + 1, + (unsigned)source_node->column + 1, get_di_scope(g, var->parent_scope)); + ZigLLVMInsertDeclareAtEnd(g->dbuilder, var->value_ref, var->di_loc_var, debug_loc, + LLVMGetInsertBlock(g->builder)); +} + +static LLVMValueRef ir_llvm_value(CodeGen *g, IrInstGen *instruction) { + Error err; + + bool value_has_bits; + if ((err = type_has_bits2(g, instruction->value->type, &value_has_bits))) + codegen_report_errors_and_exit(g); + + if (!value_has_bits) + return nullptr; + + if (!instruction->llvm_value) { + if (instruction->id == IrInstGenIdAwait) { + IrInstGenAwait *await = reinterpret_cast(instruction); + if (await->result_loc != nullptr) { + return get_handle_value(g, ir_llvm_value(g, await->result_loc), + await->result_loc->value->type->data.pointer.child_type, await->result_loc->value->type); + } + } + if (instruction->spill != nullptr) { + ZigType *ptr_type = instruction->spill->value->type; + ir_assert(ptr_type->id == ZigTypeIdPointer, instruction); + return get_handle_value(g, ir_llvm_value(g, instruction->spill), + ptr_type->data.pointer.child_type, instruction->spill->value->type); + } + ir_assert(instruction->value->special != ConstValSpecialRuntime, instruction); + assert(instruction->value->type); + render_const_val(g, instruction->value, ""); + // we might have to do some pointer casting here due to the way union + // values are rendered with a type other than the one we expect + if (handle_is_ptr(g, instruction->value->type)) { + render_const_val_global(g, instruction->value, ""); + ZigType *ptr_type = get_pointer_to_type(g, instruction->value->type, true); + instruction->llvm_value = LLVMBuildBitCast(g->builder, instruction->value->llvm_global, get_llvm_type(g, ptr_type), ""); + } else { + instruction->llvm_value = LLVMBuildBitCast(g->builder, instruction->value->llvm_value, + get_llvm_type(g, instruction->value->type), ""); + } + assert(instruction->llvm_value); + } + return instruction->llvm_value; +} + +void codegen_report_errors_and_exit(CodeGen *g) { + // Clear progress indicator before printing errors + if (g->sub_progress_node != nullptr) { + stage2_progress_end(g->sub_progress_node); + g->sub_progress_node = nullptr; + } + if (g->main_progress_node != nullptr) { + stage2_progress_end(g->main_progress_node); + g->main_progress_node = nullptr; + } + + assert(g->errors.length != 0); + for (size_t i = 0; i < g->errors.length; i += 1) { + ErrorMsg *err = g->errors.at(i); + print_err_msg(err, g->err_color); + } + exit(1); +} + +static void report_errors_and_maybe_exit(CodeGen *g) { + if (g->errors.length != 0) { + codegen_report_errors_and_exit(g); + } +} + +ATTRIBUTE_NORETURN +static void give_up_with_c_abi_error(CodeGen *g, AstNode *source_node) { + ErrorMsg *msg = add_node_error(g, source_node, + buf_sprintf("TODO: support C ABI for more targets. https://github.com/ziglang/zig/issues/1481")); + add_error_note(g, msg, source_node, + buf_sprintf("pointers, integers, floats, bools, and enums work on all targets")); + codegen_report_errors_and_exit(g); +} + +static LLVMValueRef build_alloca(CodeGen *g, ZigType *type_entry, const char *name, uint32_t alignment) { + LLVMValueRef result = LLVMBuildAlloca(g->builder, get_llvm_type(g, type_entry), name); + LLVMSetAlignment(result, (alignment == 0) ? get_abi_alignment(g, type_entry) : alignment); + return result; +} + +static bool iter_function_params_c_abi(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk, size_t src_i) { + // Initialized from the type for some walks, but because of C var args, + // initialized based on callsite instructions for that one. + FnTypeParamInfo *param_info = nullptr; + ZigType *ty; + ZigType *dest_ty = nullptr; + AstNode *source_node = nullptr; + LLVMValueRef val; + LLVMValueRef llvm_fn; + unsigned di_arg_index; + ZigVar *var; + switch (fn_walk->id) { + case FnWalkIdAttrs: + if (src_i >= fn_type->data.fn.fn_type_id.param_count) + return false; + param_info = &fn_type->data.fn.fn_type_id.param_info[src_i]; + ty = param_info->type; + source_node = fn_walk->data.attrs.fn->proto_node; + llvm_fn = fn_walk->data.attrs.llvm_fn; + break; + case FnWalkIdCall: { + if (src_i >= fn_walk->data.call.inst->arg_count) + return false; + IrInstGen *arg = fn_walk->data.call.inst->args[src_i]; + ty = arg->value->type; + source_node = arg->base.source_node; + val = ir_llvm_value(g, arg); + break; + } + case FnWalkIdTypes: + if (src_i >= fn_type->data.fn.fn_type_id.param_count) + return false; + param_info = &fn_type->data.fn.fn_type_id.param_info[src_i]; + ty = param_info->type; + break; + case FnWalkIdVars: + assert(src_i < fn_type->data.fn.fn_type_id.param_count); + param_info = &fn_type->data.fn.fn_type_id.param_info[src_i]; + ty = param_info->type; + var = fn_walk->data.vars.var; + source_node = var->decl_node; + llvm_fn = fn_walk->data.vars.llvm_fn; + break; + case FnWalkIdInits: + if (src_i >= fn_type->data.fn.fn_type_id.param_count) + return false; + param_info = &fn_type->data.fn.fn_type_id.param_info[src_i]; + ty = param_info->type; + var = fn_walk->data.inits.fn->variable_list.at(src_i); + source_node = fn_walk->data.inits.fn->proto_node; + llvm_fn = fn_walk->data.inits.llvm_fn; + break; + } + + if (type_is_c_abi_int_bail(g, ty) || ty->id == ZigTypeIdFloat || ty->id == ZigTypeIdVector || + ty->id == ZigTypeIdInt // TODO investigate if we need to change this + ) { + switch (fn_walk->id) { + case FnWalkIdAttrs: { + ZigType *ptr_type = get_codegen_ptr_type_bail(g, ty); + if (ptr_type != nullptr) { + if (type_is_nonnull_ptr(g, ty)) { + addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "nonnull"); + } + if (ptr_type->id == ZigTypeIdPointer && ptr_type->data.pointer.is_const) { + addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "readonly"); + } + if (param_info->is_noalias) { + addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "noalias"); + } + } + fn_walk->data.attrs.gen_i += 1; + break; + } + case FnWalkIdCall: + fn_walk->data.call.gen_param_values->append(val); + break; + case FnWalkIdTypes: + fn_walk->data.types.gen_param_types->append(get_llvm_type(g, ty)); + fn_walk->data.types.param_di_types->append(get_llvm_di_type(g, ty)); + break; + case FnWalkIdVars: { + var->value_ref = build_alloca(g, ty, var->name, var->align_bytes); + di_arg_index = fn_walk->data.vars.gen_i; + fn_walk->data.vars.gen_i += 1; + dest_ty = ty; + goto var_ok; + } + case FnWalkIdInits: + clear_debug_source_node(g); + gen_store_untyped(g, LLVMGetParam(llvm_fn, fn_walk->data.inits.gen_i), var->value_ref, var->align_bytes, false); + if (var->decl_node) { + gen_var_debug_decl(g, var); + } + fn_walk->data.inits.gen_i += 1; + break; + } + return true; + } + + { + // Arrays are just pointers + if (ty->id == ZigTypeIdArray) { + assert(handle_is_ptr(g, ty)); + switch (fn_walk->id) { + case FnWalkIdAttrs: + // arrays passed to C ABI functions may not be at address 0 + addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "nonnull"); + addLLVMArgAttrInt(llvm_fn, fn_walk->data.attrs.gen_i, "align", get_abi_alignment(g, ty)); + fn_walk->data.attrs.gen_i += 1; + break; + case FnWalkIdCall: + fn_walk->data.call.gen_param_values->append(val); + break; + case FnWalkIdTypes: { + ZigType *gen_type = get_pointer_to_type(g, ty, true); + fn_walk->data.types.gen_param_types->append(get_llvm_type(g, gen_type)); + fn_walk->data.types.param_di_types->append(get_llvm_di_type(g, gen_type)); + break; + } + case FnWalkIdVars: { + var->value_ref = LLVMGetParam(llvm_fn, fn_walk->data.vars.gen_i); + di_arg_index = fn_walk->data.vars.gen_i; + dest_ty = get_pointer_to_type(g, ty, false); + fn_walk->data.vars.gen_i += 1; + goto var_ok; + } + case FnWalkIdInits: + if (var->decl_node) { + gen_var_debug_decl(g, var); + } + fn_walk->data.inits.gen_i += 1; + break; + } + return true; + } + + X64CABIClass abi_class = type_c_abi_x86_64_class(g, ty); + size_t ty_size = type_size(g, ty); + if (abi_class == X64CABIClass_MEMORY || abi_class == X64CABIClass_MEMORY_nobyval) { + assert(handle_is_ptr(g, ty)); + switch (fn_walk->id) { + case FnWalkIdAttrs: + if (abi_class != X64CABIClass_MEMORY_nobyval) { + ZigLLVMAddByValAttr(llvm_fn, fn_walk->data.attrs.gen_i + 1, get_llvm_type(g, ty)); + addLLVMArgAttrInt(llvm_fn, fn_walk->data.attrs.gen_i, "align", get_abi_alignment(g, ty)); + } else if (g->zig_target->arch == ZigLLVM_aarch64 || + g->zig_target->arch == ZigLLVM_aarch64_be) + { + // no attrs needed + } else { + if (source_node != nullptr) { + give_up_with_c_abi_error(g, source_node); + } + // otherwise allow codegen code to report a compile error + return false; + } + + // Byvalue parameters must not have address 0 + addLLVMArgAttr(llvm_fn, fn_walk->data.attrs.gen_i, "nonnull"); + fn_walk->data.attrs.gen_i += 1; + break; + case FnWalkIdCall: + fn_walk->data.call.gen_param_values->append(val); + break; + case FnWalkIdTypes: { + ZigType *gen_type = get_pointer_to_type(g, ty, true); + fn_walk->data.types.gen_param_types->append(get_llvm_type(g, gen_type)); + fn_walk->data.types.param_di_types->append(get_llvm_di_type(g, gen_type)); + break; + } + case FnWalkIdVars: { + di_arg_index = fn_walk->data.vars.gen_i; + var->value_ref = LLVMGetParam(llvm_fn, fn_walk->data.vars.gen_i); + dest_ty = get_pointer_to_type(g, ty, false); + fn_walk->data.vars.gen_i += 1; + goto var_ok; + } + case FnWalkIdInits: + if (var->decl_node) { + gen_var_debug_decl(g, var); + } + fn_walk->data.inits.gen_i += 1; + break; + } + return true; + } else if (abi_class == X64CABIClass_INTEGER) { + switch (fn_walk->id) { + case FnWalkIdAttrs: + fn_walk->data.attrs.gen_i += 1; + break; + case FnWalkIdCall: { + LLVMTypeRef ptr_to_int_type_ref = LLVMPointerType(LLVMIntType((unsigned)ty_size * 8), 0); + LLVMValueRef bitcasted = LLVMBuildBitCast(g->builder, val, ptr_to_int_type_ref, ""); + LLVMValueRef loaded = LLVMBuildLoad(g->builder, bitcasted, ""); + fn_walk->data.call.gen_param_values->append(loaded); + break; + } + case FnWalkIdTypes: { + ZigType *gen_type = get_int_type(g, false, ty_size * 8); + fn_walk->data.types.gen_param_types->append(get_llvm_type(g, gen_type)); + fn_walk->data.types.param_di_types->append(get_llvm_di_type(g, gen_type)); + break; + } + case FnWalkIdVars: { + di_arg_index = fn_walk->data.vars.gen_i; + var->value_ref = build_alloca(g, ty, var->name, var->align_bytes); + fn_walk->data.vars.gen_i += 1; + dest_ty = ty; + goto var_ok; + } + case FnWalkIdInits: { + clear_debug_source_node(g); + if (!fn_is_async(fn_walk->data.inits.fn)) { + LLVMValueRef arg = LLVMGetParam(llvm_fn, fn_walk->data.inits.gen_i); + LLVMTypeRef ptr_to_int_type_ref = LLVMPointerType(LLVMIntType((unsigned)ty_size * 8), 0); + LLVMValueRef bitcasted = LLVMBuildBitCast(g->builder, var->value_ref, ptr_to_int_type_ref, ""); + gen_store_untyped(g, arg, bitcasted, var->align_bytes, false); + } + if (var->decl_node) { + gen_var_debug_decl(g, var); + } + fn_walk->data.inits.gen_i += 1; + break; + } + } + return true; + } else if (abi_class == X64CABIClass_SSE) { + // For now only handle structs with only floats/doubles in it. + if (ty->id != ZigTypeIdStruct) { + if (source_node != nullptr) { + give_up_with_c_abi_error(g, source_node); + } + // otherwise allow codegen code to report a compile error + return false; + } + + for (uint32_t i = 0; i < ty->data.structure.src_field_count; i += 1) { + if (ty->data.structure.fields[i]->type_entry->id != ZigTypeIdFloat) { + if (source_node != nullptr) { + give_up_with_c_abi_error(g, source_node); + } + // otherwise allow codegen code to report a compile error + return false; + } + } + + // The SystemV ABI says that we have to setup 1 FP register per f64. + // So two f32 can be passed in one f64, but 3 f32 have to be passed in 2 FP registers. + // To achieve this with LLVM API, we pass multiple f64 parameters to the LLVM function if + // the type is bigger than 8 bytes. + + // Example: + // extern struct { + // x: f32, + // y: f32, + // z: f32, + // }; + // const ptr = (*f64)*Struct; + // Register 1: ptr.* + // Register 2: (ptr + 1).* + + // One floating point register per f64 or 2 f32's + size_t number_of_fp_regs = (size_t)ceilf((float)ty_size / (float)8); + + switch (fn_walk->id) { + case FnWalkIdAttrs: { + fn_walk->data.attrs.gen_i += 1; + break; + } + case FnWalkIdCall: { + LLVMValueRef f64_ptr_to_struct = LLVMBuildBitCast(g->builder, val, LLVMPointerType(LLVMDoubleType(), 0), ""); + for (uint32_t i = 0; i < number_of_fp_regs; i += 1) { + LLVMValueRef index = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, i, false); + LLVMValueRef indices[] = { index }; + LLVMValueRef adjusted_ptr_to_struct = LLVMBuildInBoundsGEP(g->builder, f64_ptr_to_struct, indices, 1, ""); + LLVMValueRef loaded = LLVMBuildLoad(g->builder, adjusted_ptr_to_struct, ""); + fn_walk->data.call.gen_param_values->append(loaded); + } + break; + } + case FnWalkIdTypes: { + for (uint32_t i = 0; i < number_of_fp_regs; i += 1) { + fn_walk->data.types.gen_param_types->append(get_llvm_type(g, g->builtin_types.entry_f64)); + fn_walk->data.types.param_di_types->append(get_llvm_di_type(g, g->builtin_types.entry_f64)); + } + break; + } + case FnWalkIdVars: + case FnWalkIdInits: { + // TODO: Handle exporting functions + if (source_node != nullptr) { + give_up_with_c_abi_error(g, source_node); + } + // otherwise allow codegen code to report a compile error + return false; + } + } + return true; + } + } + if (source_node != nullptr) { + give_up_with_c_abi_error(g, source_node); + } + // otherwise allow codegen code to report a compile error + return false; + +var_ok: + if (dest_ty != nullptr && var->decl_node) { + // arg index + 1 because the 0 index is return value + var->di_loc_var = ZigLLVMCreateParameterVariable(g->dbuilder, get_di_scope(g, var->parent_scope), + var->name, fn_walk->data.vars.import->data.structure.root_struct->di_file, + (unsigned)(var->decl_node->line + 1), + get_llvm_di_type(g, dest_ty), !g->strip_debug_symbols, 0, di_arg_index + 1); + } + return true; +} + +void walk_function_params(CodeGen *g, ZigType *fn_type, FnWalk *fn_walk) { + CallingConvention cc = fn_type->data.fn.fn_type_id.cc; + if (!calling_convention_allows_zig_types(cc)) { + size_t src_i = 0; + for (;;) { + if (!iter_function_params_c_abi(g, fn_type, fn_walk, src_i)) + break; + src_i += 1; + } + return; + } + if (fn_walk->id == FnWalkIdCall) { + IrInstGenCall *instruction = fn_walk->data.call.inst; + bool is_var_args = fn_walk->data.call.is_var_args; + for (size_t call_i = 0; call_i < instruction->arg_count; call_i += 1) { + IrInstGen *param_instruction = instruction->args[call_i]; + ZigType *param_type = param_instruction->value->type; + if (is_var_args || type_has_bits(g, param_type)) { + LLVMValueRef param_value = ir_llvm_value(g, param_instruction); + assert(param_value); + fn_walk->data.call.gen_param_values->append(param_value); + fn_walk->data.call.gen_param_types->append(param_type); + } + } + return; + } + size_t next_var_i = 0; + for (size_t param_i = 0; param_i < fn_type->data.fn.fn_type_id.param_count; param_i += 1) { + FnGenParamInfo *gen_info = &fn_type->data.fn.gen_param_info[param_i]; + size_t gen_index = gen_info->gen_index; + + if (gen_index == SIZE_MAX) { + continue; + } + + switch (fn_walk->id) { + case FnWalkIdAttrs: { + LLVMValueRef llvm_fn = fn_walk->data.attrs.llvm_fn; + bool is_byval = gen_info->is_byval; + FnTypeParamInfo *param_info = &fn_type->data.fn.fn_type_id.param_info[param_i]; + + ZigType *param_type = gen_info->type; + if (param_info->is_noalias) { + addLLVMArgAttr(llvm_fn, (unsigned)gen_index, "noalias"); + } + if ((param_type->id == ZigTypeIdPointer && param_type->data.pointer.is_const) || is_byval) { + addLLVMArgAttr(llvm_fn, (unsigned)gen_index, "readonly"); + } + if (get_codegen_ptr_type_bail(g, param_type) != nullptr) { + addLLVMArgAttrInt(llvm_fn, (unsigned)gen_index, "align", get_ptr_align(g, param_type)); + } + if (type_is_nonnull_ptr(g, param_type)) { + addLLVMArgAttr(llvm_fn, (unsigned)gen_index, "nonnull"); + } + break; + } + case FnWalkIdInits: { + ZigFn *fn_table_entry = fn_walk->data.inits.fn; + LLVMValueRef llvm_fn = fn_table_entry->llvm_value; + ZigVar *variable = fn_table_entry->variable_list.at(next_var_i); + assert(variable->src_arg_index != SIZE_MAX); + next_var_i += 1; + + assert(variable); + assert(variable->value_ref); + + if (!handle_is_ptr(g, variable->var_type) && !fn_is_async(fn_walk->data.inits.fn)) { + clear_debug_source_node(g); + ZigType *fn_type = fn_table_entry->type_entry; + unsigned gen_arg_index = fn_type->data.fn.gen_param_info[variable->src_arg_index].gen_index; + gen_store_untyped(g, LLVMGetParam(llvm_fn, gen_arg_index), + variable->value_ref, variable->align_bytes, false); + } + + if (variable->decl_node) { + gen_var_debug_decl(g, variable); + } + break; + } + case FnWalkIdCall: + // handled before for loop + zig_unreachable(); + case FnWalkIdTypes: + // Not called for non-c-abi + zig_unreachable(); + case FnWalkIdVars: + // iter_function_params_c_abi is called directly for this one + zig_unreachable(); + } + } +} + +static LLVMValueRef get_merge_err_ret_traces_fn_val(CodeGen *g) { + if (g->merge_err_ret_traces_fn_val) + return g->merge_err_ret_traces_fn_val; + + assert(g->stack_trace_type != nullptr); + + LLVMTypeRef param_types[] = { + get_llvm_type(g, ptr_to_stack_trace_type(g)), + get_llvm_type(g, ptr_to_stack_trace_type(g)), + }; + LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMVoidType(), param_types, 2, false); + + const char *fn_name = get_mangled_name(g, "__zig_merge_error_return_traces"); + LLVMValueRef fn_val = LLVMAddFunction(g->module, fn_name, fn_type_ref); + LLVMSetLinkage(fn_val, LLVMInternalLinkage); + ZigLLVMFunctionSetCallingConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified)); + addLLVMFnAttr(fn_val, "nounwind"); + add_uwtable_attr(g, fn_val); + addLLVMArgAttr(fn_val, (unsigned)0, "noalias"); + addLLVMArgAttr(fn_val, (unsigned)0, "writeonly"); + + addLLVMArgAttr(fn_val, (unsigned)1, "noalias"); + addLLVMArgAttr(fn_val, (unsigned)1, "readonly"); + if (codegen_have_frame_pointer(g)) { + ZigLLVMAddFunctionAttr(fn_val, "frame-pointer", "all"); + } + + // this is above the ZigLLVMClearCurrentDebugLocation + LLVMValueRef add_error_return_trace_addr_fn_val = get_add_error_return_trace_addr_fn(g); + + LLVMBasicBlockRef entry_block = LLVMAppendBasicBlock(fn_val, "Entry"); + LLVMBasicBlockRef prev_block = LLVMGetInsertBlock(g->builder); + LLVMValueRef prev_debug_location = LLVMGetCurrentDebugLocation(g->builder); + LLVMPositionBuilderAtEnd(g->builder, entry_block); + ZigLLVMClearCurrentDebugLocation(g->builder); + + // if (dest_stack_trace == null or src_stack_trace == null) return; + // var frame_index: usize = undefined; + // var frames_left: usize = undefined; + // if (src_stack_trace.index < src_stack_trace.instruction_addresses.len) { + // frame_index = 0; + // frames_left = src_stack_trace.index; + // if (frames_left == 0) return; + // } else { + // frame_index = (src_stack_trace.index + 1) % src_stack_trace.instruction_addresses.len; + // frames_left = src_stack_trace.instruction_addresses.len; + // } + // while (true) { + // __zig_add_err_ret_trace_addr(dest_stack_trace, src_stack_trace.instruction_addresses[frame_index]); + // frames_left -= 1; + // if (frames_left == 0) return; + // frame_index = (frame_index + 1) % src_stack_trace.instruction_addresses.len; + // } + LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(fn_val, "Return"); + LLVMBasicBlockRef non_null_block = LLVMAppendBasicBlock(fn_val, "NonNull"); + + LLVMValueRef frame_index_ptr = LLVMBuildAlloca(g->builder, g->builtin_types.entry_usize->llvm_type, "frame_index"); + LLVMValueRef frames_left_ptr = LLVMBuildAlloca(g->builder, g->builtin_types.entry_usize->llvm_type, "frames_left"); + + LLVMValueRef dest_stack_trace_ptr = LLVMGetParam(fn_val, 0); + LLVMValueRef src_stack_trace_ptr = LLVMGetParam(fn_val, 1); + + LLVMValueRef null_dest_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, dest_stack_trace_ptr, + LLVMConstNull(LLVMTypeOf(dest_stack_trace_ptr)), ""); + LLVMValueRef null_src_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, src_stack_trace_ptr, + LLVMConstNull(LLVMTypeOf(src_stack_trace_ptr)), ""); + LLVMValueRef null_bit = LLVMBuildOr(g->builder, null_dest_bit, null_src_bit, ""); + LLVMBuildCondBr(g->builder, null_bit, return_block, non_null_block); + + LLVMPositionBuilderAtEnd(g->builder, non_null_block); + size_t src_index_field_index = g->stack_trace_type->data.structure.fields[0]->gen_index; + size_t src_addresses_field_index = g->stack_trace_type->data.structure.fields[1]->gen_index; + LLVMValueRef src_index_field_ptr = LLVMBuildStructGEP(g->builder, src_stack_trace_ptr, + (unsigned)src_index_field_index, ""); + LLVMValueRef src_addresses_field_ptr = LLVMBuildStructGEP(g->builder, src_stack_trace_ptr, + (unsigned)src_addresses_field_index, ""); + ZigType *slice_type = g->stack_trace_type->data.structure.fields[1]->type_entry; + size_t ptr_field_index = slice_type->data.structure.fields[slice_ptr_index]->gen_index; + LLVMValueRef src_ptr_field_ptr = LLVMBuildStructGEP(g->builder, src_addresses_field_ptr, (unsigned)ptr_field_index, ""); + size_t len_field_index = slice_type->data.structure.fields[slice_len_index]->gen_index; + LLVMValueRef src_len_field_ptr = LLVMBuildStructGEP(g->builder, src_addresses_field_ptr, (unsigned)len_field_index, ""); + LLVMValueRef src_index_val = LLVMBuildLoad(g->builder, src_index_field_ptr, ""); + LLVMValueRef src_ptr_val = LLVMBuildLoad(g->builder, src_ptr_field_ptr, ""); + LLVMValueRef src_len_val = LLVMBuildLoad(g->builder, src_len_field_ptr, ""); + LLVMValueRef no_wrap_bit = LLVMBuildICmp(g->builder, LLVMIntULT, src_index_val, src_len_val, ""); + LLVMBasicBlockRef no_wrap_block = LLVMAppendBasicBlock(fn_val, "NoWrap"); + LLVMBasicBlockRef yes_wrap_block = LLVMAppendBasicBlock(fn_val, "YesWrap"); + LLVMBasicBlockRef loop_block = LLVMAppendBasicBlock(fn_val, "Loop"); + LLVMBuildCondBr(g->builder, no_wrap_bit, no_wrap_block, yes_wrap_block); + + LLVMPositionBuilderAtEnd(g->builder, no_wrap_block); + LLVMValueRef usize_zero = LLVMConstNull(g->builtin_types.entry_usize->llvm_type); + LLVMBuildStore(g->builder, usize_zero, frame_index_ptr); + LLVMBuildStore(g->builder, src_index_val, frames_left_ptr); + LLVMValueRef frames_left_eq_zero_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, src_index_val, usize_zero, ""); + LLVMBuildCondBr(g->builder, frames_left_eq_zero_bit, return_block, loop_block); + + LLVMPositionBuilderAtEnd(g->builder, yes_wrap_block); + LLVMValueRef usize_one = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, 1, false); + LLVMValueRef plus_one = LLVMBuildNUWAdd(g->builder, src_index_val, usize_one, ""); + LLVMValueRef mod_len = LLVMBuildURem(g->builder, plus_one, src_len_val, ""); + LLVMBuildStore(g->builder, mod_len, frame_index_ptr); + LLVMBuildStore(g->builder, src_len_val, frames_left_ptr); + LLVMBuildBr(g->builder, loop_block); + + LLVMPositionBuilderAtEnd(g->builder, loop_block); + LLVMValueRef ptr_index = LLVMBuildLoad(g->builder, frame_index_ptr, ""); + LLVMValueRef addr_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr_val, &ptr_index, 1, ""); + LLVMValueRef this_addr_val = LLVMBuildLoad(g->builder, addr_ptr, ""); + LLVMValueRef args[] = {dest_stack_trace_ptr, this_addr_val}; + ZigLLVMBuildCall(g->builder, add_error_return_trace_addr_fn_val, args, 2, get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_CallAttrAlwaysInline, ""); + LLVMValueRef prev_frames_left = LLVMBuildLoad(g->builder, frames_left_ptr, ""); + LLVMValueRef new_frames_left = LLVMBuildNUWSub(g->builder, prev_frames_left, usize_one, ""); + LLVMValueRef done_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, new_frames_left, usize_zero, ""); + LLVMBasicBlockRef continue_block = LLVMAppendBasicBlock(fn_val, "Continue"); + LLVMBuildCondBr(g->builder, done_bit, return_block, continue_block); + + LLVMPositionBuilderAtEnd(g->builder, return_block); + LLVMBuildRetVoid(g->builder); + + LLVMPositionBuilderAtEnd(g->builder, continue_block); + LLVMBuildStore(g->builder, new_frames_left, frames_left_ptr); + LLVMValueRef prev_index = LLVMBuildLoad(g->builder, frame_index_ptr, ""); + LLVMValueRef index_plus_one = LLVMBuildNUWAdd(g->builder, prev_index, usize_one, ""); + LLVMValueRef index_mod_len = LLVMBuildURem(g->builder, index_plus_one, src_len_val, ""); + LLVMBuildStore(g->builder, index_mod_len, frame_index_ptr); + LLVMBuildBr(g->builder, loop_block); + + LLVMPositionBuilderAtEnd(g->builder, prev_block); + if (!g->strip_debug_symbols) { + LLVMSetCurrentDebugLocation(g->builder, prev_debug_location); + } + + g->merge_err_ret_traces_fn_val = fn_val; + return fn_val; + +} +static LLVMValueRef ir_render_save_err_ret_addr(CodeGen *g, IrExecutableGen *executable, + IrInstGenSaveErrRetAddr *save_err_ret_addr_instruction) +{ + assert(g->have_err_ret_tracing); + + LLVMValueRef return_err_fn = get_return_err_fn(g); + bool is_llvm_alloca; + LLVMValueRef my_err_trace_val = get_cur_err_ret_trace_val(g, save_err_ret_addr_instruction->base.base.scope, + &is_llvm_alloca); + ZigLLVMBuildCall(g->builder, return_err_fn, &my_err_trace_val, 1, + get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_CallAttrAuto, ""); + + ZigType *ret_type = g->cur_fn->type_entry->data.fn.fn_type_id.return_type; + if (fn_is_async(g->cur_fn) && codegen_fn_has_err_ret_tracing_arg(g, ret_type)) { + LLVMValueRef trace_ptr_ptr = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr, + frame_index_trace_arg(g, ret_type), ""); + LLVMBuildStore(g->builder, my_err_trace_val, trace_ptr_ptr); + } + + return nullptr; +} + +static void gen_assert_resume_id(CodeGen *g, IrInstGen *source_instr, ResumeId resume_id, PanicMsgId msg_id, + LLVMBasicBlockRef end_bb) +{ + LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; + + if (ir_want_runtime_safety(g, source_instr)) { + // Write a value to the resume index which indicates the function was resumed while not suspended. + LLVMBuildStore(g->builder, g->cur_bad_not_suspended_index, g->cur_async_resume_index_ptr); + } + + LLVMBasicBlockRef bad_resume_block = LLVMAppendBasicBlock(g->cur_fn_val, "BadResume"); + if (end_bb == nullptr) end_bb = LLVMAppendBasicBlock(g->cur_fn_val, "OkResume"); + LLVMValueRef expected_value = LLVMConstSub(LLVMConstAllOnes(usize_type_ref), + LLVMConstInt(usize_type_ref, resume_id, false)); + LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, LLVMGetParam(g->cur_fn_val, 1), expected_value, ""); + LLVMBuildCondBr(g->builder, ok_bit, end_bb, bad_resume_block); + + LLVMPositionBuilderAtEnd(g->builder, bad_resume_block); + gen_assertion(g, msg_id, source_instr); + + LLVMPositionBuilderAtEnd(g->builder, end_bb); +} + +static LLVMValueRef gen_resume(CodeGen *g, LLVMValueRef fn_val, LLVMValueRef target_frame_ptr, ResumeId resume_id) { + LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; + if (fn_val == nullptr) { + LLVMValueRef fn_ptr_ptr = LLVMBuildStructGEP(g->builder, target_frame_ptr, frame_fn_ptr_index, ""); + fn_val = LLVMBuildLoad(g->builder, fn_ptr_ptr, ""); + } + LLVMValueRef arg_val = LLVMConstSub(LLVMConstAllOnes(usize_type_ref), + LLVMConstInt(usize_type_ref, resume_id, false)); + LLVMValueRef args[] = {target_frame_ptr, arg_val}; + return ZigLLVMBuildCall(g->builder, fn_val, args, 2, ZigLLVM_Fast, ZigLLVM_CallAttrAuto, ""); +} + +static LLVMBasicBlockRef gen_suspend_begin(CodeGen *g, const char *name_hint) { + LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; + LLVMBasicBlockRef resume_bb = LLVMAppendBasicBlock(g->cur_fn_val, name_hint); + size_t new_block_index = g->cur_resume_block_count; + g->cur_resume_block_count += 1; + LLVMValueRef new_block_index_val = LLVMConstInt(usize_type_ref, new_block_index, false); + LLVMAddCase(g->cur_async_switch_instr, new_block_index_val, resume_bb); + LLVMBuildStore(g->builder, new_block_index_val, g->cur_async_resume_index_ptr); + return resume_bb; +} + +// Be careful setting tail call. According to LLVM lang ref, +// tail and musttail imply that the callee does not access allocas from the caller. +// This works for async functions since the locals are spilled. +// http://llvm.org/docs/LangRef.html#id320 +static void set_tail_call_if_appropriate(CodeGen *g, LLVMValueRef call_inst) { + LLVMSetTailCall(call_inst, true); +} + +static LLVMValueRef gen_maybe_atomic_op(CodeGen *g, LLVMAtomicRMWBinOp op, LLVMValueRef ptr, LLVMValueRef val, + LLVMAtomicOrdering order) +{ + if (g->is_single_threaded) { + LLVMValueRef loaded = LLVMBuildLoad(g->builder, ptr, ""); + LLVMValueRef modified; + switch (op) { + case LLVMAtomicRMWBinOpXchg: + modified = val; + break; + case LLVMAtomicRMWBinOpXor: + modified = LLVMBuildXor(g->builder, loaded, val, ""); + break; + default: + zig_unreachable(); + } + LLVMBuildStore(g->builder, modified, ptr); + return loaded; + } else { + return LLVMBuildAtomicRMW(g->builder, op, ptr, val, order, false); + } +} + +static void gen_async_return(CodeGen *g, IrInstGenReturn *instruction) { + LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; + + ZigType *operand_type = (instruction->operand != nullptr) ? instruction->operand->value->type : nullptr; + bool operand_has_bits = (operand_type != nullptr) && type_has_bits(g, operand_type); + ZigType *ret_type = g->cur_fn->type_entry->data.fn.fn_type_id.return_type; + bool ret_type_has_bits = type_has_bits(g, ret_type); + + if (operand_has_bits && instruction->operand != nullptr) { + bool need_store = instruction->operand->value->special != ConstValSpecialRuntime || !handle_is_ptr(g, ret_type); + if (need_store) { + // It didn't get written to the result ptr. We do that now. + ZigType *ret_ptr_type = get_pointer_to_type(g, ret_type, true); + gen_assign_raw(g, g->cur_ret_ptr, ret_ptr_type, ir_llvm_value(g, instruction->operand)); + } + } + + // Whether we tail resume the awaiter, or do an early return, we are done and will not be resumed. + if (ir_want_runtime_safety(g, &instruction->base)) { + LLVMValueRef new_resume_index = LLVMConstAllOnes(usize_type_ref); + LLVMBuildStore(g->builder, new_resume_index, g->cur_async_resume_index_ptr); + } + + LLVMValueRef zero = LLVMConstNull(usize_type_ref); + LLVMValueRef all_ones = LLVMConstAllOnes(usize_type_ref); + + LLVMValueRef prev_val = gen_maybe_atomic_op(g, LLVMAtomicRMWBinOpXor, g->cur_async_awaiter_ptr, + all_ones, LLVMAtomicOrderingAcquire); + + LLVMBasicBlockRef bad_return_block = LLVMAppendBasicBlock(g->cur_fn_val, "BadReturn"); + LLVMBasicBlockRef early_return_block = LLVMAppendBasicBlock(g->cur_fn_val, "EarlyReturn"); + LLVMBasicBlockRef resume_them_block = LLVMAppendBasicBlock(g->cur_fn_val, "ResumeThem"); + + LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, prev_val, resume_them_block, 2); + + LLVMAddCase(switch_instr, zero, early_return_block); + LLVMAddCase(switch_instr, all_ones, bad_return_block); + + // Something has gone horribly wrong, and this is an invalid second return. + LLVMPositionBuilderAtEnd(g->builder, bad_return_block); + gen_assertion(g, PanicMsgIdBadReturn, &instruction->base); + + // There is no awaiter yet, but we're completely done. + LLVMPositionBuilderAtEnd(g->builder, early_return_block); + LLVMBuildRetVoid(g->builder); + + // We need to resume the caller by tail calling them, + // but first write through the result pointer and possibly + // error return trace pointer. + LLVMPositionBuilderAtEnd(g->builder, resume_them_block); + + if (ret_type_has_bits) { + // If the awaiter result pointer is non-null, we need to copy the result to there. + LLVMBasicBlockRef copy_block = LLVMAppendBasicBlock(g->cur_fn_val, "CopyResult"); + LLVMBasicBlockRef copy_end_block = LLVMAppendBasicBlock(g->cur_fn_val, "CopyResultEnd"); + LLVMValueRef awaiter_ret_ptr_ptr = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr, frame_ret_start + 1, ""); + LLVMValueRef awaiter_ret_ptr = LLVMBuildLoad(g->builder, awaiter_ret_ptr_ptr, ""); + LLVMValueRef zero_ptr = LLVMConstNull(LLVMTypeOf(awaiter_ret_ptr)); + LLVMValueRef need_copy_bit = LLVMBuildICmp(g->builder, LLVMIntNE, awaiter_ret_ptr, zero_ptr, ""); + LLVMBuildCondBr(g->builder, need_copy_bit, copy_block, copy_end_block); + + LLVMPositionBuilderAtEnd(g->builder, copy_block); + LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0); + LLVMValueRef dest_ptr_casted = LLVMBuildBitCast(g->builder, awaiter_ret_ptr, ptr_u8, ""); + LLVMValueRef src_ptr_casted = LLVMBuildBitCast(g->builder, g->cur_ret_ptr, ptr_u8, ""); + bool is_volatile = false; + uint32_t abi_align = get_abi_alignment(g, ret_type); + LLVMValueRef byte_count_val = LLVMConstInt(usize_type_ref, type_size(g, ret_type), false); + ZigLLVMBuildMemCpy(g->builder, + dest_ptr_casted, abi_align, + src_ptr_casted, abi_align, byte_count_val, is_volatile); + LLVMBuildBr(g->builder, copy_end_block); + + LLVMPositionBuilderAtEnd(g->builder, copy_end_block); + if (codegen_fn_has_err_ret_tracing_arg(g, ret_type)) { + LLVMValueRef awaiter_trace_ptr_ptr = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr, + frame_index_trace_arg(g, ret_type) + 1, ""); + LLVMValueRef dest_trace_ptr = LLVMBuildLoad(g->builder, awaiter_trace_ptr_ptr, ""); + bool is_llvm_alloca; + LLVMValueRef my_err_trace_val = get_cur_err_ret_trace_val(g, instruction->base.base.scope, &is_llvm_alloca); + LLVMValueRef args[] = { dest_trace_ptr, my_err_trace_val }; + ZigLLVMBuildCall(g->builder, get_merge_err_ret_traces_fn_val(g), args, 2, + get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_CallAttrAuto, ""); + } + } + + // Resume the caller by tail calling them. + ZigType *any_frame_type = get_any_frame_type(g, ret_type); + LLVMValueRef their_frame_ptr = LLVMBuildIntToPtr(g->builder, prev_val, get_llvm_type(g, any_frame_type), ""); + LLVMValueRef call_inst = gen_resume(g, nullptr, their_frame_ptr, ResumeIdReturn); + set_tail_call_if_appropriate(g, call_inst); + LLVMBuildRetVoid(g->builder); +} + +static LLVMValueRef ir_render_return(CodeGen *g, IrExecutableGen *executable, IrInstGenReturn *instruction) { + if (fn_is_async(g->cur_fn)) { + gen_async_return(g, instruction); + return nullptr; + } + + if (want_first_arg_sret(g, &g->cur_fn->type_entry->data.fn.fn_type_id)) { + if (instruction->operand == nullptr) { + LLVMBuildRetVoid(g->builder); + return nullptr; + } + assert(g->cur_ret_ptr); + ir_assert(instruction->operand->value->special != ConstValSpecialRuntime, &instruction->base); + LLVMValueRef value = ir_llvm_value(g, instruction->operand); + ZigType *return_type = instruction->operand->value->type; + gen_assign_raw(g, g->cur_ret_ptr, get_pointer_to_type(g, return_type, false), value); + LLVMBuildRetVoid(g->builder); + } else if (g->cur_fn->type_entry->data.fn.fn_type_id.cc != CallingConventionAsync && + handle_is_ptr(g, g->cur_fn->type_entry->data.fn.fn_type_id.return_type)) + { + if (instruction->operand == nullptr) { + LLVMValueRef by_val_value = gen_load_untyped(g, g->cur_ret_ptr, 0, false, ""); + LLVMBuildRet(g->builder, by_val_value); + } else { + LLVMValueRef value = ir_llvm_value(g, instruction->operand); + LLVMValueRef by_val_value = gen_load_untyped(g, value, 0, false, ""); + LLVMBuildRet(g->builder, by_val_value); + } + } else if (instruction->operand == nullptr) { + if (g->cur_ret_ptr == nullptr) { + LLVMBuildRetVoid(g->builder); + } else { + LLVMValueRef by_val_value = gen_load_untyped(g, g->cur_ret_ptr, 0, false, ""); + LLVMBuildRet(g->builder, by_val_value); + } + } else { + LLVMValueRef value = ir_llvm_value(g, instruction->operand); + LLVMBuildRet(g->builder, value); + } + return nullptr; +} + +enum class ScalarizePredicate { + // Returns true iff all the elements in the vector are 1. + // Equivalent to folding all the bits with `and`. + All, + // Returns true iff there's at least one element in the vector that is 1. + // Equivalent to folding all the bits with `or`. + Any, +}; + +// Collapses a vector into a single i1 according to the given predicate +static LLVMValueRef scalarize_cmp_result(CodeGen *g, LLVMValueRef val, ScalarizePredicate predicate) { + assert(LLVMGetTypeKind(LLVMTypeOf(val)) == LLVMVectorTypeKind); + LLVMTypeRef scalar_type = LLVMIntType(LLVMGetVectorSize(LLVMTypeOf(val))); + LLVMValueRef casted = LLVMBuildBitCast(g->builder, val, scalar_type, ""); + + switch (predicate) { + case ScalarizePredicate::Any: { + LLVMValueRef all_zeros = LLVMConstNull(scalar_type); + return LLVMBuildICmp(g->builder, LLVMIntNE, casted, all_zeros, ""); + } + case ScalarizePredicate::All: { + LLVMValueRef all_ones = LLVMConstAllOnes(scalar_type); + return LLVMBuildICmp(g->builder, LLVMIntEQ, casted, all_ones, ""); + } + } + + zig_unreachable(); +} + + +static LLVMValueRef gen_overflow_shl_op(CodeGen *g, ZigType *operand_type, + LLVMValueRef val1, LLVMValueRef val2) +{ + // for unsigned left shifting, we do the lossy shift, then logically shift + // right the same number of bits + // if the values don't match, we have an overflow + // for signed left shifting we do the same except arithmetic shift right + ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? + operand_type->data.vector.elem_type : operand_type; + + assert(scalar_type->id == ZigTypeIdInt); + + LLVMValueRef result = LLVMBuildShl(g->builder, val1, val2, ""); + LLVMValueRef orig_val; + if (scalar_type->data.integral.is_signed) { + orig_val = LLVMBuildAShr(g->builder, result, val2, ""); + } else { + orig_val = LLVMBuildLShr(g->builder, result, val2, ""); + } + LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, val1, orig_val, ""); + + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowOk"); + LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowFail"); + if (operand_type->id == ZigTypeIdVector) { + ok_bit = scalarize_cmp_result(g, ok_bit, ScalarizePredicate::All); + } + LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); + + LLVMPositionBuilderAtEnd(g->builder, fail_block); + gen_safety_crash(g, PanicMsgIdShlOverflowedBits); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + return result; +} + +static LLVMValueRef gen_overflow_shr_op(CodeGen *g, ZigType *operand_type, + LLVMValueRef val1, LLVMValueRef val2) +{ + ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? + operand_type->data.vector.elem_type : operand_type; + + assert(scalar_type->id == ZigTypeIdInt); + + LLVMValueRef result; + if (scalar_type->data.integral.is_signed) { + result = LLVMBuildAShr(g->builder, val1, val2, ""); + } else { + result = LLVMBuildLShr(g->builder, val1, val2, ""); + } + LLVMValueRef orig_val = LLVMBuildShl(g->builder, result, val2, ""); + LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, val1, orig_val, ""); + + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowOk"); + LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "OverflowFail"); + if (operand_type->id == ZigTypeIdVector) { + ok_bit = scalarize_cmp_result(g, ok_bit, ScalarizePredicate::All); + } + LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); + + LLVMPositionBuilderAtEnd(g->builder, fail_block); + gen_safety_crash(g, PanicMsgIdShrOverflowedBits); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + return result; +} + +static LLVMValueRef gen_float_op(CodeGen *g, LLVMValueRef val, ZigType *type_entry, BuiltinFnId op) { + assert(type_entry->id == ZigTypeIdFloat || type_entry->id == ZigTypeIdVector); + LLVMValueRef floor_fn = get_float_fn(g, type_entry, ZigLLVMFnIdFloatOp, op); + return LLVMBuildCall(g->builder, floor_fn, &val, 1, ""); +} + +enum DivKind { + DivKindFloat, + DivKindTrunc, + DivKindFloor, + DivKindExact, +}; + +static LLVMValueRef bigint_to_llvm_const(LLVMTypeRef type_ref, BigInt *bigint) { + if (bigint->digit_count == 0) { + return LLVMConstNull(type_ref); + } + + if (LLVMGetTypeKind(type_ref) == LLVMVectorTypeKind) { + const unsigned vector_len = LLVMGetVectorSize(type_ref); + LLVMTypeRef elem_type = LLVMGetElementType(type_ref); + + LLVMValueRef *values = heap::c_allocator.allocate_nonzero(vector_len); + // Create a vector with all the elements having the same value + for (unsigned i = 0; i < vector_len; i++) { + values[i] = bigint_to_llvm_const(elem_type, bigint); + } + LLVMValueRef result = LLVMConstVector(values, vector_len); + heap::c_allocator.deallocate(values, vector_len); + return result; + } + + LLVMValueRef unsigned_val; + if (bigint->digit_count == 1) { + unsigned_val = LLVMConstInt(type_ref, bigint_ptr(bigint)[0], false); + } else { + unsigned_val = LLVMConstIntOfArbitraryPrecision(type_ref, bigint->digit_count, bigint_ptr(bigint)); + } + if (bigint->is_negative) { + return LLVMConstNeg(unsigned_val); + } else { + return unsigned_val; + } +} + +static LLVMValueRef gen_div(CodeGen *g, bool want_runtime_safety, bool want_fast_math, + LLVMValueRef val1, LLVMValueRef val2, ZigType *operand_type, DivKind div_kind) +{ + ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? + operand_type->data.vector.elem_type : operand_type; + + ZigLLVMSetFastMath(g->builder, want_fast_math); + + LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, operand_type)); + if (want_runtime_safety && (want_fast_math || scalar_type->id != ZigTypeIdFloat)) { + // Safety check: divisor != 0 + LLVMValueRef is_zero_bit; + if (scalar_type->id == ZigTypeIdInt) { + is_zero_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, zero, ""); + } else if (scalar_type->id == ZigTypeIdFloat) { + is_zero_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, val2, zero, ""); + } else { + zig_unreachable(); + } + + if (operand_type->id == ZigTypeIdVector) { + is_zero_bit = scalarize_cmp_result(g, is_zero_bit, ScalarizePredicate::Any); + } + + LLVMBasicBlockRef div_zero_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivZeroFail"); + LLVMBasicBlockRef div_zero_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivZeroOk"); + LLVMBuildCondBr(g->builder, is_zero_bit, div_zero_fail_block, div_zero_ok_block); + + LLVMPositionBuilderAtEnd(g->builder, div_zero_fail_block); + gen_safety_crash(g, PanicMsgIdDivisionByZero); + + LLVMPositionBuilderAtEnd(g->builder, div_zero_ok_block); + + // Safety check: check for overflow (dividend = minInt and divisor = -1) + if (scalar_type->id == ZigTypeIdInt && scalar_type->data.integral.is_signed) { + LLVMValueRef neg_1_value = LLVMConstAllOnes(get_llvm_type(g, operand_type)); + BigInt int_min_bi = {0}; + eval_min_max_value_int(g, scalar_type, &int_min_bi, false); + LLVMValueRef int_min_value = bigint_to_llvm_const(get_llvm_type(g, operand_type), &int_min_bi); + + LLVMBasicBlockRef overflow_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivOverflowFail"); + LLVMBasicBlockRef overflow_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivOverflowOk"); + LLVMValueRef num_is_int_min = LLVMBuildICmp(g->builder, LLVMIntEQ, val1, int_min_value, ""); + LLVMValueRef den_is_neg_1 = LLVMBuildICmp(g->builder, LLVMIntEQ, val2, neg_1_value, ""); + LLVMValueRef overflow_fail_bit = LLVMBuildAnd(g->builder, num_is_int_min, den_is_neg_1, ""); + if (operand_type->id == ZigTypeIdVector) { + overflow_fail_bit = scalarize_cmp_result(g, overflow_fail_bit, ScalarizePredicate::Any); + } + LLVMBuildCondBr(g->builder, overflow_fail_bit, overflow_fail_block, overflow_ok_block); + + LLVMPositionBuilderAtEnd(g->builder, overflow_fail_block); + gen_safety_crash(g, PanicMsgIdIntegerOverflow); + + LLVMPositionBuilderAtEnd(g->builder, overflow_ok_block); + } + } + + if (scalar_type->id == ZigTypeIdFloat) { + LLVMValueRef result = LLVMBuildFDiv(g->builder, val1, val2, ""); + switch (div_kind) { + case DivKindFloat: + return result; + case DivKindExact: + if (want_runtime_safety) { + // Safety check: a / b == floor(a / b) + LLVMValueRef floored = gen_float_op(g, result, operand_type, BuiltinFnIdFloor); + + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk"); + LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail"); + LLVMValueRef ok_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, floored, result, ""); + if (operand_type->id == ZigTypeIdVector) { + ok_bit = scalarize_cmp_result(g, ok_bit, ScalarizePredicate::All); + } + LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); + + LLVMPositionBuilderAtEnd(g->builder, fail_block); + gen_safety_crash(g, PanicMsgIdExactDivisionRemainder); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + } + return result; + case DivKindTrunc: + { + LLVMBasicBlockRef ltz_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivTruncLTZero"); + LLVMBasicBlockRef gez_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivTruncGEZero"); + LLVMBasicBlockRef end_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivTruncEnd"); + LLVMValueRef ltz = LLVMBuildFCmp(g->builder, LLVMRealOLT, val1, zero, ""); + if (operand_type->id == ZigTypeIdVector) { + ltz = scalarize_cmp_result(g, ltz, ScalarizePredicate::Any); + } + LLVMBuildCondBr(g->builder, ltz, ltz_block, gez_block); + + LLVMPositionBuilderAtEnd(g->builder, ltz_block); + LLVMValueRef ceiled = gen_float_op(g, result, operand_type, BuiltinFnIdCeil); + LLVMBasicBlockRef ceiled_end_block = LLVMGetInsertBlock(g->builder); + LLVMBuildBr(g->builder, end_block); + + LLVMPositionBuilderAtEnd(g->builder, gez_block); + LLVMValueRef floored = gen_float_op(g, result, operand_type, BuiltinFnIdFloor); + LLVMBasicBlockRef floored_end_block = LLVMGetInsertBlock(g->builder); + LLVMBuildBr(g->builder, end_block); + + LLVMPositionBuilderAtEnd(g->builder, end_block); + LLVMValueRef phi = LLVMBuildPhi(g->builder, get_llvm_type(g, operand_type), ""); + LLVMValueRef incoming_values[] = { ceiled, floored }; + LLVMBasicBlockRef incoming_blocks[] = { ceiled_end_block, floored_end_block }; + LLVMAddIncoming(phi, incoming_values, incoming_blocks, 2); + return phi; + } + case DivKindFloor: + return gen_float_op(g, result, operand_type, BuiltinFnIdFloor); + } + zig_unreachable(); + } + + assert(scalar_type->id == ZigTypeIdInt); + + switch (div_kind) { + case DivKindFloat: + zig_unreachable(); + case DivKindTrunc: + if (scalar_type->data.integral.is_signed) { + return LLVMBuildSDiv(g->builder, val1, val2, ""); + } else { + return LLVMBuildUDiv(g->builder, val1, val2, ""); + } + case DivKindExact: + if (want_runtime_safety) { + // Safety check: a % b == 0 + LLVMValueRef remainder_val; + if (scalar_type->data.integral.is_signed) { + remainder_val = LLVMBuildSRem(g->builder, val1, val2, ""); + } else { + remainder_val = LLVMBuildURem(g->builder, val1, val2, ""); + } + + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactOk"); + LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "DivExactFail"); + LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, remainder_val, zero, ""); + if (operand_type->id == ZigTypeIdVector) { + ok_bit = scalarize_cmp_result(g, ok_bit, ScalarizePredicate::All); + } + LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); + + LLVMPositionBuilderAtEnd(g->builder, fail_block); + gen_safety_crash(g, PanicMsgIdExactDivisionRemainder); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + } + if (scalar_type->data.integral.is_signed) { + return LLVMBuildExactSDiv(g->builder, val1, val2, ""); + } else { + return LLVMBuildExactUDiv(g->builder, val1, val2, ""); + } + case DivKindFloor: + { + if (!scalar_type->data.integral.is_signed) { + return LLVMBuildUDiv(g->builder, val1, val2, ""); + } + // const d = @divTrunc(a, b); + // const r = @rem(a, b); + // return if (r == 0) d else d - ((a < 0) ^ (b < 0)); + + LLVMValueRef div_trunc = LLVMBuildSDiv(g->builder, val1, val2, ""); + LLVMValueRef rem = LLVMBuildSRem(g->builder, val1, val2, ""); + LLVMValueRef rem_eq_0 = LLVMBuildICmp(g->builder, LLVMIntEQ, rem, zero, ""); + LLVMValueRef a_lt_0 = LLVMBuildICmp(g->builder, LLVMIntSLT, val1, zero, ""); + LLVMValueRef b_lt_0 = LLVMBuildICmp(g->builder, LLVMIntSLT, val2, zero, ""); + LLVMValueRef a_b_xor = LLVMBuildXor(g->builder, a_lt_0, b_lt_0, ""); + LLVMValueRef a_b_xor_ext = LLVMBuildZExt(g->builder, a_b_xor, LLVMTypeOf(div_trunc), ""); + LLVMValueRef d_sub_xor = LLVMBuildSub(g->builder, div_trunc, a_b_xor_ext, ""); + return LLVMBuildSelect(g->builder, rem_eq_0, div_trunc, d_sub_xor, ""); + } + } + zig_unreachable(); +} + +enum RemKind { + RemKindRem, + RemKindMod, +}; + +static LLVMValueRef gen_rem(CodeGen *g, bool want_runtime_safety, bool want_fast_math, + LLVMValueRef val1, LLVMValueRef val2, ZigType *operand_type, RemKind rem_kind) +{ + ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? + operand_type->data.vector.elem_type : operand_type; + + ZigLLVMSetFastMath(g->builder, want_fast_math); + + LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, operand_type)); + if (want_runtime_safety) { + // Safety check: divisor != 0 + LLVMValueRef is_zero_bit; + if (scalar_type->id == ZigTypeIdInt) { + LLVMIntPredicate pred = scalar_type->data.integral.is_signed ? LLVMIntSLE : LLVMIntEQ; + is_zero_bit = LLVMBuildICmp(g->builder, pred, val2, zero, ""); + } else if (scalar_type->id == ZigTypeIdFloat) { + is_zero_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, val2, zero, ""); + } else { + zig_unreachable(); + } + + if (operand_type->id == ZigTypeIdVector) { + is_zero_bit = scalarize_cmp_result(g, is_zero_bit, ScalarizePredicate::Any); + } + + LLVMBasicBlockRef rem_zero_ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "RemZeroOk"); + LLVMBasicBlockRef rem_zero_fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "RemZeroFail"); + LLVMBuildCondBr(g->builder, is_zero_bit, rem_zero_fail_block, rem_zero_ok_block); + + LLVMPositionBuilderAtEnd(g->builder, rem_zero_fail_block); + gen_safety_crash(g, PanicMsgIdRemainderDivisionByZero); + + LLVMPositionBuilderAtEnd(g->builder, rem_zero_ok_block); + } + + if (scalar_type->id == ZigTypeIdFloat) { + if (rem_kind == RemKindRem) { + return LLVMBuildFRem(g->builder, val1, val2, ""); + } else { + LLVMValueRef a = LLVMBuildFRem(g->builder, val1, val2, ""); + LLVMValueRef b = LLVMBuildFAdd(g->builder, a, val2, ""); + LLVMValueRef c = LLVMBuildFRem(g->builder, b, val2, ""); + LLVMValueRef ltz = LLVMBuildFCmp(g->builder, LLVMRealOLT, val1, zero, ""); + return LLVMBuildSelect(g->builder, ltz, c, a, ""); + } + } else { + assert(scalar_type->id == ZigTypeIdInt); + if (scalar_type->data.integral.is_signed) { + if (rem_kind == RemKindRem) { + return LLVMBuildSRem(g->builder, val1, val2, ""); + } else { + LLVMValueRef a = LLVMBuildSRem(g->builder, val1, val2, ""); + LLVMValueRef b = LLVMBuildNSWAdd(g->builder, a, val2, ""); + LLVMValueRef c = LLVMBuildSRem(g->builder, b, val2, ""); + LLVMValueRef ltz = LLVMBuildICmp(g->builder, LLVMIntSLT, val1, zero, ""); + return LLVMBuildSelect(g->builder, ltz, c, a, ""); + } + } else { + return LLVMBuildURem(g->builder, val1, val2, ""); + } + } + +} + +static void gen_shift_rhs_check(CodeGen *g, ZigType *lhs_type, ZigType *rhs_type, LLVMValueRef value) { + // We only check if the rhs value of the shift expression is greater or + // equal to the number of bits of the lhs if it's not a power of two, + // otherwise the check is useful as the allowed values are limited by the + // operand type itself + if (!is_power_of_2(lhs_type->data.integral.bit_count)) { + BigInt bit_count_bi = {0}; + bigint_init_unsigned(&bit_count_bi, lhs_type->data.integral.bit_count); + LLVMValueRef bit_count_value = bigint_to_llvm_const(get_llvm_type(g, rhs_type), + &bit_count_bi); + + LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "CheckFail"); + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "CheckOk"); + LLVMValueRef less_than_bit = LLVMBuildICmp(g->builder, LLVMIntULT, value, bit_count_value, ""); + if (rhs_type->id == ZigTypeIdVector) { + less_than_bit = scalarize_cmp_result(g, less_than_bit, ScalarizePredicate::Any); + } + LLVMBuildCondBr(g->builder, less_than_bit, ok_block, fail_block); + + LLVMPositionBuilderAtEnd(g->builder, fail_block); + gen_safety_crash(g, PanicMsgIdShxTooBigRhs); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + } +} + +static LLVMValueRef ir_render_bin_op(CodeGen *g, IrExecutableGen *executable, + IrInstGenBinOp *bin_op_instruction) +{ + IrBinOp op_id = bin_op_instruction->op_id; + IrInstGen *op1 = bin_op_instruction->op1; + IrInstGen *op2 = bin_op_instruction->op2; + + ZigType *operand_type = op1->value->type; + ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? operand_type->data.vector.elem_type : operand_type; + + bool want_runtime_safety = bin_op_instruction->safety_check_on && + ir_want_runtime_safety(g, &bin_op_instruction->base); + + LLVMValueRef op1_value = ir_llvm_value(g, op1); + LLVMValueRef op2_value = ir_llvm_value(g, op2); + + + switch (op_id) { + case IrBinOpInvalid: + case IrBinOpArrayCat: + case IrBinOpArrayMult: + case IrBinOpRemUnspecified: + zig_unreachable(); + case IrBinOpBoolOr: + return LLVMBuildOr(g->builder, op1_value, op2_value, ""); + case IrBinOpBoolAnd: + return LLVMBuildAnd(g->builder, op1_value, op2_value, ""); + case IrBinOpCmpEq: + case IrBinOpCmpNotEq: + case IrBinOpCmpLessThan: + case IrBinOpCmpGreaterThan: + case IrBinOpCmpLessOrEq: + case IrBinOpCmpGreaterOrEq: + if (scalar_type->id == ZigTypeIdFloat) { + ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base)); + LLVMRealPredicate pred = cmp_op_to_real_predicate(op_id); + return LLVMBuildFCmp(g->builder, pred, op1_value, op2_value, ""); + } else if (scalar_type->id == ZigTypeIdInt) { + LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, scalar_type->data.integral.is_signed); + return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, ""); + } else if (scalar_type->id == ZigTypeIdEnum || + scalar_type->id == ZigTypeIdErrorSet || + scalar_type->id == ZigTypeIdBool || + get_codegen_ptr_type_bail(g, scalar_type) != nullptr) + { + LLVMIntPredicate pred = cmp_op_to_int_predicate(op_id, false); + return LLVMBuildICmp(g->builder, pred, op1_value, op2_value, ""); + } else { + zig_unreachable(); + } + case IrBinOpMult: + case IrBinOpMultWrap: + case IrBinOpAdd: + case IrBinOpAddWrap: + case IrBinOpSub: + case IrBinOpSubWrap: { + bool is_wrapping = (op_id == IrBinOpSubWrap || op_id == IrBinOpAddWrap || op_id == IrBinOpMultWrap); + AddSubMul add_sub_mul = + op_id == IrBinOpAdd || op_id == IrBinOpAddWrap ? AddSubMulAdd : + op_id == IrBinOpSub || op_id == IrBinOpSubWrap ? AddSubMulSub : + AddSubMulMul; + + if (scalar_type->id == ZigTypeIdPointer) { + LLVMValueRef subscript_value; + if (operand_type->id == ZigTypeIdVector) + zig_panic("TODO: Implement vector operations on pointers."); + + switch (add_sub_mul) { + case AddSubMulAdd: + subscript_value = op2_value; + break; + case AddSubMulSub: + subscript_value = LLVMBuildNeg(g->builder, op2_value, ""); + break; + case AddSubMulMul: + zig_unreachable(); + } + + // TODO runtime safety + return LLVMBuildInBoundsGEP(g->builder, op1_value, &subscript_value, 1, ""); + } else if (scalar_type->id == ZigTypeIdFloat) { + ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &bin_op_instruction->base)); + return float_op[add_sub_mul](g->builder, op1_value, op2_value, ""); + } else if (scalar_type->id == ZigTypeIdInt) { + if (is_wrapping) { + return wrap_op[add_sub_mul](g->builder, op1_value, op2_value, ""); + } else if (want_runtime_safety) { + return gen_overflow_op(g, operand_type, add_sub_mul, op1_value, op2_value); + } else if (scalar_type->data.integral.is_signed) { + return signed_op[add_sub_mul](g->builder, op1_value, op2_value, ""); + } else { + return unsigned_op[add_sub_mul](g->builder, op1_value, op2_value, ""); + } + } else { + zig_unreachable(); + } + } + case IrBinOpBinOr: + return LLVMBuildOr(g->builder, op1_value, op2_value, ""); + case IrBinOpBinXor: + return LLVMBuildXor(g->builder, op1_value, op2_value, ""); + case IrBinOpBinAnd: + return LLVMBuildAnd(g->builder, op1_value, op2_value, ""); + case IrBinOpBitShiftLeftLossy: + case IrBinOpBitShiftLeftExact: + { + assert(scalar_type->id == ZigTypeIdInt); + LLVMValueRef op2_casted = LLVMBuildZExt(g->builder, op2_value, + LLVMTypeOf(op1_value), ""); + + if (want_runtime_safety) { + gen_shift_rhs_check(g, scalar_type, op2->value->type, op2_value); + } + + bool is_sloppy = (op_id == IrBinOpBitShiftLeftLossy); + if (is_sloppy) { + return LLVMBuildShl(g->builder, op1_value, op2_casted, ""); + } else if (want_runtime_safety) { + return gen_overflow_shl_op(g, operand_type, op1_value, op2_casted); + } else if (scalar_type->data.integral.is_signed) { + return ZigLLVMBuildNSWShl(g->builder, op1_value, op2_casted, ""); + } else { + return ZigLLVMBuildNUWShl(g->builder, op1_value, op2_casted, ""); + } + } + case IrBinOpBitShiftRightLossy: + case IrBinOpBitShiftRightExact: + { + assert(scalar_type->id == ZigTypeIdInt); + LLVMValueRef op2_casted = LLVMBuildZExt(g->builder, op2_value, + LLVMTypeOf(op1_value), ""); + + if (want_runtime_safety) { + gen_shift_rhs_check(g, scalar_type, op2->value->type, op2_value); + } + + bool is_sloppy = (op_id == IrBinOpBitShiftRightLossy); + if (is_sloppy) { + if (scalar_type->data.integral.is_signed) { + return LLVMBuildAShr(g->builder, op1_value, op2_casted, ""); + } else { + return LLVMBuildLShr(g->builder, op1_value, op2_casted, ""); + } + } else if (want_runtime_safety) { + return gen_overflow_shr_op(g, operand_type, op1_value, op2_casted); + } else if (scalar_type->data.integral.is_signed) { + return ZigLLVMBuildAShrExact(g->builder, op1_value, op2_casted, ""); + } else { + return ZigLLVMBuildLShrExact(g->builder, op1_value, op2_casted, ""); + } + } + case IrBinOpDivUnspecified: + return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), + op1_value, op2_value, operand_type, DivKindFloat); + case IrBinOpDivExact: + return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), + op1_value, op2_value, operand_type, DivKindExact); + case IrBinOpDivTrunc: + return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), + op1_value, op2_value, operand_type, DivKindTrunc); + case IrBinOpDivFloor: + return gen_div(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), + op1_value, op2_value, operand_type, DivKindFloor); + case IrBinOpRemRem: + return gen_rem(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), + op1_value, op2_value, operand_type, RemKindRem); + case IrBinOpRemMod: + return gen_rem(g, want_runtime_safety, ir_want_fast_math(g, &bin_op_instruction->base), + op1_value, op2_value, operand_type, RemKindMod); + } + zig_unreachable(); +} + +static void add_error_range_check(CodeGen *g, ZigType *err_set_type, ZigType *int_type, LLVMValueRef target_val) { + assert(err_set_type->id == ZigTypeIdErrorSet); + + if (type_is_global_error_set(err_set_type)) { + LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, int_type)); + LLVMValueRef neq_zero_bit = LLVMBuildICmp(g->builder, LLVMIntNE, target_val, zero, ""); + LLVMValueRef ok_bit; + + BigInt biggest_possible_err_val = {0}; + eval_min_max_value_int(g, int_type, &biggest_possible_err_val, true); + + if (bigint_fits_in_bits(&biggest_possible_err_val, 64, false) && + bigint_as_usize(&biggest_possible_err_val) < g->errors_by_index.length) + { + ok_bit = neq_zero_bit; + } else { + LLVMValueRef error_value_count = LLVMConstInt(get_llvm_type(g, int_type), g->errors_by_index.length, false); + LLVMValueRef in_bounds_bit = LLVMBuildICmp(g->builder, LLVMIntULT, target_val, error_value_count, ""); + ok_bit = LLVMBuildAnd(g->builder, neq_zero_bit, in_bounds_bit, ""); + } + + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "IntToErrOk"); + LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "IntToErrFail"); + + LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); + + LLVMPositionBuilderAtEnd(g->builder, fail_block); + gen_safety_crash(g, PanicMsgIdInvalidErrorCode); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + } else { + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "IntToErrOk"); + LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "IntToErrFail"); + + uint32_t err_count = err_set_type->data.error_set.err_count; + LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, target_val, fail_block, err_count); + for (uint32_t i = 0; i < err_count; i += 1) { + LLVMValueRef case_value = LLVMConstInt(get_llvm_type(g, g->err_tag_type), + err_set_type->data.error_set.errors[i]->value, false); + LLVMAddCase(switch_instr, case_value, ok_block); + } + + LLVMPositionBuilderAtEnd(g->builder, fail_block); + gen_safety_crash(g, PanicMsgIdInvalidErrorCode); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + } +} + +static LLVMValueRef ir_render_cast(CodeGen *g, IrExecutableGen *executable, + IrInstGenCast *cast_instruction) +{ + Error err; + ZigType *actual_type = cast_instruction->value->value->type; + ZigType *wanted_type = cast_instruction->base.value->type; + bool wanted_type_has_bits; + if ((err = type_has_bits2(g, wanted_type, &wanted_type_has_bits))) + codegen_report_errors_and_exit(g); + if (!wanted_type_has_bits) + return nullptr; + LLVMValueRef expr_val = ir_llvm_value(g, cast_instruction->value); + ir_assert(expr_val, &cast_instruction->base); + + switch (cast_instruction->cast_op) { + case CastOpNoCast: + case CastOpNumLitToConcrete: + zig_unreachable(); + case CastOpNoop: + if (actual_type->id == ZigTypeIdPointer && wanted_type->id == ZigTypeIdPointer && + actual_type->data.pointer.child_type->id == ZigTypeIdArray && + wanted_type->data.pointer.child_type->id == ZigTypeIdArray) + { + return LLVMBuildBitCast(g->builder, expr_val, get_llvm_type(g, wanted_type), ""); + } else { + return expr_val; + } + case CastOpIntToFloat: + assert(actual_type->id == ZigTypeIdInt); + if (actual_type->data.integral.is_signed) { + return LLVMBuildSIToFP(g->builder, expr_val, get_llvm_type(g, wanted_type), ""); + } else { + return LLVMBuildUIToFP(g->builder, expr_val, get_llvm_type(g, wanted_type), ""); + } + case CastOpFloatToInt: { + assert(wanted_type->id == ZigTypeIdInt); + ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, &cast_instruction->base)); + + bool want_safety = ir_want_runtime_safety(g, &cast_instruction->base); + + LLVMValueRef result; + if (wanted_type->data.integral.is_signed) { + result = LLVMBuildFPToSI(g->builder, expr_val, get_llvm_type(g, wanted_type), ""); + } else { + result = LLVMBuildFPToUI(g->builder, expr_val, get_llvm_type(g, wanted_type), ""); + } + + if (want_safety) { + LLVMValueRef back_to_float; + if (wanted_type->data.integral.is_signed) { + back_to_float = LLVMBuildSIToFP(g->builder, result, LLVMTypeOf(expr_val), ""); + } else { + back_to_float = LLVMBuildUIToFP(g->builder, result, LLVMTypeOf(expr_val), ""); + } + LLVMValueRef difference = LLVMBuildFSub(g->builder, expr_val, back_to_float, ""); + LLVMValueRef one_pos = LLVMConstReal(LLVMTypeOf(expr_val), 1.0f); + LLVMValueRef one_neg = LLVMConstReal(LLVMTypeOf(expr_val), -1.0f); + LLVMValueRef ok_bit_pos = LLVMBuildFCmp(g->builder, LLVMRealOLT, difference, one_pos, ""); + LLVMValueRef ok_bit_neg = LLVMBuildFCmp(g->builder, LLVMRealOGT, difference, one_neg, ""); + LLVMValueRef ok_bit = LLVMBuildAnd(g->builder, ok_bit_pos, ok_bit_neg, ""); + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "FloatCheckOk"); + LLVMBasicBlockRef bad_block = LLVMAppendBasicBlock(g->cur_fn_val, "FloatCheckFail"); + LLVMBuildCondBr(g->builder, ok_bit, ok_block, bad_block); + LLVMPositionBuilderAtEnd(g->builder, bad_block); + gen_safety_crash(g, PanicMsgIdFloatToInt); + LLVMPositionBuilderAtEnd(g->builder, ok_block); + } + return result; + } + case CastOpBoolToInt: + assert(wanted_type->id == ZigTypeIdInt); + assert(actual_type->id == ZigTypeIdBool); + return LLVMBuildZExt(g->builder, expr_val, get_llvm_type(g, wanted_type), ""); + case CastOpErrSet: + if (ir_want_runtime_safety(g, &cast_instruction->base)) { + add_error_range_check(g, wanted_type, g->err_tag_type, expr_val); + } + return expr_val; + case CastOpBitCast: + return LLVMBuildBitCast(g->builder, expr_val, get_llvm_type(g, wanted_type), ""); + } + zig_unreachable(); +} + +static LLVMValueRef ir_render_ptr_of_array_to_slice(CodeGen *g, IrExecutableGen *executable, + IrInstGenPtrOfArrayToSlice *instruction) +{ + ZigType *actual_type = instruction->operand->value->type; + ZigType *slice_type = instruction->base.value->type; + ZigType *slice_ptr_type = slice_type->data.structure.fields[slice_ptr_index]->type_entry; + size_t ptr_index = slice_type->data.structure.fields[slice_ptr_index]->gen_index; + size_t len_index = slice_type->data.structure.fields[slice_len_index]->gen_index; + + LLVMValueRef result_loc = ir_llvm_value(g, instruction->result_loc); + + assert(actual_type->id == ZigTypeIdPointer); + ZigType *array_type = actual_type->data.pointer.child_type; + assert(array_type->id == ZigTypeIdArray); + + if (type_has_bits(g, actual_type)) { + LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, result_loc, ptr_index, ""); + LLVMValueRef indices[] = { + LLVMConstNull(g->builtin_types.entry_usize->llvm_type), + LLVMConstInt(g->builtin_types.entry_usize->llvm_type, 0, false), + }; + LLVMValueRef expr_val = ir_llvm_value(g, instruction->operand); + LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, expr_val, indices, 2, ""); + gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); + } else if (ir_want_runtime_safety(g, &instruction->base)) { + LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, result_loc, ptr_index, ""); + gen_undef_init(g, slice_ptr_type->abi_align, slice_ptr_type, ptr_field_ptr); + } + + LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, result_loc, len_index, ""); + LLVMValueRef len_value = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, + array_type->data.array.len, false); + gen_store_untyped(g, len_value, len_field_ptr, 0, false); + + return result_loc; +} + +static LLVMValueRef ir_render_ptr_cast(CodeGen *g, IrExecutableGen *executable, + IrInstGenPtrCast *instruction) +{ + ZigType *wanted_type = instruction->base.value->type; + if (!type_has_bits(g, wanted_type)) { + return nullptr; + } + LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr); + LLVMValueRef result_ptr = LLVMBuildBitCast(g->builder, ptr, get_llvm_type(g, wanted_type), ""); + bool want_safety_check = instruction->safety_check_on && ir_want_runtime_safety(g, &instruction->base); + if (!want_safety_check || ptr_allows_addr_zero(wanted_type)) + return result_ptr; + + LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(result_ptr)); + LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntNE, result_ptr, zero, ""); + LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "PtrCastFail"); + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "PtrCastOk"); + LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); + + LLVMPositionBuilderAtEnd(g->builder, fail_block); + gen_safety_crash(g, PanicMsgIdPtrCastNull); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + return result_ptr; +} + +static LLVMValueRef ir_render_bit_cast(CodeGen *g, IrExecutableGen *executable, + IrInstGenBitCast *instruction) +{ + ZigType *wanted_type = instruction->base.value->type; + ZigType *actual_type = instruction->operand->value->type; + LLVMValueRef value = ir_llvm_value(g, instruction->operand); + + bool wanted_is_ptr = handle_is_ptr(g, wanted_type); + bool actual_is_ptr = handle_is_ptr(g, actual_type); + if (wanted_is_ptr == actual_is_ptr) { + // We either bitcast the value directly or bitcast the pointer which does a pointer cast + LLVMTypeRef wanted_type_ref = wanted_is_ptr ? + LLVMPointerType(get_llvm_type(g, wanted_type), 0) : get_llvm_type(g, wanted_type); + return LLVMBuildBitCast(g->builder, value, wanted_type_ref, ""); + } else if (actual_is_ptr) { + // A scalar is wanted but we got a pointer + LLVMTypeRef wanted_ptr_type_ref = LLVMPointerType(get_llvm_type(g, wanted_type), 0); + LLVMValueRef bitcasted_ptr = LLVMBuildBitCast(g->builder, value, wanted_ptr_type_ref, ""); + uint32_t alignment = get_abi_alignment(g, actual_type); + return gen_load_untyped(g, bitcasted_ptr, alignment, false, ""); + } else { + // A pointer is wanted but we got a scalar + assert(actual_type->id == ZigTypeIdPointer); + LLVMTypeRef wanted_ptr_type_ref = LLVMPointerType(get_llvm_type(g, wanted_type), 0); + return LLVMBuildBitCast(g->builder, value, wanted_ptr_type_ref, ""); + } +} + +static LLVMValueRef ir_render_widen_or_shorten(CodeGen *g, IrExecutableGen *executable, + IrInstGenWidenOrShorten *instruction) +{ + ZigType *actual_type = instruction->target->value->type; + // TODO instead of this logic, use the Noop instruction to change the type from + // enum_tag to the underlying int type + ZigType *int_type; + if (actual_type->id == ZigTypeIdEnum) { + int_type = actual_type->data.enumeration.tag_int_type; + } else { + int_type = actual_type; + } + LLVMValueRef target_val = ir_llvm_value(g, instruction->target); + return gen_widen_or_shorten(g, ir_want_runtime_safety(g, &instruction->base), int_type, + instruction->base.value->type, target_val); +} + +static LLVMValueRef ir_render_int_to_ptr(CodeGen *g, IrExecutableGen *executable, IrInstGenIntToPtr *instruction) { + ZigType *wanted_type = instruction->base.value->type; + LLVMValueRef target_val = ir_llvm_value(g, instruction->target); + const uint32_t align_bytes = get_ptr_align(g, wanted_type); + + if (ir_want_runtime_safety(g, &instruction->base) && align_bytes > 1) { + ZigType *usize = g->builtin_types.entry_usize; + LLVMValueRef zero = LLVMConstNull(usize->llvm_type); + + if (!ptr_allows_addr_zero(wanted_type)) { + LLVMValueRef is_zero_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, target_val, zero, ""); + LLVMBasicBlockRef bad_block = LLVMAppendBasicBlock(g->cur_fn_val, "PtrToIntBad"); + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "PtrToIntOk"); + LLVMBuildCondBr(g->builder, is_zero_bit, bad_block, ok_block); + + LLVMPositionBuilderAtEnd(g->builder, bad_block); + gen_safety_crash(g, PanicMsgIdPtrCastNull); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + } + + { + LLVMValueRef alignment_minus_1 = LLVMConstInt(usize->llvm_type, align_bytes - 1, false); + LLVMValueRef anded_val = LLVMBuildAnd(g->builder, target_val, alignment_minus_1, ""); + LLVMValueRef is_ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, anded_val, zero, ""); + LLVMBasicBlockRef bad_block = LLVMAppendBasicBlock(g->cur_fn_val, "PtrToIntAlignBad"); + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "PtrToIntAlignOk"); + LLVMBuildCondBr(g->builder, is_ok_bit, ok_block, bad_block); + + LLVMPositionBuilderAtEnd(g->builder, bad_block); + gen_safety_crash(g, PanicMsgIdIncorrectAlignment); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + } + } + return LLVMBuildIntToPtr(g->builder, target_val, get_llvm_type(g, wanted_type), ""); +} + +static LLVMValueRef ir_render_ptr_to_int(CodeGen *g, IrExecutableGen *executable, IrInstGenPtrToInt *instruction) { + ZigType *wanted_type = instruction->base.value->type; + LLVMValueRef target_val = ir_llvm_value(g, instruction->target); + return LLVMBuildPtrToInt(g->builder, target_val, get_llvm_type(g, wanted_type), ""); +} + +static LLVMValueRef ir_render_int_to_enum(CodeGen *g, IrExecutableGen *executable, IrInstGenIntToEnum *instruction) { + ZigType *wanted_type = instruction->base.value->type; + assert(wanted_type->id == ZigTypeIdEnum); + ZigType *tag_int_type = wanted_type->data.enumeration.tag_int_type; + + LLVMValueRef target_val = ir_llvm_value(g, instruction->target); + LLVMValueRef tag_int_value = gen_widen_or_shorten(g, ir_want_runtime_safety(g, &instruction->base), + instruction->target->value->type, tag_int_type, target_val); + + if (ir_want_runtime_safety(g, &instruction->base) && !wanted_type->data.enumeration.non_exhaustive) { + LLVMBasicBlockRef bad_value_block = LLVMAppendBasicBlock(g->cur_fn_val, "BadValue"); + LLVMBasicBlockRef ok_value_block = LLVMAppendBasicBlock(g->cur_fn_val, "OkValue"); + size_t field_count = wanted_type->data.enumeration.src_field_count; + LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, tag_int_value, bad_value_block, field_count); + + HashMap occupied_tag_values = {}; + occupied_tag_values.init(field_count); + + for (size_t field_i = 0; field_i < field_count; field_i += 1) { + TypeEnumField *type_enum_field = &wanted_type->data.enumeration.fields[field_i]; + + Buf *name = type_enum_field->name; + auto entry = occupied_tag_values.put_unique(type_enum_field->value, name); + if (entry != nullptr) { + continue; + } + + LLVMValueRef this_tag_int_value = bigint_to_llvm_const(get_llvm_type(g, tag_int_type), + &type_enum_field->value); + LLVMAddCase(switch_instr, this_tag_int_value, ok_value_block); + } + occupied_tag_values.deinit(); + LLVMPositionBuilderAtEnd(g->builder, bad_value_block); + gen_safety_crash(g, PanicMsgIdBadEnumValue); + + LLVMPositionBuilderAtEnd(g->builder, ok_value_block); + } + return tag_int_value; +} + +static LLVMValueRef ir_render_int_to_err(CodeGen *g, IrExecutableGen *executable, IrInstGenIntToErr *instruction) { + ZigType *wanted_type = instruction->base.value->type; + assert(wanted_type->id == ZigTypeIdErrorSet); + + ZigType *actual_type = instruction->target->value->type; + assert(actual_type->id == ZigTypeIdInt); + assert(!actual_type->data.integral.is_signed); + + LLVMValueRef target_val = ir_llvm_value(g, instruction->target); + + if (ir_want_runtime_safety(g, &instruction->base)) { + add_error_range_check(g, wanted_type, actual_type, target_val); + } + + return gen_widen_or_shorten(g, false, actual_type, g->err_tag_type, target_val); +} + +static LLVMValueRef ir_render_err_to_int(CodeGen *g, IrExecutableGen *executable, IrInstGenErrToInt *instruction) { + ZigType *wanted_type = instruction->base.value->type; + assert(wanted_type->id == ZigTypeIdInt); + assert(!wanted_type->data.integral.is_signed); + + ZigType *actual_type = instruction->target->value->type; + LLVMValueRef target_val = ir_llvm_value(g, instruction->target); + + if (actual_type->id == ZigTypeIdErrorSet) { + return gen_widen_or_shorten(g, ir_want_runtime_safety(g, &instruction->base), + g->err_tag_type, wanted_type, target_val); + } else if (actual_type->id == ZigTypeIdErrorUnion) { + // this should have been a compile time constant + assert(type_has_bits(g, actual_type->data.error_union.err_set_type)); + + if (!type_has_bits(g, actual_type->data.error_union.payload_type)) { + return gen_widen_or_shorten(g, ir_want_runtime_safety(g, &instruction->base), + g->err_tag_type, wanted_type, target_val); + } else { + zig_panic("TODO err to int when error union payload type not void"); + } + } else { + zig_unreachable(); + } +} + +static LLVMValueRef ir_render_unreachable(CodeGen *g, IrExecutableGen *executable, + IrInstGenUnreachable *unreachable_instruction) +{ + if (ir_want_runtime_safety(g, &unreachable_instruction->base)) { + gen_safety_crash(g, PanicMsgIdUnreachable); + } else { + LLVMBuildUnreachable(g->builder); + } + return nullptr; +} + +static LLVMValueRef ir_render_cond_br(CodeGen *g, IrExecutableGen *executable, + IrInstGenCondBr *cond_br_instruction) +{ + LLVMBuildCondBr(g->builder, + ir_llvm_value(g, cond_br_instruction->condition), + cond_br_instruction->then_block->llvm_block, + cond_br_instruction->else_block->llvm_block); + return nullptr; +} + +static LLVMValueRef ir_render_br(CodeGen *g, IrExecutableGen *executable, IrInstGenBr *br_instruction) { + LLVMBuildBr(g->builder, br_instruction->dest_block->llvm_block); + return nullptr; +} + +static LLVMValueRef ir_render_binary_not(CodeGen *g, IrExecutableGen *executable, + IrInstGenBinaryNot *inst) +{ + LLVMValueRef operand = ir_llvm_value(g, inst->operand); + return LLVMBuildNot(g->builder, operand, ""); +} + +static LLVMValueRef ir_gen_negation(CodeGen *g, IrInstGen *inst, IrInstGen *operand, bool wrapping) { + LLVMValueRef llvm_operand = ir_llvm_value(g, operand); + ZigType *operand_type = operand->value->type; + ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? + operand_type->data.vector.elem_type : operand_type; + + if (scalar_type->id == ZigTypeIdFloat) { + ZigLLVMSetFastMath(g->builder, ir_want_fast_math(g, inst)); + return LLVMBuildFNeg(g->builder, llvm_operand, ""); + } else if (scalar_type->id == ZigTypeIdInt) { + if (wrapping) { + return LLVMBuildNeg(g->builder, llvm_operand, ""); + } else if (ir_want_runtime_safety(g, inst)) { + LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(llvm_operand)); + return gen_overflow_op(g, operand_type, AddSubMulSub, zero, llvm_operand); + } else if (scalar_type->data.integral.is_signed) { + return LLVMBuildNSWNeg(g->builder, llvm_operand, ""); + } else { + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +static LLVMValueRef ir_render_negation(CodeGen *g, IrExecutableGen *executable, + IrInstGenNegation *inst) +{ + return ir_gen_negation(g, &inst->base, inst->operand, false); +} + +static LLVMValueRef ir_render_negation_wrapping(CodeGen *g, IrExecutableGen *executable, + IrInstGenNegationWrapping *inst) +{ + return ir_gen_negation(g, &inst->base, inst->operand, true); +} + +static LLVMValueRef ir_render_bool_not(CodeGen *g, IrExecutableGen *executable, IrInstGenBoolNot *instruction) { + LLVMValueRef value = ir_llvm_value(g, instruction->value); + LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(value)); + return LLVMBuildICmp(g->builder, LLVMIntEQ, value, zero, ""); +} + +static void render_decl_var(CodeGen *g, ZigVar *var) { + if (!type_has_bits(g, var->var_type)) + return; + + var->value_ref = ir_llvm_value(g, var->ptr_instruction); + gen_var_debug_decl(g, var); +} + +static LLVMValueRef ir_render_decl_var(CodeGen *g, IrExecutableGen *executable, IrInstGenDeclVar *instruction) { + instruction->var->ptr_instruction = instruction->var_ptr; + instruction->var->did_the_decl_codegen = true; + render_decl_var(g, instruction->var); + return nullptr; +} + +static LLVMValueRef ir_render_load_ptr(CodeGen *g, IrExecutableGen *executable, + IrInstGenLoadPtr *instruction) +{ + ZigType *child_type = instruction->base.value->type; + if (!type_has_bits(g, child_type)) + return nullptr; + + LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr); + ZigType *ptr_type = instruction->ptr->value->type; + assert(ptr_type->id == ZigTypeIdPointer); + + ir_assert(ptr_type->data.pointer.vector_index != VECTOR_INDEX_RUNTIME, &instruction->base); + if (ptr_type->data.pointer.vector_index != VECTOR_INDEX_NONE) { + LLVMValueRef index_val = LLVMConstInt(LLVMInt32Type(), + ptr_type->data.pointer.vector_index, false); + LLVMValueRef loaded_vector = LLVMBuildLoad(g->builder, ptr, ""); + return LLVMBuildExtractElement(g->builder, loaded_vector, index_val, ""); + } + + uint32_t host_int_bytes = ptr_type->data.pointer.host_int_bytes; + if (host_int_bytes == 0) + return get_handle_value(g, ptr, child_type, ptr_type); + + bool big_endian = g->is_big_endian; + + LLVMTypeRef int_ptr_ty = LLVMPointerType(LLVMIntType(host_int_bytes * 8), 0); + LLVMValueRef int_ptr = LLVMBuildBitCast(g->builder, ptr, int_ptr_ty, ""); + LLVMValueRef containing_int = gen_load(g, int_ptr, ptr_type, ""); + + uint32_t host_bit_count = LLVMGetIntTypeWidth(LLVMTypeOf(containing_int)); + assert(host_bit_count == host_int_bytes * 8); + uint32_t size_in_bits = type_size_bits(g, child_type); + + uint32_t bit_offset = ptr_type->data.pointer.bit_offset_in_host; + uint32_t shift_amt = big_endian ? host_bit_count - bit_offset - size_in_bits : bit_offset; + + LLVMValueRef shift_amt_val = LLVMConstInt(LLVMTypeOf(containing_int), shift_amt, false); + LLVMValueRef shifted_value = LLVMBuildLShr(g->builder, containing_int, shift_amt_val, ""); + + if (handle_is_ptr(g, child_type)) { + LLVMValueRef result_loc = ir_llvm_value(g, instruction->result_loc); + LLVMTypeRef same_size_int = LLVMIntType(size_in_bits); + LLVMValueRef truncated_int = LLVMBuildTrunc(g->builder, shifted_value, same_size_int, ""); + LLVMValueRef bitcasted_ptr = LLVMBuildBitCast(g->builder, result_loc, + LLVMPointerType(same_size_int, 0), ""); + LLVMBuildStore(g->builder, truncated_int, bitcasted_ptr); + return result_loc; + } + + if (child_type->id == ZigTypeIdFloat) { + LLVMTypeRef same_size_int = LLVMIntType(size_in_bits); + LLVMValueRef truncated_int = LLVMBuildTrunc(g->builder, shifted_value, same_size_int, ""); + return LLVMBuildBitCast(g->builder, truncated_int, get_llvm_type(g, child_type), ""); + } + + return LLVMBuildTrunc(g->builder, shifted_value, get_llvm_type(g, child_type), ""); +} + +static bool value_is_all_undef_array(CodeGen *g, ZigValue *const_val, size_t len) { + switch (const_val->data.x_array.special) { + case ConstArraySpecialUndef: + return true; + case ConstArraySpecialBuf: + return false; + case ConstArraySpecialNone: + for (size_t i = 0; i < len; i += 1) { + if (!value_is_all_undef(g, &const_val->data.x_array.data.s_none.elements[i])) + return false; + } + return true; + } + zig_unreachable(); +} + +static bool value_is_all_undef(CodeGen *g, ZigValue *const_val) { + Error err; + if (const_val->special == ConstValSpecialLazy && + (err = ir_resolve_lazy(g, nullptr, const_val))) + codegen_report_errors_and_exit(g); + + switch (const_val->special) { + case ConstValSpecialLazy: + zig_unreachable(); + case ConstValSpecialRuntime: + return false; + case ConstValSpecialUndef: + return true; + case ConstValSpecialStatic: + if (const_val->type->id == ZigTypeIdStruct) { + for (size_t i = 0; i < const_val->type->data.structure.src_field_count; i += 1) { + if (!value_is_all_undef(g, const_val->data.x_struct.fields[i])) + return false; + } + return true; + } else if (const_val->type->id == ZigTypeIdArray) { + return value_is_all_undef_array(g, const_val, const_val->type->data.array.len); + } else if (const_val->type->id == ZigTypeIdVector) { + return value_is_all_undef_array(g, const_val, const_val->type->data.vector.len); + } else { + return false; + } + } + zig_unreachable(); +} + +static LLVMValueRef gen_valgrind_client_request(CodeGen *g, LLVMValueRef default_value, LLVMValueRef request, + LLVMValueRef a1, LLVMValueRef a2, LLVMValueRef a3, LLVMValueRef a4, LLVMValueRef a5) +{ + if (!target_has_valgrind_support(g->zig_target)) { + return default_value; + } + LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; + bool asm_has_side_effects = true; + bool asm_is_alignstack = false; + if (g->zig_target->arch == ZigLLVM_x86_64) { + if (g->zig_target->os == OsLinux || target_os_is_darwin(g->zig_target->os) || g->zig_target->os == OsSolaris || + (g->zig_target->os == OsWindows && g->zig_target->abi != ZigLLVM_MSVC)) + { + if (g->cur_fn->valgrind_client_request_array == nullptr) { + LLVMBasicBlockRef prev_block = LLVMGetInsertBlock(g->builder); + LLVMBasicBlockRef entry_block = LLVMGetEntryBasicBlock(g->cur_fn->llvm_value); + LLVMValueRef first_inst = LLVMGetFirstInstruction(entry_block); + LLVMPositionBuilderBefore(g->builder, first_inst); + LLVMTypeRef array_type_ref = LLVMArrayType(usize_type_ref, 6); + g->cur_fn->valgrind_client_request_array = LLVMBuildAlloca(g->builder, array_type_ref, ""); + LLVMPositionBuilderAtEnd(g->builder, prev_block); + } + LLVMValueRef array_ptr = g->cur_fn->valgrind_client_request_array; + LLVMValueRef array_elements[] = {request, a1, a2, a3, a4, a5}; + LLVMValueRef zero = LLVMConstInt(usize_type_ref, 0, false); + for (unsigned i = 0; i < 6; i += 1) { + LLVMValueRef indexes[] = { + zero, + LLVMConstInt(usize_type_ref, i, false), + }; + LLVMValueRef elem_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indexes, 2, ""); + LLVMBuildStore(g->builder, array_elements[i], elem_ptr); + } + + Buf *asm_template = buf_create_from_str( + "rolq $$3, %rdi ; rolq $$13, %rdi\n" + "rolq $$61, %rdi ; rolq $$51, %rdi\n" + "xchgq %rbx,%rbx\n" + ); + Buf *asm_constraints = buf_create_from_str( + "={rdx},{rax},0,~{cc},~{memory}" + ); + unsigned input_and_output_count = 2; + LLVMValueRef array_ptr_as_usize = LLVMBuildPtrToInt(g->builder, array_ptr, usize_type_ref, ""); + LLVMValueRef param_values[] = { array_ptr_as_usize, default_value }; + LLVMTypeRef param_types[] = {usize_type_ref, usize_type_ref}; + LLVMTypeRef function_type = LLVMFunctionType(usize_type_ref, param_types, + input_and_output_count, false); + LLVMValueRef asm_fn = LLVMGetInlineAsm(function_type, buf_ptr(asm_template), buf_len(asm_template), + buf_ptr(asm_constraints), buf_len(asm_constraints), asm_has_side_effects, asm_is_alignstack, + LLVMInlineAsmDialectATT); + return LLVMBuildCall(g->builder, asm_fn, param_values, input_and_output_count, ""); + } + } + zig_unreachable(); +} + +static void gen_valgrind_undef(CodeGen *g, LLVMValueRef dest_ptr, LLVMValueRef byte_count) { + static const uint32_t VG_USERREQ__MAKE_MEM_UNDEFINED = 1296236545; + ZigType *usize = g->builtin_types.entry_usize; + LLVMValueRef zero = LLVMConstInt(usize->llvm_type, 0, false); + LLVMValueRef req = LLVMConstInt(usize->llvm_type, VG_USERREQ__MAKE_MEM_UNDEFINED, false); + LLVMValueRef ptr_as_usize = LLVMBuildPtrToInt(g->builder, dest_ptr, usize->llvm_type, ""); + gen_valgrind_client_request(g, zero, req, ptr_as_usize, byte_count, zero, zero, zero); +} + +static void gen_undef_init(CodeGen *g, uint32_t ptr_align_bytes, ZigType *value_type, LLVMValueRef ptr) { + assert(type_has_bits(g, value_type)); + uint64_t size_bytes = LLVMStoreSizeOfType(g->target_data_ref, get_llvm_type(g, value_type)); + assert(size_bytes > 0); + assert(ptr_align_bytes > 0); + // memset uninitialized memory to 0xaa + LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0); + LLVMValueRef fill_char = LLVMConstInt(LLVMInt8Type(), 0xaa, false); + LLVMValueRef dest_ptr = LLVMBuildBitCast(g->builder, ptr, ptr_u8, ""); + ZigType *usize = g->builtin_types.entry_usize; + LLVMValueRef byte_count = LLVMConstInt(usize->llvm_type, size_bytes, false); + ZigLLVMBuildMemSet(g->builder, dest_ptr, fill_char, byte_count, ptr_align_bytes, false); + // then tell valgrind that the memory is undefined even though we just memset it + if (g->valgrind_enabled) { + gen_valgrind_undef(g, dest_ptr, byte_count); + } +} + +static LLVMValueRef ir_render_store_ptr(CodeGen *g, IrExecutableGen *executable, IrInstGenStorePtr *instruction) { + Error err; + + ZigType *ptr_type = instruction->ptr->value->type; + assert(ptr_type->id == ZigTypeIdPointer); + bool ptr_type_has_bits; + if ((err = type_has_bits2(g, ptr_type, &ptr_type_has_bits))) + codegen_report_errors_and_exit(g); + if (!ptr_type_has_bits) + return nullptr; + if (instruction->ptr->base.ref_count == 0) { + // In this case, this StorePtr instruction should be elided. Something happened like this: + // var t = true; + // const x = if (t) Num.Two else unreachable; + // The if condition is a runtime value, so the StorePtr for `x = Num.Two` got generated + // (this instruction being rendered) but because of `else unreachable` the result ended + // up being a comptime const value. + return nullptr; + } + + bool have_init_expr = !value_is_all_undef(g, instruction->value->value); + if (have_init_expr) { + LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr); + LLVMValueRef value = ir_llvm_value(g, instruction->value); + gen_assign_raw(g, ptr, ptr_type, value); + } else if (ir_want_runtime_safety(g, &instruction->base)) { + gen_undef_init(g, get_ptr_align(g, ptr_type), instruction->value->value->type, + ir_llvm_value(g, instruction->ptr)); + } + return nullptr; +} + +static LLVMValueRef ir_render_vector_store_elem(CodeGen *g, IrExecutableGen *executable, + IrInstGenVectorStoreElem *instruction) +{ + LLVMValueRef vector_ptr = ir_llvm_value(g, instruction->vector_ptr); + LLVMValueRef index = ir_llvm_value(g, instruction->index); + LLVMValueRef value = ir_llvm_value(g, instruction->value); + + LLVMValueRef loaded_vector = gen_load(g, vector_ptr, instruction->vector_ptr->value->type, ""); + LLVMValueRef modified_vector = LLVMBuildInsertElement(g->builder, loaded_vector, value, index, ""); + gen_store(g, modified_vector, vector_ptr, instruction->vector_ptr->value->type); + return nullptr; +} + +static LLVMValueRef ir_render_var_ptr(CodeGen *g, IrExecutableGen *executable, IrInstGenVarPtr *instruction) { + if (instruction->base.value->special != ConstValSpecialRuntime) + return ir_llvm_value(g, &instruction->base); + ZigVar *var = instruction->var; + if (type_has_bits(g, var->var_type)) { + assert(var->value_ref); + return var->value_ref; + } else { + return nullptr; + } +} + +static LLVMValueRef ir_render_return_ptr(CodeGen *g, IrExecutableGen *executable, + IrInstGenReturnPtr *instruction) +{ + if (!type_has_bits(g, instruction->base.value->type)) + return nullptr; + ir_assert(g->cur_ret_ptr != nullptr, &instruction->base); + return g->cur_ret_ptr; +} + +static LLVMValueRef ir_render_elem_ptr(CodeGen *g, IrExecutableGen *executable, IrInstGenElemPtr *instruction) { + LLVMValueRef array_ptr_ptr = ir_llvm_value(g, instruction->array_ptr); + ZigType *array_ptr_type = instruction->array_ptr->value->type; + assert(array_ptr_type->id == ZigTypeIdPointer); + ZigType *array_type = array_ptr_type->data.pointer.child_type; + LLVMValueRef subscript_value = ir_llvm_value(g, instruction->elem_index); + assert(subscript_value); + + if (!type_has_bits(g, array_type)) + return nullptr; + + bool safety_check_on = ir_want_runtime_safety(g, &instruction->base) && instruction->safety_check_on; + + if (array_type->id == ZigTypeIdArray || + (array_type->id == ZigTypeIdPointer && array_type->data.pointer.ptr_len == PtrLenSingle)) + { + LLVMValueRef array_ptr = get_handle_value(g, array_ptr_ptr, array_type, array_ptr_type); + if (array_type->id == ZigTypeIdPointer) { + assert(array_type->data.pointer.child_type->id == ZigTypeIdArray); + array_type = array_type->data.pointer.child_type; + } + + assert(array_type->data.array.len != 0 || array_type->data.array.sentinel != nullptr); + + if (safety_check_on) { + uint64_t extra_len_from_sentinel = (array_type->data.array.sentinel != nullptr) ? 1 : 0; + uint64_t full_len = array_type->data.array.len + extra_len_from_sentinel; + LLVMValueRef end = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, full_len, false); + add_bounds_check(g, subscript_value, LLVMIntEQ, nullptr, LLVMIntULT, end); + } + if (array_ptr_type->data.pointer.host_int_bytes != 0) { + return array_ptr_ptr; + } + ZigType *child_type = array_type->data.array.child_type; + if (child_type->id == ZigTypeIdStruct && + child_type->data.structure.layout == ContainerLayoutPacked) + { + ZigType *ptr_type = instruction->base.value->type; + size_t host_int_bytes = ptr_type->data.pointer.host_int_bytes; + if (host_int_bytes != 0) { + uint32_t size_in_bits = type_size_bits(g, ptr_type->data.pointer.child_type); + LLVMTypeRef ptr_u8_type_ref = LLVMPointerType(LLVMInt8Type(), 0); + LLVMValueRef u8_array_ptr = LLVMBuildBitCast(g->builder, array_ptr, ptr_u8_type_ref, ""); + assert(size_in_bits % 8 == 0); + LLVMValueRef elem_size_bytes = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, + size_in_bits / 8, false); + LLVMValueRef byte_offset = LLVMBuildNUWMul(g->builder, subscript_value, elem_size_bytes, ""); + LLVMValueRef indices[] = { + byte_offset + }; + LLVMValueRef elem_byte_ptr = LLVMBuildInBoundsGEP(g->builder, u8_array_ptr, indices, 1, ""); + return LLVMBuildBitCast(g->builder, elem_byte_ptr, LLVMPointerType(get_llvm_type(g, child_type), 0), ""); + } + } + LLVMValueRef indices[] = { + LLVMConstNull(g->builtin_types.entry_usize->llvm_type), + subscript_value + }; + return LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, ""); + } else if (array_type->id == ZigTypeIdPointer) { + LLVMValueRef array_ptr = get_handle_value(g, array_ptr_ptr, array_type, array_ptr_type); + assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind); + LLVMValueRef indices[] = { + subscript_value + }; + return LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 1, ""); + } else if (array_type->id == ZigTypeIdStruct) { + LLVMValueRef array_ptr = get_handle_value(g, array_ptr_ptr, array_type, array_ptr_type); + assert(array_type->data.structure.special == StructSpecialSlice); + + ZigType *ptr_type = array_type->data.structure.fields[slice_ptr_index]->type_entry; + if (!type_has_bits(g, ptr_type)) { + if (safety_check_on) { + assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMIntegerTypeKind); + add_bounds_check(g, subscript_value, LLVMIntEQ, nullptr, LLVMIntULT, array_ptr); + } + return nullptr; + } + + assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind); + assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind); + + if (safety_check_on) { + size_t len_index = array_type->data.structure.fields[slice_len_index]->gen_index; + assert(len_index != SIZE_MAX); + LLVMValueRef len_ptr = LLVMBuildStructGEP(g->builder, array_ptr, (unsigned)len_index, ""); + LLVMValueRef len = gen_load_untyped(g, len_ptr, 0, false, ""); + LLVMIntPredicate upper_op = (ptr_type->data.pointer.sentinel != nullptr) ? LLVMIntULE : LLVMIntULT; + add_bounds_check(g, subscript_value, LLVMIntEQ, nullptr, upper_op, len); + } + + size_t ptr_index = array_type->data.structure.fields[slice_ptr_index]->gen_index; + assert(ptr_index != SIZE_MAX); + LLVMValueRef ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, (unsigned)ptr_index, ""); + LLVMValueRef ptr = gen_load_untyped(g, ptr_ptr, 0, false, ""); + return LLVMBuildInBoundsGEP(g->builder, ptr, &subscript_value, 1, ""); + } else if (array_type->id == ZigTypeIdVector) { + return array_ptr_ptr; + } else { + zig_unreachable(); + } +} + +static LLVMValueRef get_new_stack_addr(CodeGen *g, LLVMValueRef new_stack) { + LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, new_stack, (unsigned)slice_ptr_index, ""); + LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, new_stack, (unsigned)slice_len_index, ""); + + LLVMValueRef ptr_value = gen_load_untyped(g, ptr_field_ptr, 0, false, ""); + LLVMValueRef len_value = gen_load_untyped(g, len_field_ptr, 0, false, ""); + + LLVMValueRef ptr_addr = LLVMBuildPtrToInt(g->builder, ptr_value, LLVMTypeOf(len_value), ""); + LLVMValueRef end_addr = LLVMBuildNUWAdd(g->builder, ptr_addr, len_value, ""); + const unsigned alignment_factor = ZigLLVMDataLayoutGetStackAlignment(g->target_data_ref); + LLVMValueRef align_amt = LLVMConstInt(LLVMTypeOf(end_addr), alignment_factor, false); + LLVMValueRef align_adj = LLVMBuildURem(g->builder, end_addr, align_amt, ""); + return LLVMBuildNUWSub(g->builder, end_addr, align_adj, ""); +} + +static void gen_set_stack_pointer(CodeGen *g, LLVMValueRef aligned_end_addr) { + LLVMValueRef write_register_fn_val = get_write_register_fn_val(g); + + if (g->sp_md_node == nullptr) { + Buf *sp_reg_name = buf_create_from_str(arch_stack_pointer_register_name(g->zig_target->arch)); + LLVMValueRef str_node = LLVMMDString(buf_ptr(sp_reg_name), buf_len(sp_reg_name) + 1); + g->sp_md_node = LLVMMDNode(&str_node, 1); + } + + LLVMValueRef params[] = { + g->sp_md_node, + aligned_end_addr, + }; + + LLVMBuildCall(g->builder, write_register_fn_val, params, 2, ""); +} + +static void set_call_instr_sret(CodeGen *g, LLVMValueRef call_instr) { + unsigned attr_kind_id = LLVMGetEnumAttributeKindForName("sret", 4); + LLVMAttributeRef sret_attr = LLVMCreateEnumAttribute(LLVMGetGlobalContext(), attr_kind_id, 0); + LLVMAddCallSiteAttribute(call_instr, 1, sret_attr); +} + +static void render_async_spills(CodeGen *g) { + ZigType *fn_type = g->cur_fn->type_entry; + ZigType *import = get_scope_import(&g->cur_fn->fndef_scope->base); + + CalcLLVMFieldIndex arg_calc = {0}; + frame_index_arg_calc(g, &arg_calc, fn_type->data.fn.fn_type_id.return_type); + for (size_t var_i = 0; var_i < g->cur_fn->variable_list.length; var_i += 1) { + ZigVar *var = g->cur_fn->variable_list.at(var_i); + + if (!type_has_bits(g, var->var_type)) { + continue; + } + if (ir_get_var_is_comptime(var)) + continue; + switch (type_requires_comptime(g, var->var_type)) { + case ReqCompTimeInvalid: + zig_unreachable(); + case ReqCompTimeYes: + continue; + case ReqCompTimeNo: + break; + } + if (var->src_arg_index == SIZE_MAX) { + continue; + } + + calc_llvm_field_index_add(g, &arg_calc, var->var_type); + var->value_ref = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr, arg_calc.field_index - 1, var->name); + if (var->decl_node) { + var->di_loc_var = ZigLLVMCreateAutoVariable(g->dbuilder, get_di_scope(g, var->parent_scope), + var->name, import->data.structure.root_struct->di_file, + (unsigned)(var->decl_node->line + 1), + get_llvm_di_type(g, var->var_type), !g->strip_debug_symbols, 0); + gen_var_debug_decl(g, var); + } + } + + ZigType *frame_type = g->cur_fn->frame_type->data.frame.locals_struct; + + for (size_t alloca_i = 0; alloca_i < g->cur_fn->alloca_gen_list.length; alloca_i += 1) { + IrInstGenAlloca *instruction = g->cur_fn->alloca_gen_list.at(alloca_i); + if (instruction->field_index == SIZE_MAX) + continue; + + size_t gen_index = frame_type->data.structure.fields[instruction->field_index]->gen_index; + instruction->base.llvm_value = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr, gen_index, + instruction->name_hint); + } +} + +static void render_async_var_decls(CodeGen *g, Scope *scope) { + for (;;) { + switch (scope->id) { + case ScopeIdCImport: + zig_unreachable(); + case ScopeIdFnDef: + return; + case ScopeIdVarDecl: { + ZigVar *var = reinterpret_cast(scope)->var; + if (var->did_the_decl_codegen) { + render_decl_var(g, var); + } + } + ZIG_FALLTHROUGH; + + case ScopeIdDecls: + case ScopeIdBlock: + case ScopeIdDefer: + case ScopeIdDeferExpr: + case ScopeIdLoop: + case ScopeIdSuspend: + case ScopeIdCompTime: + case ScopeIdNoSuspend: + case ScopeIdRuntime: + case ScopeIdTypeOf: + case ScopeIdExpr: + scope = scope->parent; + continue; + } + } +} + +static LLVMValueRef gen_frame_size(CodeGen *g, LLVMValueRef fn_val) { + assert(g->need_frame_size_prefix_data); + LLVMTypeRef usize_llvm_type = g->builtin_types.entry_usize->llvm_type; + LLVMTypeRef ptr_usize_llvm_type = LLVMPointerType(usize_llvm_type, 0); + LLVMValueRef casted_fn_val = LLVMBuildBitCast(g->builder, fn_val, ptr_usize_llvm_type, ""); + LLVMValueRef negative_one = LLVMConstInt(LLVMInt32Type(), -1, true); + LLVMValueRef prefix_ptr = LLVMBuildInBoundsGEP(g->builder, casted_fn_val, &negative_one, 1, ""); + return LLVMBuildLoad(g->builder, prefix_ptr, ""); +} + +static void gen_init_stack_trace(CodeGen *g, LLVMValueRef trace_field_ptr, LLVMValueRef addrs_field_ptr) { + LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; + LLVMValueRef zero = LLVMConstNull(usize_type_ref); + + LLVMValueRef index_ptr = LLVMBuildStructGEP(g->builder, trace_field_ptr, 0, ""); + LLVMBuildStore(g->builder, zero, index_ptr); + + LLVMValueRef addrs_slice_ptr = LLVMBuildStructGEP(g->builder, trace_field_ptr, 1, ""); + LLVMValueRef addrs_ptr_ptr = LLVMBuildStructGEP(g->builder, addrs_slice_ptr, slice_ptr_index, ""); + LLVMValueRef indices[] = { LLVMConstNull(usize_type_ref), LLVMConstNull(usize_type_ref) }; + LLVMValueRef trace_field_addrs_as_ptr = LLVMBuildInBoundsGEP(g->builder, addrs_field_ptr, indices, 2, ""); + LLVMBuildStore(g->builder, trace_field_addrs_as_ptr, addrs_ptr_ptr); + + LLVMValueRef addrs_len_ptr = LLVMBuildStructGEP(g->builder, addrs_slice_ptr, slice_len_index, ""); + LLVMBuildStore(g->builder, LLVMConstInt(usize_type_ref, stack_trace_ptr_count, false), addrs_len_ptr); +} + +static LLVMValueRef ir_render_call(CodeGen *g, IrExecutableGen *executable, IrInstGenCall *instruction) { + Error err; + + LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; + + LLVMValueRef fn_val; + ZigType *fn_type; + bool callee_is_async; + if (instruction->fn_entry) { + fn_val = fn_llvm_value(g, instruction->fn_entry); + fn_type = instruction->fn_entry->type_entry; + callee_is_async = fn_is_async(instruction->fn_entry); + } else { + assert(instruction->fn_ref); + fn_val = ir_llvm_value(g, instruction->fn_ref); + fn_type = instruction->fn_ref->value->type; + callee_is_async = fn_type->data.fn.fn_type_id.cc == CallingConventionAsync; + } + + FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; + + ZigType *src_return_type = fn_type_id->return_type; + bool ret_has_bits = type_has_bits(g, src_return_type); + + CallingConvention cc = fn_type->data.fn.fn_type_id.cc; + + bool first_arg_ret = ret_has_bits && want_first_arg_sret(g, fn_type_id); + bool prefix_arg_err_ret_stack = codegen_fn_has_err_ret_tracing_arg(g, fn_type_id->return_type); + bool is_var_args = fn_type_id->is_var_args; + ZigList gen_param_values = {}; + ZigList gen_param_types = {}; + LLVMValueRef result_loc = instruction->result_loc ? ir_llvm_value(g, instruction->result_loc) : nullptr; + LLVMValueRef zero = LLVMConstNull(usize_type_ref); + bool need_frame_ptr_ptr_spill = false; + ZigType *anyframe_type = nullptr; + LLVMValueRef frame_result_loc_uncasted = nullptr; + LLVMValueRef frame_result_loc; + LLVMValueRef awaiter_init_val; + LLVMValueRef ret_ptr; + if (callee_is_async) { + if (instruction->new_stack == nullptr) { + if (instruction->modifier == CallModifierAsync) { + frame_result_loc = result_loc; + } else { + ir_assert(instruction->frame_result_loc != nullptr, &instruction->base); + frame_result_loc_uncasted = ir_llvm_value(g, instruction->frame_result_loc); + ir_assert(instruction->fn_entry != nullptr, &instruction->base); + frame_result_loc = LLVMBuildBitCast(g->builder, frame_result_loc_uncasted, + LLVMPointerType(get_llvm_type(g, instruction->fn_entry->frame_type), 0), ""); + } + } else { + if (instruction->new_stack->value->type->id == ZigTypeIdPointer && + instruction->new_stack->value->type->data.pointer.child_type->id == ZigTypeIdFnFrame) + { + frame_result_loc = ir_llvm_value(g, instruction->new_stack); + } else { + LLVMValueRef frame_slice_ptr = ir_llvm_value(g, instruction->new_stack); + if (ir_want_runtime_safety(g, &instruction->base)) { + LLVMValueRef given_len_ptr = LLVMBuildStructGEP(g->builder, frame_slice_ptr, slice_len_index, ""); + LLVMValueRef given_frame_len = LLVMBuildLoad(g->builder, given_len_ptr, ""); + LLVMValueRef actual_frame_len = gen_frame_size(g, fn_val); + + LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "FrameSizeCheckFail"); + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "FrameSizeCheckOk"); + + LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntUGE, given_frame_len, actual_frame_len, ""); + LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); + + LLVMPositionBuilderAtEnd(g->builder, fail_block); + gen_safety_crash(g, PanicMsgIdFrameTooSmall); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + } + need_frame_ptr_ptr_spill = true; + LLVMValueRef frame_ptr_ptr = LLVMBuildStructGEP(g->builder, frame_slice_ptr, slice_ptr_index, ""); + LLVMValueRef frame_ptr = LLVMBuildLoad(g->builder, frame_ptr_ptr, ""); + if (instruction->fn_entry == nullptr) { + anyframe_type = get_any_frame_type(g, src_return_type); + frame_result_loc = LLVMBuildBitCast(g->builder, frame_ptr, get_llvm_type(g, anyframe_type), ""); + } else { + ZigType *frame_type = get_fn_frame_type(g, instruction->fn_entry); + if ((err = type_resolve(g, frame_type, ResolveStatusLLVMFull))) + codegen_report_errors_and_exit(g); + ZigType *ptr_frame_type = get_pointer_to_type(g, frame_type, false); + frame_result_loc = LLVMBuildBitCast(g->builder, frame_ptr, + get_llvm_type(g, ptr_frame_type), ""); + } + } + } + if (instruction->modifier == CallModifierAsync) { + if (instruction->new_stack == nullptr) { + awaiter_init_val = zero; + + if (ret_has_bits) { + // Use the result location which is inside the frame if this is an async call. + ret_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, frame_ret_start + 2, ""); + } + } else { + awaiter_init_val = zero; + + if (ret_has_bits) { + if (result_loc != nullptr) { + // Use the result location provided to the @asyncCall builtin + ret_ptr = result_loc; + } else { + // no result location provided to @asyncCall - use the one inside the frame. + ret_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, frame_ret_start + 2, ""); + } + } + } + + // even if prefix_arg_err_ret_stack is true, let the async function do its own + // initialization. + } else { + if (instruction->modifier == CallModifierNoSuspend && !fn_is_async(g->cur_fn)) { + // Async function called as a normal function, and calling function is not async. + // This is allowed because it was called with `nosuspend` which asserts that it will + // never suspend. + awaiter_init_val = zero; + } else { + // async function called as a normal function + awaiter_init_val = LLVMBuildPtrToInt(g->builder, g->cur_frame_ptr, usize_type_ref, ""); // caller's own frame pointer + } + if (ret_has_bits) { + if (result_loc == nullptr) { + // return type is a scalar, but we still need a pointer to it. Use the async fn frame. + ret_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, frame_ret_start + 2, ""); + } else { + // Use the call instruction's result location. + ret_ptr = result_loc; + } + + // Store a zero in the awaiter's result ptr to indicate we do not need a copy made. + LLVMValueRef awaiter_ret_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, frame_ret_start + 1, ""); + LLVMValueRef zero_ptr = LLVMConstNull(LLVMGetElementType(LLVMTypeOf(awaiter_ret_ptr))); + LLVMBuildStore(g->builder, zero_ptr, awaiter_ret_ptr); + } + + if (prefix_arg_err_ret_stack) { + LLVMValueRef err_ret_trace_ptr_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, + frame_index_trace_arg(g, src_return_type) + 1, ""); + bool is_llvm_alloca; + LLVMValueRef my_err_ret_trace_val = get_cur_err_ret_trace_val(g, instruction->base.base.scope, + &is_llvm_alloca); + LLVMBuildStore(g->builder, my_err_ret_trace_val, err_ret_trace_ptr_ptr); + } + } + + assert(frame_result_loc != nullptr); + + LLVMValueRef fn_ptr_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, frame_fn_ptr_index, ""); + LLVMValueRef bitcasted_fn_val = LLVMBuildBitCast(g->builder, fn_val, + LLVMGetElementType(LLVMTypeOf(fn_ptr_ptr)), ""); + LLVMBuildStore(g->builder, bitcasted_fn_val, fn_ptr_ptr); + + LLVMValueRef resume_index_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, frame_resume_index, ""); + LLVMBuildStore(g->builder, zero, resume_index_ptr); + + LLVMValueRef awaiter_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, frame_awaiter_index, ""); + LLVMBuildStore(g->builder, awaiter_init_val, awaiter_ptr); + + if (ret_has_bits) { + LLVMValueRef ret_ptr_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, frame_ret_start, ""); + LLVMBuildStore(g->builder, ret_ptr, ret_ptr_ptr); + } + } else if (instruction->modifier == CallModifierAsync) { + // Async call of blocking function + if (instruction->new_stack != nullptr) { + zig_panic("TODO @asyncCall of non-async function"); + } + frame_result_loc = result_loc; + awaiter_init_val = LLVMConstAllOnes(usize_type_ref); + + LLVMValueRef awaiter_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, frame_awaiter_index, ""); + LLVMBuildStore(g->builder, awaiter_init_val, awaiter_ptr); + + if (ret_has_bits) { + ret_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, frame_ret_start + 2, ""); + LLVMValueRef ret_ptr_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, frame_ret_start, ""); + LLVMBuildStore(g->builder, ret_ptr, ret_ptr_ptr); + + if (first_arg_ret) { + gen_param_values.append(ret_ptr); + } + if (prefix_arg_err_ret_stack) { + // Set up the callee stack trace pointer pointing into the frame. + // Then we have to wire up the StackTrace pointers. + // Await is responsible for merging error return traces. + uint32_t trace_field_index_start = frame_index_trace_arg(g, src_return_type); + LLVMValueRef callee_trace_ptr_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, + trace_field_index_start, ""); + LLVMValueRef trace_field_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, + trace_field_index_start + 2, ""); + LLVMValueRef addrs_field_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, + trace_field_index_start + 3, ""); + + LLVMBuildStore(g->builder, trace_field_ptr, callee_trace_ptr_ptr); + + gen_init_stack_trace(g, trace_field_ptr, addrs_field_ptr); + + bool is_llvm_alloca; + gen_param_values.append(get_cur_err_ret_trace_val(g, instruction->base.base.scope, &is_llvm_alloca)); + } + } + } else { + if (first_arg_ret) { + gen_param_values.append(result_loc); + } + if (prefix_arg_err_ret_stack) { + bool is_llvm_alloca; + gen_param_values.append(get_cur_err_ret_trace_val(g, instruction->base.base.scope, &is_llvm_alloca)); + } + } + FnWalk fn_walk = {}; + fn_walk.id = FnWalkIdCall; + fn_walk.data.call.inst = instruction; + fn_walk.data.call.is_var_args = is_var_args; + fn_walk.data.call.gen_param_values = &gen_param_values; + fn_walk.data.call.gen_param_types = &gen_param_types; + walk_function_params(g, fn_type, &fn_walk); + + ZigLLVM_CallAttr call_attr; + switch (instruction->modifier) { + case CallModifierBuiltin: + case CallModifierCompileTime: + zig_unreachable(); + case CallModifierNone: + case CallModifierNoSuspend: + case CallModifierAsync: + call_attr = ZigLLVM_CallAttrAuto; + break; + case CallModifierNeverTail: + call_attr = ZigLLVM_CallAttrNeverTail; + break; + case CallModifierNeverInline: + call_attr = ZigLLVM_CallAttrNeverInline; + break; + case CallModifierAlwaysTail: + call_attr = ZigLLVM_CallAttrAlwaysTail; + break; + case CallModifierAlwaysInline: + ir_assert(instruction->fn_entry != nullptr, &instruction->base); + call_attr = ZigLLVM_CallAttrAlwaysInline; + break; + } + + ZigLLVM_CallingConv llvm_cc = get_llvm_cc(g, cc); + LLVMValueRef result; + + if (callee_is_async) { + CalcLLVMFieldIndex arg_calc_start = {0}; + frame_index_arg_calc(g, &arg_calc_start, fn_type->data.fn.fn_type_id.return_type); + + LLVMValueRef casted_frame; + if (instruction->new_stack != nullptr && instruction->fn_entry == nullptr) { + // We need the frame type to be a pointer to a struct that includes the args + + // Count ahead to determine how many llvm struct fields we need. + CalcLLVMFieldIndex arg_calc = arg_calc_start; + for (size_t i = 0; i < gen_param_types.length; i += 1) { + calc_llvm_field_index_add(g, &arg_calc, gen_param_types.at(i)); + } + size_t field_count = arg_calc.field_index; + + LLVMTypeRef *field_types = heap::c_allocator.allocate_nonzero(field_count); + LLVMGetStructElementTypes(LLVMGetElementType(LLVMTypeOf(frame_result_loc)), field_types); + assert(LLVMCountStructElementTypes(LLVMGetElementType(LLVMTypeOf(frame_result_loc))) == arg_calc_start.field_index); + + arg_calc = arg_calc_start; + for (size_t arg_i = 0; arg_i < gen_param_values.length; arg_i += 1) { + CalcLLVMFieldIndex prev = arg_calc; + calc_llvm_field_index_add(g, &arg_calc, gen_param_types.at(arg_i)); + field_types[arg_calc.field_index - 1] = LLVMTypeOf(gen_param_values.at(arg_i)); + if (arg_calc.field_index - prev.field_index > 1) { + // Padding field + uint32_t pad_bytes = arg_calc.offset - prev.offset - gen_param_types.at(arg_i)->abi_size; + LLVMTypeRef pad_llvm_type = LLVMArrayType(LLVMInt8Type(), pad_bytes); + field_types[arg_calc.field_index - 2] = pad_llvm_type; + } + } + LLVMTypeRef frame_with_args_type = LLVMStructType(field_types, field_count, false); + LLVMTypeRef ptr_frame_with_args_type = LLVMPointerType(frame_with_args_type, 0); + + casted_frame = LLVMBuildBitCast(g->builder, frame_result_loc, ptr_frame_with_args_type, ""); + } else { + casted_frame = frame_result_loc; + } + + CalcLLVMFieldIndex arg_calc = arg_calc_start; + for (size_t arg_i = 0; arg_i < gen_param_values.length; arg_i += 1) { + calc_llvm_field_index_add(g, &arg_calc, gen_param_types.at(arg_i)); + LLVMValueRef arg_ptr = LLVMBuildStructGEP(g->builder, casted_frame, arg_calc.field_index - 1, ""); + gen_assign_raw(g, arg_ptr, get_pointer_to_type(g, gen_param_types.at(arg_i), true), + gen_param_values.at(arg_i)); + } + + if (instruction->modifier == CallModifierAsync) { + gen_resume(g, fn_val, frame_result_loc, ResumeIdCall); + if (instruction->new_stack != nullptr) { + return LLVMBuildBitCast(g->builder, frame_result_loc, + get_llvm_type(g, instruction->base.value->type), ""); + } + return nullptr; + } else if (instruction->modifier == CallModifierNoSuspend && !fn_is_async(g->cur_fn)) { + gen_resume(g, fn_val, frame_result_loc, ResumeIdCall); + + if (ir_want_runtime_safety(g, &instruction->base)) { + LLVMValueRef awaiter_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, + frame_awaiter_index, ""); + LLVMValueRef all_ones = LLVMConstAllOnes(usize_type_ref); + LLVMValueRef prev_val = gen_maybe_atomic_op(g, LLVMAtomicRMWBinOpXchg, awaiter_ptr, + all_ones, LLVMAtomicOrderingRelease); + LLVMValueRef ok_val = LLVMBuildICmp(g->builder, LLVMIntEQ, prev_val, all_ones, ""); + + LLVMBasicBlockRef bad_block = LLVMAppendBasicBlock(g->cur_fn_val, "NoSuspendPanic"); + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "NoSuspendOk"); + LLVMBuildCondBr(g->builder, ok_val, ok_block, bad_block); + + // The async function suspended, but this nosuspend call asserted it wouldn't. + LLVMPositionBuilderAtEnd(g->builder, bad_block); + gen_safety_crash(g, PanicMsgIdBadNoSuspendCall); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + } + + ZigType *result_type = instruction->base.value->type; + ZigType *ptr_result_type = get_pointer_to_type(g, result_type, true); + return gen_await_early_return(g, &instruction->base, frame_result_loc, + result_type, ptr_result_type, result_loc, true); + } else { + ZigType *ptr_result_type = get_pointer_to_type(g, src_return_type, true); + + LLVMBasicBlockRef call_bb = gen_suspend_begin(g, "CallResume"); + + LLVMValueRef call_inst = gen_resume(g, fn_val, frame_result_loc, ResumeIdCall); + set_tail_call_if_appropriate(g, call_inst); + LLVMBuildRetVoid(g->builder); + + LLVMPositionBuilderAtEnd(g->builder, call_bb); + gen_assert_resume_id(g, &instruction->base, ResumeIdReturn, PanicMsgIdResumedAnAwaitingFn, nullptr); + render_async_var_decls(g, instruction->base.base.scope); + + if (!type_has_bits(g, src_return_type)) + return nullptr; + + if (result_loc != nullptr) { + if (instruction->result_loc->id == IrInstGenIdReturnPtr) { + instruction->base.spill = nullptr; + return g->cur_ret_ptr; + } else { + return get_handle_value(g, result_loc, src_return_type, ptr_result_type); + } + } + + if (need_frame_ptr_ptr_spill) { + LLVMValueRef frame_slice_ptr = ir_llvm_value(g, instruction->new_stack); + LLVMValueRef frame_ptr_ptr = LLVMBuildStructGEP(g->builder, frame_slice_ptr, slice_ptr_index, ""); + frame_result_loc_uncasted = LLVMBuildLoad(g->builder, frame_ptr_ptr, ""); + } + if (frame_result_loc_uncasted != nullptr) { + if (instruction->fn_entry != nullptr) { + frame_result_loc = LLVMBuildBitCast(g->builder, frame_result_loc_uncasted, + LLVMPointerType(get_llvm_type(g, instruction->fn_entry->frame_type), 0), ""); + } else { + frame_result_loc = LLVMBuildBitCast(g->builder, frame_result_loc_uncasted, + get_llvm_type(g, anyframe_type), ""); + } + } + + LLVMValueRef result_ptr = LLVMBuildStructGEP(g->builder, frame_result_loc, frame_ret_start + 2, ""); + return LLVMBuildLoad(g->builder, result_ptr, ""); + } + } + + if (instruction->new_stack == nullptr || instruction->is_async_call_builtin) { + result = ZigLLVMBuildCall(g->builder, fn_val, + gen_param_values.items, (unsigned)gen_param_values.length, llvm_cc, call_attr, ""); + } else if (instruction->modifier == CallModifierAsync) { + zig_panic("TODO @asyncCall of non-async function"); + } else { + LLVMValueRef new_stack_addr = get_new_stack_addr(g, ir_llvm_value(g, instruction->new_stack)); + LLVMValueRef old_stack_ref; + if (src_return_type->id != ZigTypeIdUnreachable) { + LLVMValueRef stacksave_fn_val = get_stacksave_fn_val(g); + old_stack_ref = LLVMBuildCall(g->builder, stacksave_fn_val, nullptr, 0, ""); + } + gen_set_stack_pointer(g, new_stack_addr); + result = ZigLLVMBuildCall(g->builder, fn_val, + gen_param_values.items, (unsigned)gen_param_values.length, llvm_cc, call_attr, ""); + if (src_return_type->id != ZigTypeIdUnreachable) { + LLVMValueRef stackrestore_fn_val = get_stackrestore_fn_val(g); + LLVMBuildCall(g->builder, stackrestore_fn_val, &old_stack_ref, 1, ""); + } + } + + if (src_return_type->id == ZigTypeIdUnreachable) { + return LLVMBuildUnreachable(g->builder); + } else if (!ret_has_bits) { + return nullptr; + } else if (first_arg_ret) { + set_call_instr_sret(g, result); + return result_loc; + } else if (handle_is_ptr(g, src_return_type)) { + LLVMValueRef store_instr = LLVMBuildStore(g->builder, result, result_loc); + LLVMSetAlignment(store_instr, get_ptr_align(g, instruction->result_loc->value->type)); + return result_loc; + } else if (!callee_is_async && instruction->modifier == CallModifierAsync) { + LLVMBuildStore(g->builder, result, ret_ptr); + return result_loc; + } else { + return result; + } +} + +static LLVMValueRef ir_render_struct_field_ptr(CodeGen *g, IrExecutableGen *executable, + IrInstGenStructFieldPtr *instruction) +{ + Error err; + + if (instruction->base.value->special != ConstValSpecialRuntime) + return nullptr; + + LLVMValueRef struct_ptr = ir_llvm_value(g, instruction->struct_ptr); + // not necessarily a pointer. could be ZigTypeIdStruct + ZigType *struct_ptr_type = instruction->struct_ptr->value->type; + TypeStructField *field = instruction->field; + + if (!type_has_bits(g, field->type_entry)) + return nullptr; + + if (struct_ptr_type->id == ZigTypeIdPointer && + struct_ptr_type->data.pointer.host_int_bytes != 0) + { + return struct_ptr; + } + + ZigType *struct_type; + if (struct_ptr_type->id == ZigTypeIdPointer) { + if (struct_ptr_type->data.pointer.inferred_struct_field != nullptr) { + struct_type = struct_ptr_type->data.pointer.inferred_struct_field->inferred_struct_type; + } else { + struct_type = struct_ptr_type->data.pointer.child_type; + } + } else { + struct_type = struct_ptr_type; + } + + if ((err = type_resolve(g, struct_type, ResolveStatusLLVMFull))) + codegen_report_errors_and_exit(g); + + ir_assert(field->gen_index != SIZE_MAX, &instruction->base); + LLVMValueRef field_ptr_val = LLVMBuildStructGEP(g->builder, struct_ptr, (unsigned)field->gen_index, ""); + ZigType *res_type = instruction->base.value->type; + ir_assert(res_type->id == ZigTypeIdPointer, &instruction->base); + if (res_type->data.pointer.host_int_bytes != 0) { + // We generate packed structs with get_llvm_type_of_n_bytes, which is + // u8 for 1 byte or [n]u8 for multiple bytes. But the pointer to the type + // is supposed to be a pointer to the integer. So we bitcast it here. + LLVMTypeRef int_elem_type = LLVMIntType(8*res_type->data.pointer.host_int_bytes); + LLVMTypeRef integer_ptr_type = LLVMPointerType(int_elem_type, 0); + return LLVMBuildBitCast(g->builder, field_ptr_val, integer_ptr_type, ""); + } + return field_ptr_val; +} + +static LLVMValueRef ir_render_union_field_ptr(CodeGen *g, IrExecutableGen *executable, + IrInstGenUnionFieldPtr *instruction) +{ + if (instruction->base.value->special != ConstValSpecialRuntime) + return nullptr; + + ZigType *union_ptr_type = instruction->union_ptr->value->type; + assert(union_ptr_type->id == ZigTypeIdPointer); + ZigType *union_type = union_ptr_type->data.pointer.child_type; + assert(union_type->id == ZigTypeIdUnion); + + TypeUnionField *field = instruction->field; + + if (!type_has_bits(g, field->type_entry)) { + ZigType *tag_type = union_type->data.unionation.tag_type; + if (!instruction->initializing || tag_type == nullptr || !type_has_bits(g, tag_type)) + return nullptr; + + // The field has no bits but we still have to change the discriminant + // value here + LLVMValueRef union_ptr = ir_llvm_value(g, instruction->union_ptr); + + LLVMTypeRef tag_type_ref = get_llvm_type(g, tag_type); + LLVMValueRef tag_field_ptr = nullptr; + if (union_type->data.unionation.gen_field_count == 0) { + assert(union_type->data.unionation.gen_tag_index == SIZE_MAX); + // The whole union is collapsed into the discriminant + tag_field_ptr = LLVMBuildBitCast(g->builder, union_ptr, + LLVMPointerType(tag_type_ref, 0), ""); + } else { + assert(union_type->data.unionation.gen_tag_index != SIZE_MAX); + tag_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, + union_type->data.unionation.gen_tag_index, ""); + } + + LLVMValueRef tag_value = bigint_to_llvm_const(tag_type_ref, + &field->enum_field->value); + assert(tag_field_ptr != nullptr); + gen_store_untyped(g, tag_value, tag_field_ptr, 0, false); + + return nullptr; + } + + LLVMValueRef union_ptr = ir_llvm_value(g, instruction->union_ptr); + LLVMTypeRef field_type_ref = LLVMPointerType(get_llvm_type(g, field->type_entry), 0); + + if (union_type->data.unionation.gen_tag_index == SIZE_MAX) { + LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, 0, ""); + LLVMValueRef bitcasted_union_field_ptr = LLVMBuildBitCast(g->builder, union_field_ptr, field_type_ref, ""); + return bitcasted_union_field_ptr; + } + + if (instruction->initializing) { + LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, union_type->data.unionation.gen_tag_index, ""); + LLVMValueRef tag_value = bigint_to_llvm_const(get_llvm_type(g, union_type->data.unionation.tag_type), + &field->enum_field->value); + gen_store_untyped(g, tag_value, tag_field_ptr, 0, false); + } else if (instruction->safety_check_on && ir_want_runtime_safety(g, &instruction->base)) { + LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, union_type->data.unionation.gen_tag_index, ""); + LLVMValueRef tag_value = gen_load_untyped(g, tag_field_ptr, 0, false, ""); + + + LLVMValueRef expected_tag_value = bigint_to_llvm_const(get_llvm_type(g, union_type->data.unionation.tag_type), + &field->enum_field->value); + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnionCheckOk"); + LLVMBasicBlockRef bad_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnionCheckFail"); + LLVMValueRef ok_val = LLVMBuildICmp(g->builder, LLVMIntEQ, tag_value, expected_tag_value, ""); + LLVMBuildCondBr(g->builder, ok_val, ok_block, bad_block); + + LLVMPositionBuilderAtEnd(g->builder, bad_block); + gen_safety_crash(g, PanicMsgIdBadUnionField); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + } + + LLVMValueRef union_field_ptr = LLVMBuildStructGEP(g->builder, union_ptr, + union_type->data.unionation.gen_union_index, ""); + LLVMValueRef bitcasted_union_field_ptr = LLVMBuildBitCast(g->builder, union_field_ptr, field_type_ref, ""); + return bitcasted_union_field_ptr; +} + +static size_t find_asm_index(CodeGen *g, AstNode *node, AsmToken *tok, Buf *src_template) { + const char *ptr = buf_ptr(src_template) + tok->start + 2; + size_t len = tok->end - tok->start - 2; + size_t result = 0; + for (size_t i = 0; i < node->data.asm_expr.output_list.length; i += 1, result += 1) { + AsmOutput *asm_output = node->data.asm_expr.output_list.at(i); + if (buf_eql_mem(asm_output->asm_symbolic_name, ptr, len)) { + return result; + } + } + for (size_t i = 0; i < node->data.asm_expr.input_list.length; i += 1, result += 1) { + AsmInput *asm_input = node->data.asm_expr.input_list.at(i); + if (buf_eql_mem(asm_input->asm_symbolic_name, ptr, len)) { + return result; + } + } + return SIZE_MAX; +} + +static LLVMValueRef ir_render_asm_gen(CodeGen *g, IrExecutableGen *executable, IrInstGenAsm *instruction) { + AstNode *asm_node = instruction->base.base.source_node; + assert(asm_node->type == NodeTypeAsmExpr); + AstNodeAsmExpr *asm_expr = &asm_node->data.asm_expr; + + Buf *src_template = instruction->asm_template; + + Buf llvm_template = BUF_INIT; + buf_resize(&llvm_template, 0); + + for (size_t token_i = 0; token_i < instruction->token_list_len; token_i += 1) { + AsmToken *asm_token = &instruction->token_list[token_i]; + switch (asm_token->id) { + case AsmTokenIdTemplate: + for (size_t offset = asm_token->start; offset < asm_token->end; offset += 1) { + uint8_t c = *((uint8_t*)(buf_ptr(src_template) + offset)); + if (c == '$') { + buf_append_str(&llvm_template, "$$"); + } else { + buf_append_char(&llvm_template, c); + } + } + break; + case AsmTokenIdPercent: + buf_append_char(&llvm_template, '%'); + break; + case AsmTokenIdVar: + { + size_t index = find_asm_index(g, asm_node, asm_token, src_template); + assert(index < SIZE_MAX); + buf_appendf(&llvm_template, "$%" ZIG_PRI_usize "", index); + break; + } + case AsmTokenIdUniqueId: + buf_append_str(&llvm_template, "${:uid}"); + break; + } + } + + Buf constraint_buf = BUF_INIT; + buf_resize(&constraint_buf, 0); + + assert(instruction->return_count == 0 || instruction->return_count == 1); + + size_t total_constraint_count = asm_expr->output_list.length + + asm_expr->input_list.length + + asm_expr->clobber_list.length; + size_t input_and_output_count = asm_expr->output_list.length + + asm_expr->input_list.length - + instruction->return_count; + size_t total_index = 0; + size_t param_index = 0; + LLVMTypeRef *param_types = heap::c_allocator.allocate(input_and_output_count); + LLVMValueRef *param_values = heap::c_allocator.allocate(input_and_output_count); + for (size_t i = 0; i < asm_expr->output_list.length; i += 1, total_index += 1) { + AsmOutput *asm_output = asm_expr->output_list.at(i); + bool is_return = (asm_output->return_type != nullptr); + assert(*buf_ptr(asm_output->constraint) == '='); + // LLVM uses commas internally to separate different constraints, + // alternative constraints are achieved with pipes. + // We still allow the user to use commas in a way that is similar + // to GCC's inline assembly. + // http://llvm.org/docs/LangRef.html#constraint-codes + buf_replace(asm_output->constraint, ',', '|'); + + if (is_return) { + buf_appendf(&constraint_buf, "=%s", buf_ptr(asm_output->constraint) + 1); + } else { + buf_appendf(&constraint_buf, "=*%s", buf_ptr(asm_output->constraint) + 1); + } + if (total_index + 1 < total_constraint_count) { + buf_append_char(&constraint_buf, ','); + } + + if (!is_return) { + ZigVar *variable = instruction->output_vars[i]; + assert(variable); + param_types[param_index] = LLVMTypeOf(variable->value_ref); + param_values[param_index] = variable->value_ref; + param_index += 1; + } + } + for (size_t i = 0; i < asm_expr->input_list.length; i += 1, total_index += 1, param_index += 1) { + AsmInput *asm_input = asm_expr->input_list.at(i); + buf_replace(asm_input->constraint, ',', '|'); + IrInstGen *ir_input = instruction->input_list[i]; + buf_append_buf(&constraint_buf, asm_input->constraint); + if (total_index + 1 < total_constraint_count) { + buf_append_char(&constraint_buf, ','); + } + + ZigType *const type = ir_input->value->type; + LLVMTypeRef type_ref = get_llvm_type(g, type); + LLVMValueRef value_ref = ir_llvm_value(g, ir_input); + // Handle integers of non pot bitsize by widening them. + if (type->id == ZigTypeIdInt) { + const size_t bitsize = type->data.integral.bit_count; + if (bitsize < 8 || !is_power_of_2(bitsize)) { + const bool is_signed = type->data.integral.is_signed; + const size_t wider_bitsize = bitsize < 8 ? 8 : round_to_next_power_of_2(bitsize); + ZigType *const wider_type = get_int_type(g, is_signed, wider_bitsize); + type_ref = get_llvm_type(g, wider_type); + value_ref = gen_widen_or_shorten(g, false, type, wider_type, value_ref); + } + } + + param_types[param_index] = type_ref; + param_values[param_index] = value_ref; + } + for (size_t i = 0; i < asm_expr->clobber_list.length; i += 1, total_index += 1) { + Buf *clobber_buf = asm_expr->clobber_list.at(i); + buf_appendf(&constraint_buf, "~{%s}", buf_ptr(clobber_buf)); + if (total_index + 1 < total_constraint_count) { + buf_append_char(&constraint_buf, ','); + } + } + + LLVMTypeRef ret_type; + if (instruction->return_count == 0) { + ret_type = LLVMVoidType(); + } else { + ret_type = get_llvm_type(g, instruction->base.value->type); + } + LLVMTypeRef function_type = LLVMFunctionType(ret_type, param_types, (unsigned)input_and_output_count, false); + + bool is_volatile = instruction->has_side_effects || (asm_expr->output_list.length == 0); + LLVMValueRef asm_fn = LLVMGetInlineAsm(function_type, buf_ptr(&llvm_template), buf_len(&llvm_template), + buf_ptr(&constraint_buf), buf_len(&constraint_buf), is_volatile, false, LLVMInlineAsmDialectATT); + + return LLVMBuildCall(g->builder, asm_fn, param_values, (unsigned)input_and_output_count, ""); +} + +static LLVMValueRef gen_non_null_bit(CodeGen *g, ZigType *maybe_type, LLVMValueRef maybe_handle) { + assert(maybe_type->id == ZigTypeIdOptional || + (maybe_type->id == ZigTypeIdPointer && maybe_type->data.pointer.allow_zero)); + + ZigType *child_type = maybe_type->data.maybe.child_type; + if (!type_has_bits(g, child_type)) + return maybe_handle; + + bool is_scalar = !handle_is_ptr(g, maybe_type); + if (is_scalar) + return LLVMBuildICmp(g->builder, LLVMIntNE, maybe_handle, LLVMConstNull(get_llvm_type(g, maybe_type)), ""); + + LLVMValueRef maybe_field_ptr = LLVMBuildStructGEP(g->builder, maybe_handle, maybe_null_index, ""); + return gen_load_untyped(g, maybe_field_ptr, 0, false, ""); +} + +static LLVMValueRef ir_render_test_non_null(CodeGen *g, IrExecutableGen *executable, + IrInstGenTestNonNull *instruction) +{ + return gen_non_null_bit(g, instruction->value->value->type, ir_llvm_value(g, instruction->value)); +} + +static LLVMValueRef ir_render_optional_unwrap_ptr(CodeGen *g, IrExecutableGen *executable, + IrInstGenOptionalUnwrapPtr *instruction) +{ + if (instruction->base.value->special != ConstValSpecialRuntime) + return nullptr; + + ZigType *ptr_type = instruction->base_ptr->value->type; + assert(ptr_type->id == ZigTypeIdPointer); + ZigType *maybe_type = ptr_type->data.pointer.child_type; + assert(maybe_type->id == ZigTypeIdOptional); + ZigType *child_type = maybe_type->data.maybe.child_type; + LLVMValueRef base_ptr = ir_llvm_value(g, instruction->base_ptr); + if (instruction->safety_check_on && ir_want_runtime_safety(g, &instruction->base)) { + LLVMValueRef maybe_handle = get_handle_value(g, base_ptr, maybe_type, ptr_type); + LLVMValueRef non_null_bit = gen_non_null_bit(g, maybe_type, maybe_handle); + LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapOptionalFail"); + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapOptionalOk"); + LLVMBuildCondBr(g->builder, non_null_bit, ok_block, fail_block); + + LLVMPositionBuilderAtEnd(g->builder, fail_block); + gen_safety_crash(g, PanicMsgIdUnwrapOptionalFail); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + } + if (!type_has_bits(g, child_type)) { + if (instruction->initializing) { + LLVMValueRef non_null_bit = LLVMConstInt(LLVMInt1Type(), 1, false); + gen_store_untyped(g, non_null_bit, base_ptr, 0, false); + } + return nullptr; + } else { + bool is_scalar = !handle_is_ptr(g, maybe_type); + if (is_scalar) { + return base_ptr; + } else { + LLVMValueRef optional_struct_ref = get_handle_value(g, base_ptr, maybe_type, ptr_type); + if (instruction->initializing) { + LLVMValueRef non_null_bit_ptr = LLVMBuildStructGEP(g->builder, optional_struct_ref, + maybe_null_index, ""); + LLVMValueRef non_null_bit = LLVMConstInt(LLVMInt1Type(), 1, false); + gen_store_untyped(g, non_null_bit, non_null_bit_ptr, 0, false); + } + return LLVMBuildStructGEP(g->builder, optional_struct_ref, maybe_child_index, ""); + } + } +} + +static LLVMValueRef get_int_builtin_fn(CodeGen *g, ZigType *expr_type, BuiltinFnId fn_id) { + bool is_vector = expr_type->id == ZigTypeIdVector; + ZigType *int_type = is_vector ? expr_type->data.vector.elem_type : expr_type; + assert(int_type->id == ZigTypeIdInt); + uint32_t vector_len = is_vector ? expr_type->data.vector.len : 0; + ZigLLVMFnKey key = {}; + const char *fn_name; + uint32_t n_args; + if (fn_id == BuiltinFnIdCtz) { + fn_name = "cttz"; + n_args = 2; + key.id = ZigLLVMFnIdCtz; + key.data.ctz.bit_count = (uint32_t)int_type->data.integral.bit_count; + } else if (fn_id == BuiltinFnIdClz) { + fn_name = "ctlz"; + n_args = 2; + key.id = ZigLLVMFnIdClz; + key.data.clz.bit_count = (uint32_t)int_type->data.integral.bit_count; + } else if (fn_id == BuiltinFnIdPopCount) { + fn_name = "ctpop"; + n_args = 1; + key.id = ZigLLVMFnIdPopCount; + key.data.pop_count.bit_count = (uint32_t)int_type->data.integral.bit_count; + } else if (fn_id == BuiltinFnIdBswap) { + fn_name = "bswap"; + n_args = 1; + key.id = ZigLLVMFnIdBswap; + key.data.bswap.bit_count = (uint32_t)int_type->data.integral.bit_count; + key.data.bswap.vector_len = vector_len; + } else if (fn_id == BuiltinFnIdBitReverse) { + fn_name = "bitreverse"; + n_args = 1; + key.id = ZigLLVMFnIdBitReverse; + key.data.bit_reverse.bit_count = (uint32_t)int_type->data.integral.bit_count; + } else { + zig_unreachable(); + } + + auto existing_entry = g->llvm_fn_table.maybe_get(key); + if (existing_entry) + return existing_entry->value; + + char llvm_name[64]; + if (is_vector) + sprintf(llvm_name, "llvm.%s.v%" PRIu32 "i%" PRIu32, fn_name, vector_len, int_type->data.integral.bit_count); + else + sprintf(llvm_name, "llvm.%s.i%" PRIu32, fn_name, int_type->data.integral.bit_count); + LLVMTypeRef param_types[] = { + get_llvm_type(g, expr_type), + LLVMInt1Type(), + }; + LLVMTypeRef fn_type = LLVMFunctionType(get_llvm_type(g, expr_type), param_types, n_args, false); + LLVMValueRef fn_val = LLVMAddFunction(g->module, llvm_name, fn_type); + assert(LLVMGetIntrinsicID(fn_val)); + + g->llvm_fn_table.put(key, fn_val); + + return fn_val; +} + +static LLVMValueRef ir_render_clz(CodeGen *g, IrExecutableGen *executable, IrInstGenClz *instruction) { + ZigType *int_type = instruction->op->value->type; + LLVMValueRef fn_val = get_int_builtin_fn(g, int_type, BuiltinFnIdClz); + LLVMValueRef operand = ir_llvm_value(g, instruction->op); + LLVMValueRef params[] { + operand, + LLVMConstNull(LLVMInt1Type()), + }; + LLVMValueRef wrong_size_int = LLVMBuildCall(g->builder, fn_val, params, 2, ""); + return gen_widen_or_shorten(g, false, int_type, instruction->base.value->type, wrong_size_int); +} + +static LLVMValueRef ir_render_ctz(CodeGen *g, IrExecutableGen *executable, IrInstGenCtz *instruction) { + ZigType *int_type = instruction->op->value->type; + LLVMValueRef fn_val = get_int_builtin_fn(g, int_type, BuiltinFnIdCtz); + LLVMValueRef operand = ir_llvm_value(g, instruction->op); + LLVMValueRef params[] { + operand, + LLVMConstNull(LLVMInt1Type()), + }; + LLVMValueRef wrong_size_int = LLVMBuildCall(g->builder, fn_val, params, 2, ""); + return gen_widen_or_shorten(g, false, int_type, instruction->base.value->type, wrong_size_int); +} + +static LLVMValueRef ir_render_shuffle_vector(CodeGen *g, IrExecutableGen *executable, IrInstGenShuffleVector *instruction) { + uint64_t len_a = instruction->a->value->type->data.vector.len; + uint64_t len_mask = instruction->mask->value->type->data.vector.len; + + // LLVM uses integers larger than the length of the first array to + // index into the second array. This was deemed unnecessarily fragile + // when changing code, so Zig uses negative numbers to index the + // second vector. These start at -1 and go down, and are easiest to use + // with the ~ operator. Here we convert between the two formats. + IrInstGen *mask = instruction->mask; + LLVMValueRef *values = heap::c_allocator.allocate(len_mask); + for (uint64_t i = 0; i < len_mask; i++) { + if (mask->value->data.x_array.data.s_none.elements[i].special == ConstValSpecialUndef) { + values[i] = LLVMGetUndef(LLVMInt32Type()); + } else { + int32_t v = bigint_as_signed(&mask->value->data.x_array.data.s_none.elements[i].data.x_bigint); + uint32_t index_val = (v >= 0) ? (uint32_t)v : (uint32_t)~v + (uint32_t)len_a; + values[i] = LLVMConstInt(LLVMInt32Type(), index_val, false); + } + } + + LLVMValueRef llvm_mask_value = LLVMConstVector(values, len_mask); + heap::c_allocator.deallocate(values, len_mask); + + return LLVMBuildShuffleVector(g->builder, + ir_llvm_value(g, instruction->a), + ir_llvm_value(g, instruction->b), + llvm_mask_value, ""); +} + +static LLVMValueRef ir_render_splat(CodeGen *g, IrExecutableGen *executable, IrInstGenSplat *instruction) { + ZigType *result_type = instruction->base.value->type; + ir_assert(result_type->id == ZigTypeIdVector, &instruction->base); + uint32_t len = result_type->data.vector.len; + LLVMTypeRef op_llvm_type = LLVMVectorType(get_llvm_type(g, instruction->scalar->value->type), 1); + LLVMTypeRef mask_llvm_type = LLVMVectorType(LLVMInt32Type(), len); + LLVMValueRef undef_vector = LLVMGetUndef(op_llvm_type); + LLVMValueRef op_vector = LLVMBuildInsertElement(g->builder, undef_vector, + ir_llvm_value(g, instruction->scalar), LLVMConstInt(LLVMInt32Type(), 0, false), ""); + return LLVMBuildShuffleVector(g->builder, op_vector, undef_vector, LLVMConstNull(mask_llvm_type), ""); +} + +static LLVMValueRef ir_render_pop_count(CodeGen *g, IrExecutableGen *executable, IrInstGenPopCount *instruction) { + ZigType *int_type = instruction->op->value->type; + LLVMValueRef fn_val = get_int_builtin_fn(g, int_type, BuiltinFnIdPopCount); + LLVMValueRef operand = ir_llvm_value(g, instruction->op); + LLVMValueRef wrong_size_int = LLVMBuildCall(g->builder, fn_val, &operand, 1, ""); + return gen_widen_or_shorten(g, false, int_type, instruction->base.value->type, wrong_size_int); +} + +static LLVMValueRef ir_render_switch_br(CodeGen *g, IrExecutableGen *executable, IrInstGenSwitchBr *instruction) { + ZigType *target_type = instruction->target_value->value->type; + LLVMBasicBlockRef else_block = instruction->else_block->llvm_block; + + LLVMValueRef target_value = ir_llvm_value(g, instruction->target_value); + if (target_type->id == ZigTypeIdPointer) { + const ZigType *usize = g->builtin_types.entry_usize; + target_value = LLVMBuildPtrToInt(g->builder, target_value, usize->llvm_type, ""); + } + + LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, target_value, else_block, + (unsigned)instruction->case_count); + + for (size_t i = 0; i < instruction->case_count; i += 1) { + IrInstGenSwitchBrCase *this_case = &instruction->cases[i]; + + LLVMValueRef case_value = ir_llvm_value(g, this_case->value); + if (target_type->id == ZigTypeIdPointer) { + const ZigType *usize = g->builtin_types.entry_usize; + case_value = LLVMBuildPtrToInt(g->builder, case_value, usize->llvm_type, ""); + } + + LLVMAddCase(switch_instr, case_value, this_case->block->llvm_block); + } + + return nullptr; +} + +static LLVMValueRef ir_render_phi(CodeGen *g, IrExecutableGen *executable, IrInstGenPhi *instruction) { + if (!type_has_bits(g, instruction->base.value->type)) + return nullptr; + + LLVMTypeRef phi_type; + if (handle_is_ptr(g, instruction->base.value->type)) { + phi_type = LLVMPointerType(get_llvm_type(g,instruction->base.value->type), 0); + } else { + phi_type = get_llvm_type(g, instruction->base.value->type); + } + + LLVMValueRef phi = LLVMBuildPhi(g->builder, phi_type, ""); + LLVMValueRef *incoming_values = heap::c_allocator.allocate(instruction->incoming_count); + LLVMBasicBlockRef *incoming_blocks = heap::c_allocator.allocate(instruction->incoming_count); + for (size_t i = 0; i < instruction->incoming_count; i += 1) { + incoming_values[i] = ir_llvm_value(g, instruction->incoming_values[i]); + incoming_blocks[i] = instruction->incoming_blocks[i]->llvm_exit_block; + } + LLVMAddIncoming(phi, incoming_values, incoming_blocks, (unsigned)instruction->incoming_count); + return phi; +} + +static LLVMValueRef ir_render_ref(CodeGen *g, IrExecutableGen *executable, IrInstGenRef *instruction) { + if (!type_has_bits(g, instruction->base.value->type)) { + return nullptr; + } + if (instruction->operand->id == IrInstGenIdCall) { + IrInstGenCall *call = reinterpret_cast(instruction->operand); + if (call->result_loc != nullptr) { + return ir_llvm_value(g, call->result_loc); + } + } + LLVMValueRef value = ir_llvm_value(g, instruction->operand); + if (handle_is_ptr(g, instruction->operand->value->type)) { + return value; + } else { + LLVMValueRef result_loc = ir_llvm_value(g, instruction->result_loc); + gen_store_untyped(g, value, result_loc, 0, false); + return result_loc; + } +} + +static LLVMValueRef ir_render_err_name(CodeGen *g, IrExecutableGen *executable, IrInstGenErrName *instruction) { + assert(g->generate_error_name_table); + + if (g->errors_by_index.length == 1) { + LLVMBuildUnreachable(g->builder); + return nullptr; + } + + LLVMValueRef err_val = ir_llvm_value(g, instruction->value); + if (ir_want_runtime_safety(g, &instruction->base)) { + LLVMValueRef zero = LLVMConstNull(LLVMTypeOf(err_val)); + LLVMValueRef end_val = LLVMConstInt(LLVMTypeOf(err_val), g->errors_by_index.length, false); + add_bounds_check(g, err_val, LLVMIntNE, zero, LLVMIntULT, end_val); + } + + LLVMValueRef indices[] = { + LLVMConstNull(g->builtin_types.entry_usize->llvm_type), + err_val, + }; + return LLVMBuildInBoundsGEP(g->builder, g->err_name_table, indices, 2, ""); +} + +static LLVMValueRef get_enum_tag_name_function(CodeGen *g, ZigType *enum_type) { + assert(enum_type->id == ZigTypeIdEnum); + if (enum_type->data.enumeration.name_function) + return enum_type->data.enumeration.name_function; + + ZigType *u8_ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, false, false, + PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0, false); + ZigType *u8_slice_type = get_slice_type(g, u8_ptr_type); + ZigType *tag_int_type = enum_type->data.enumeration.tag_int_type; + + LLVMTypeRef tag_int_llvm_type = get_llvm_type(g, tag_int_type); + LLVMTypeRef fn_type_ref = LLVMFunctionType(LLVMPointerType(get_llvm_type(g, u8_slice_type), 0), + &tag_int_llvm_type, 1, false); + + const char *fn_name = get_mangled_name(g, + buf_ptr(buf_sprintf("__zig_tag_name_%s", buf_ptr(&enum_type->name)))); + LLVMValueRef fn_val = LLVMAddFunction(g->module, fn_name, fn_type_ref); + LLVMSetLinkage(fn_val, LLVMInternalLinkage); + ZigLLVMFunctionSetCallingConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified)); + addLLVMFnAttr(fn_val, "nounwind"); + add_uwtable_attr(g, fn_val); + if (codegen_have_frame_pointer(g)) { + ZigLLVMAddFunctionAttr(fn_val, "frame-pointer", "all"); + } + + LLVMBasicBlockRef prev_block = LLVMGetInsertBlock(g->builder); + LLVMValueRef prev_debug_location = LLVMGetCurrentDebugLocation(g->builder); + ZigFn *prev_cur_fn = g->cur_fn; + LLVMValueRef prev_cur_fn_val = g->cur_fn_val; + + LLVMBasicBlockRef entry_block = LLVMAppendBasicBlock(fn_val, "Entry"); + LLVMPositionBuilderAtEnd(g->builder, entry_block); + ZigLLVMClearCurrentDebugLocation(g->builder); + g->cur_fn = nullptr; + g->cur_fn_val = fn_val; + + size_t field_count = enum_type->data.enumeration.src_field_count; + LLVMBasicBlockRef bad_value_block = LLVMAppendBasicBlock(g->cur_fn_val, "BadValue"); + LLVMValueRef tag_int_value = LLVMGetParam(fn_val, 0); + LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, tag_int_value, bad_value_block, field_count); + + + ZigType *usize = g->builtin_types.entry_usize; + LLVMValueRef array_ptr_indices[] = { + LLVMConstNull(usize->llvm_type), + LLVMConstNull(usize->llvm_type), + }; + + HashMap occupied_tag_values = {}; + occupied_tag_values.init(field_count); + + for (size_t field_i = 0; field_i < field_count; field_i += 1) { + TypeEnumField *type_enum_field = &enum_type->data.enumeration.fields[field_i]; + + Buf *name = type_enum_field->name; + auto entry = occupied_tag_values.put_unique(type_enum_field->value, name); + if (entry != nullptr) { + continue; + } + + LLVMValueRef str_init = LLVMConstString(buf_ptr(name), (unsigned)buf_len(name), true); + LLVMValueRef str_global = LLVMAddGlobal(g->module, LLVMTypeOf(str_init), ""); + LLVMSetInitializer(str_global, str_init); + LLVMSetLinkage(str_global, LLVMPrivateLinkage); + LLVMSetGlobalConstant(str_global, true); + LLVMSetUnnamedAddr(str_global, true); + LLVMSetAlignment(str_global, LLVMABIAlignmentOfType(g->target_data_ref, LLVMTypeOf(str_init))); + + LLVMValueRef fields[] = { + LLVMConstGEP(str_global, array_ptr_indices, 2), + LLVMConstInt(g->builtin_types.entry_usize->llvm_type, buf_len(name), false), + }; + LLVMValueRef slice_init_value = LLVMConstNamedStruct(get_llvm_type(g, u8_slice_type), fields, 2); + + LLVMValueRef slice_global = LLVMAddGlobal(g->module, LLVMTypeOf(slice_init_value), ""); + LLVMSetInitializer(slice_global, slice_init_value); + LLVMSetLinkage(slice_global, LLVMPrivateLinkage); + LLVMSetGlobalConstant(slice_global, true); + LLVMSetUnnamedAddr(slice_global, true); + LLVMSetAlignment(slice_global, LLVMABIAlignmentOfType(g->target_data_ref, LLVMTypeOf(slice_init_value))); + + LLVMBasicBlockRef return_block = LLVMAppendBasicBlock(g->cur_fn_val, "Name"); + LLVMValueRef this_tag_int_value = bigint_to_llvm_const(get_llvm_type(g, tag_int_type), + &enum_type->data.enumeration.fields[field_i].value); + LLVMAddCase(switch_instr, this_tag_int_value, return_block); + + LLVMPositionBuilderAtEnd(g->builder, return_block); + LLVMBuildRet(g->builder, slice_global); + } + occupied_tag_values.deinit(); + + LLVMPositionBuilderAtEnd(g->builder, bad_value_block); + if (g->build_mode == BuildModeDebug || g->build_mode == BuildModeSafeRelease) { + gen_safety_crash(g, PanicMsgIdBadEnumValue); + } else { + LLVMBuildUnreachable(g->builder); + } + + g->cur_fn = prev_cur_fn; + g->cur_fn_val = prev_cur_fn_val; + LLVMPositionBuilderAtEnd(g->builder, prev_block); + if (!g->strip_debug_symbols) { + LLVMSetCurrentDebugLocation(g->builder, prev_debug_location); + } + + enum_type->data.enumeration.name_function = fn_val; + return fn_val; +} + +static LLVMValueRef ir_render_enum_tag_name(CodeGen *g, IrExecutableGen *executable, + IrInstGenTagName *instruction) +{ + ZigType *enum_type = instruction->target->value->type; + assert(enum_type->id == ZigTypeIdEnum); + + LLVMValueRef enum_name_function = get_enum_tag_name_function(g, enum_type); + + LLVMValueRef enum_tag_value = ir_llvm_value(g, instruction->target); + return ZigLLVMBuildCall(g->builder, enum_name_function, &enum_tag_value, 1, + get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_CallAttrAuto, ""); +} + +static LLVMValueRef ir_render_field_parent_ptr(CodeGen *g, IrExecutableGen *executable, + IrInstGenFieldParentPtr *instruction) +{ + ZigType *container_ptr_type = instruction->base.value->type; + assert(container_ptr_type->id == ZigTypeIdPointer); + + ZigType *container_type = container_ptr_type->data.pointer.child_type; + + size_t byte_offset = LLVMOffsetOfElement(g->target_data_ref, + get_llvm_type(g, container_type), instruction->field->gen_index); + + LLVMValueRef field_ptr_val = ir_llvm_value(g, instruction->field_ptr); + + if (byte_offset == 0) { + return LLVMBuildBitCast(g->builder, field_ptr_val, get_llvm_type(g, container_ptr_type), ""); + } else { + ZigType *usize = g->builtin_types.entry_usize; + + LLVMValueRef field_ptr_int = LLVMBuildPtrToInt(g->builder, field_ptr_val, usize->llvm_type, ""); + + LLVMValueRef base_ptr_int = LLVMBuildNUWSub(g->builder, field_ptr_int, + LLVMConstInt(usize->llvm_type, byte_offset, false), ""); + + return LLVMBuildIntToPtr(g->builder, base_ptr_int, get_llvm_type(g, container_ptr_type), ""); + } +} + +static LLVMValueRef ir_render_align_cast(CodeGen *g, IrExecutableGen *executable, IrInstGenAlignCast *instruction) { + LLVMValueRef target_val = ir_llvm_value(g, instruction->target); + assert(target_val); + + bool want_runtime_safety = ir_want_runtime_safety(g, &instruction->base); + if (!want_runtime_safety) { + return target_val; + } + + ZigType *target_type = instruction->base.value->type; + uint32_t align_bytes; + LLVMValueRef ptr_val; + + if (target_type->id == ZigTypeIdPointer) { + align_bytes = get_ptr_align(g, target_type); + ptr_val = target_val; + } else if (target_type->id == ZigTypeIdFn) { + align_bytes = target_type->data.fn.fn_type_id.alignment; + ptr_val = target_val; + } else if (target_type->id == ZigTypeIdOptional && + target_type->data.maybe.child_type->id == ZigTypeIdPointer) + { + align_bytes = get_ptr_align(g, target_type->data.maybe.child_type); + ptr_val = target_val; + } else if (target_type->id == ZigTypeIdOptional && + target_type->data.maybe.child_type->id == ZigTypeIdFn) + { + align_bytes = target_type->data.maybe.child_type->data.fn.fn_type_id.alignment; + ptr_val = target_val; + } else if (target_type->id == ZigTypeIdStruct && + target_type->data.structure.special == StructSpecialSlice) + { + ZigType *slice_ptr_type = target_type->data.structure.fields[slice_ptr_index]->type_entry; + align_bytes = get_ptr_align(g, slice_ptr_type); + + size_t ptr_index = target_type->data.structure.fields[slice_ptr_index]->gen_index; + LLVMValueRef ptr_val_ptr = LLVMBuildStructGEP(g->builder, target_val, (unsigned)ptr_index, ""); + ptr_val = gen_load_untyped(g, ptr_val_ptr, 0, false, ""); + } else { + zig_unreachable(); + } + + assert(align_bytes != 1); + + ZigType *usize = g->builtin_types.entry_usize; + LLVMValueRef ptr_as_int_val = LLVMBuildPtrToInt(g->builder, ptr_val, usize->llvm_type, ""); + LLVMValueRef alignment_minus_1 = LLVMConstInt(usize->llvm_type, align_bytes - 1, false); + LLVMValueRef anded_val = LLVMBuildAnd(g->builder, ptr_as_int_val, alignment_minus_1, ""); + LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, anded_val, LLVMConstNull(usize->llvm_type), ""); + + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "AlignCastOk"); + LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "AlignCastFail"); + + LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); + + LLVMPositionBuilderAtEnd(g->builder, fail_block); + gen_safety_crash(g, PanicMsgIdIncorrectAlignment); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + + return target_val; +} + +static LLVMValueRef ir_render_error_return_trace(CodeGen *g, IrExecutableGen *executable, + IrInstGenErrorReturnTrace *instruction) +{ + bool is_llvm_alloca; + LLVMValueRef cur_err_ret_trace_val = get_cur_err_ret_trace_val(g, instruction->base.base.scope, &is_llvm_alloca); + if (cur_err_ret_trace_val == nullptr) { + return LLVMConstNull(get_llvm_type(g, ptr_to_stack_trace_type(g))); + } + return cur_err_ret_trace_val; +} + +static LLVMAtomicOrdering to_LLVMAtomicOrdering(AtomicOrder atomic_order) { + switch (atomic_order) { + case AtomicOrderUnordered: return LLVMAtomicOrderingUnordered; + case AtomicOrderMonotonic: return LLVMAtomicOrderingMonotonic; + case AtomicOrderAcquire: return LLVMAtomicOrderingAcquire; + case AtomicOrderRelease: return LLVMAtomicOrderingRelease; + case AtomicOrderAcqRel: return LLVMAtomicOrderingAcquireRelease; + case AtomicOrderSeqCst: return LLVMAtomicOrderingSequentiallyConsistent; + } + zig_unreachable(); +} + +static enum ZigLLVM_AtomicRMWBinOp to_ZigLLVMAtomicRMWBinOp(AtomicRmwOp op, bool is_signed, bool is_float) { + switch (op) { + case AtomicRmwOp_xchg: return ZigLLVMAtomicRMWBinOpXchg; + case AtomicRmwOp_add: + return is_float ? ZigLLVMAtomicRMWBinOpFAdd : ZigLLVMAtomicRMWBinOpAdd; + case AtomicRmwOp_sub: + return is_float ? ZigLLVMAtomicRMWBinOpFSub : ZigLLVMAtomicRMWBinOpSub; + case AtomicRmwOp_and: return ZigLLVMAtomicRMWBinOpAnd; + case AtomicRmwOp_nand: return ZigLLVMAtomicRMWBinOpNand; + case AtomicRmwOp_or: return ZigLLVMAtomicRMWBinOpOr; + case AtomicRmwOp_xor: return ZigLLVMAtomicRMWBinOpXor; + case AtomicRmwOp_max: + return is_signed ? ZigLLVMAtomicRMWBinOpMax : ZigLLVMAtomicRMWBinOpUMax; + case AtomicRmwOp_min: + return is_signed ? ZigLLVMAtomicRMWBinOpMin : ZigLLVMAtomicRMWBinOpUMin; + } + zig_unreachable(); +} + +static LLVMTypeRef get_atomic_abi_type(CodeGen *g, IrInstGen *instruction) { + // If the operand type of an atomic operation is not a power of two sized + // we need to widen it before using it and then truncate the result. + + ir_assert(instruction->value->type->id == ZigTypeIdPointer, instruction); + ZigType *operand_type = instruction->value->type->data.pointer.child_type; + if (operand_type->id == ZigTypeIdInt || operand_type->id == ZigTypeIdEnum) { + if (operand_type->id == ZigTypeIdEnum) { + operand_type = operand_type->data.enumeration.tag_int_type; + } + auto bit_count = operand_type->data.integral.bit_count; + bool is_signed = operand_type->data.integral.is_signed; + + ir_assert(bit_count != 0, instruction); + if (bit_count == 1 || !is_power_of_2(bit_count)) { + return get_llvm_type(g, get_int_type(g, is_signed, operand_type->abi_size * 8)); + } else { + return nullptr; + } + } else if (operand_type->id == ZigTypeIdFloat) { + return nullptr; + } else if (operand_type->id == ZigTypeIdBool) { + return g->builtin_types.entry_u8->llvm_type; + } else { + ir_assert(get_codegen_ptr_type_bail(g, operand_type) != nullptr, instruction); + return nullptr; + } +} + +static LLVMValueRef ir_render_cmpxchg(CodeGen *g, IrExecutableGen *executable, IrInstGenCmpxchg *instruction) { + LLVMValueRef ptr_val = ir_llvm_value(g, instruction->ptr); + LLVMValueRef cmp_val = ir_llvm_value(g, instruction->cmp_value); + LLVMValueRef new_val = ir_llvm_value(g, instruction->new_value); + + ZigType *operand_type = instruction->new_value->value->type; + LLVMTypeRef actual_abi_type = get_atomic_abi_type(g, instruction->ptr); + if (actual_abi_type != nullptr) { + // operand needs widening and truncating + ptr_val = LLVMBuildBitCast(g->builder, ptr_val, + LLVMPointerType(actual_abi_type, 0), ""); + if (operand_type->data.integral.is_signed) { + cmp_val = LLVMBuildSExt(g->builder, cmp_val, actual_abi_type, ""); + new_val = LLVMBuildSExt(g->builder, new_val, actual_abi_type, ""); + } else { + cmp_val = LLVMBuildZExt(g->builder, cmp_val, actual_abi_type, ""); + new_val = LLVMBuildZExt(g->builder, new_val, actual_abi_type, ""); + } + } + + LLVMAtomicOrdering success_order = to_LLVMAtomicOrdering(instruction->success_order); + LLVMAtomicOrdering failure_order = to_LLVMAtomicOrdering(instruction->failure_order); + + LLVMValueRef result_val = ZigLLVMBuildCmpXchg(g->builder, ptr_val, cmp_val, new_val, + success_order, failure_order, instruction->is_weak); + + ZigType *optional_type = instruction->base.value->type; + assert(optional_type->id == ZigTypeIdOptional); + ZigType *child_type = optional_type->data.maybe.child_type; + + if (!handle_is_ptr(g, optional_type)) { + LLVMValueRef payload_val = LLVMBuildExtractValue(g->builder, result_val, 0, ""); + if (actual_abi_type != nullptr) { + payload_val = LLVMBuildTrunc(g->builder, payload_val, get_llvm_type(g, operand_type), ""); + } + LLVMValueRef success_bit = LLVMBuildExtractValue(g->builder, result_val, 1, ""); + return LLVMBuildSelect(g->builder, success_bit, LLVMConstNull(get_llvm_type(g, child_type)), payload_val, ""); + } + + // When the cmpxchg is discarded, the result location will have no bits. + if (!type_has_bits(g, instruction->result_loc->value->type)) { + return nullptr; + } + + LLVMValueRef result_loc = ir_llvm_value(g, instruction->result_loc); + ir_assert(result_loc != nullptr, &instruction->base); + ir_assert(type_has_bits(g, child_type), &instruction->base); + + LLVMValueRef payload_val = LLVMBuildExtractValue(g->builder, result_val, 0, ""); + if (actual_abi_type != nullptr) { + payload_val = LLVMBuildTrunc(g->builder, payload_val, get_llvm_type(g, operand_type), ""); + } + LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, result_loc, maybe_child_index, ""); + gen_assign_raw(g, val_ptr, get_pointer_to_type(g, child_type, false), payload_val); + + LLVMValueRef success_bit = LLVMBuildExtractValue(g->builder, result_val, 1, ""); + LLVMValueRef nonnull_bit = LLVMBuildNot(g->builder, success_bit, ""); + LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, result_loc, maybe_null_index, ""); + gen_store_untyped(g, nonnull_bit, maybe_ptr, 0, false); + return result_loc; +} + +static LLVMValueRef ir_render_fence(CodeGen *g, IrExecutableGen *executable, IrInstGenFence *instruction) { + LLVMAtomicOrdering atomic_order = to_LLVMAtomicOrdering(instruction->order); + LLVMBuildFence(g->builder, atomic_order, false, ""); + return nullptr; +} + +static LLVMValueRef ir_render_truncate(CodeGen *g, IrExecutableGen *executable, IrInstGenTruncate *instruction) { + LLVMValueRef target_val = ir_llvm_value(g, instruction->target); + ZigType *dest_type = instruction->base.value->type; + ZigType *src_type = instruction->target->value->type; + if (dest_type == src_type) { + // no-op + return target_val; + } if (src_type->data.integral.bit_count == dest_type->data.integral.bit_count) { + return LLVMBuildBitCast(g->builder, target_val, get_llvm_type(g, dest_type), ""); + } else { + LLVMValueRef target_val = ir_llvm_value(g, instruction->target); + return LLVMBuildTrunc(g->builder, target_val, get_llvm_type(g, dest_type), ""); + } +} + +static LLVMValueRef ir_render_memset(CodeGen *g, IrExecutableGen *executable, IrInstGenMemset *instruction) { + LLVMValueRef dest_ptr = ir_llvm_value(g, instruction->dest_ptr); + LLVMValueRef len_val = ir_llvm_value(g, instruction->count); + + LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0); + LLVMValueRef dest_ptr_casted = LLVMBuildBitCast(g->builder, dest_ptr, ptr_u8, ""); + + ZigType *ptr_type = instruction->dest_ptr->value->type; + assert(ptr_type->id == ZigTypeIdPointer); + + bool val_is_undef = value_is_all_undef(g, instruction->byte->value); + LLVMValueRef fill_char; + if (val_is_undef) { + if (ir_want_runtime_safety_scope(g, instruction->base.base.scope)) { + fill_char = LLVMConstInt(LLVMInt8Type(), 0xaa, false); + } else { + return nullptr; + } + } else { + fill_char = ir_llvm_value(g, instruction->byte); + } + ZigLLVMBuildMemSet(g->builder, dest_ptr_casted, fill_char, len_val, get_ptr_align(g, ptr_type), + ptr_type->data.pointer.is_volatile); + + if (val_is_undef && g->valgrind_enabled) { + gen_valgrind_undef(g, dest_ptr_casted, len_val); + } + return nullptr; +} + +static LLVMValueRef ir_render_memcpy(CodeGen *g, IrExecutableGen *executable, IrInstGenMemcpy *instruction) { + LLVMValueRef dest_ptr = ir_llvm_value(g, instruction->dest_ptr); + LLVMValueRef src_ptr = ir_llvm_value(g, instruction->src_ptr); + LLVMValueRef len_val = ir_llvm_value(g, instruction->count); + + LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0); + + LLVMValueRef dest_ptr_casted = LLVMBuildBitCast(g->builder, dest_ptr, ptr_u8, ""); + LLVMValueRef src_ptr_casted = LLVMBuildBitCast(g->builder, src_ptr, ptr_u8, ""); + + ZigType *dest_ptr_type = instruction->dest_ptr->value->type; + ZigType *src_ptr_type = instruction->src_ptr->value->type; + + assert(dest_ptr_type->id == ZigTypeIdPointer); + assert(src_ptr_type->id == ZigTypeIdPointer); + + bool is_volatile = (dest_ptr_type->data.pointer.is_volatile || src_ptr_type->data.pointer.is_volatile); + ZigLLVMBuildMemCpy(g->builder, dest_ptr_casted, get_ptr_align(g, dest_ptr_type), + src_ptr_casted, get_ptr_align(g, src_ptr_type), len_val, is_volatile); + return nullptr; +} + +static LLVMValueRef ir_render_wasm_memory_size(CodeGen *g, IrExecutableGen *executable, IrInstGenWasmMemorySize *instruction) { + // TODO adjust for wasm64 + LLVMValueRef param = ir_llvm_value(g, instruction->index); + LLVMValueRef val = LLVMBuildCall(g->builder, gen_wasm_memory_size(g), ¶m, 1, ""); + return val; +} + +static LLVMValueRef ir_render_wasm_memory_grow(CodeGen *g, IrExecutableGen *executable, IrInstGenWasmMemoryGrow *instruction) { + // TODO adjust for wasm64 + LLVMValueRef params[] = { + ir_llvm_value(g, instruction->index), + ir_llvm_value(g, instruction->delta), + }; + LLVMValueRef val = LLVMBuildCall(g->builder, gen_wasm_memory_grow(g), params, 2, ""); + return val; +} + +static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutableGen *executable, IrInstGenSlice *instruction) { + Error err; + + LLVMValueRef array_ptr_ptr = ir_llvm_value(g, instruction->ptr); + ZigType *array_ptr_type = instruction->ptr->value->type; + assert(array_ptr_type->id == ZigTypeIdPointer); + ZigType *array_type = array_ptr_type->data.pointer.child_type; + LLVMValueRef array_ptr = get_handle_value(g, array_ptr_ptr, array_type, array_ptr_type); + + bool want_runtime_safety = instruction->safety_check_on && ir_want_runtime_safety(g, &instruction->base); + + // The result is either a slice or a pointer to an array + ZigType *result_type = instruction->base.value->type; + + // This is not whether the result type has a sentinel, but whether there should be a sentinel check, + // e.g. if they used [a..b :s] syntax. + ZigValue *sentinel = instruction->sentinel; + + LLVMValueRef slice_start_ptr = nullptr; + LLVMValueRef len_value = nullptr; + + if (array_type->id == ZigTypeIdArray || + (array_type->id == ZigTypeIdPointer && array_type->data.pointer.ptr_len == PtrLenSingle)) + { + if (array_type->id == ZigTypeIdPointer) { + array_type = array_type->data.pointer.child_type; + } + LLVMValueRef start_val = ir_llvm_value(g, instruction->start); + LLVMValueRef end_val; + if (instruction->end) { + end_val = ir_llvm_value(g, instruction->end); + } else { + end_val = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, array_type->data.array.len, false); + } + + if (want_runtime_safety) { + // Safety check: start <= end + if (instruction->start->value->special == ConstValSpecialRuntime || instruction->end) { + add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val); + } + + // Safety check: the last element of the slice (the sentinel if + // requested) must be inside the array + // XXX: Overflow is not checked here... + const size_t full_len = array_type->data.array.len + + (array_type->data.array.sentinel != nullptr); + LLVMValueRef array_end = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, + full_len, false); + + LLVMValueRef check_end_val = end_val; + if (sentinel != nullptr) { + LLVMValueRef usize_one = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, 1, false); + check_end_val = LLVMBuildNUWAdd(g->builder, end_val, usize_one, ""); + } + add_bounds_check(g, check_end_val, LLVMIntEQ, nullptr, LLVMIntULE, array_end); + } + + bool value_has_bits; + if ((err = type_has_bits2(g, array_type, &value_has_bits))) + codegen_report_errors_and_exit(g); + + if (value_has_bits) { + if (want_runtime_safety && sentinel != nullptr) { + LLVMValueRef indices[] = { + LLVMConstNull(g->builtin_types.entry_usize->llvm_type), + end_val, + }; + LLVMValueRef sentinel_elem_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, ""); + add_sentinel_check(g, sentinel_elem_ptr, sentinel); + } + + LLVMValueRef indices[] = { + LLVMConstNull(g->builtin_types.entry_usize->llvm_type), + start_val, + }; + slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, ""); + } + + len_value = LLVMBuildNUWSub(g->builder, end_val, start_val, ""); + } else if (array_type->id == ZigTypeIdPointer) { + assert(array_type->data.pointer.ptr_len != PtrLenSingle); + LLVMValueRef start_val = ir_llvm_value(g, instruction->start); + LLVMValueRef end_val = ir_llvm_value(g, instruction->end); + + if (want_runtime_safety) { + // Safety check: start <= end + add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val); + } + + bool value_has_bits; + if ((err = type_has_bits2(g, array_type, &value_has_bits))) + codegen_report_errors_and_exit(g); + + if (value_has_bits) { + if (want_runtime_safety && sentinel != nullptr) { + LLVMValueRef sentinel_elem_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &end_val, 1, ""); + add_sentinel_check(g, sentinel_elem_ptr, sentinel); + } + + slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &start_val, 1, ""); + } + + len_value = LLVMBuildNUWSub(g->builder, end_val, start_val, ""); + } else if (array_type->id == ZigTypeIdStruct) { + assert(array_type->data.structure.special == StructSpecialSlice); + assert(LLVMGetTypeKind(LLVMTypeOf(array_ptr)) == LLVMPointerTypeKind); + assert(LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(array_ptr))) == LLVMStructTypeKind); + + const size_t gen_len_index = array_type->data.structure.fields[slice_len_index]->gen_index; + assert(gen_len_index != SIZE_MAX); + + LLVMValueRef prev_end = nullptr; + if (!instruction->end || want_runtime_safety) { + LLVMValueRef src_len_ptr = LLVMBuildStructGEP(g->builder, array_ptr, gen_len_index, ""); + prev_end = gen_load_untyped(g, src_len_ptr, 0, false, ""); + } + + LLVMValueRef start_val = ir_llvm_value(g, instruction->start); + LLVMValueRef end_val; + if (instruction->end) { + end_val = ir_llvm_value(g, instruction->end); + } else { + end_val = prev_end; + } + + ZigType *ptr_field_type = array_type->data.structure.fields[slice_ptr_index]->type_entry; + + if (want_runtime_safety) { + assert(prev_end); + // Safety check: start <= end + add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val); + + // Safety check: the sentinel counts as one more element + // XXX: Overflow is not checked here... + LLVMValueRef check_prev_end = prev_end; + if (ptr_field_type->data.pointer.sentinel != nullptr) { + LLVMValueRef usize_one = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, 1, false); + check_prev_end = LLVMBuildNUWAdd(g->builder, prev_end, usize_one, ""); + } + LLVMValueRef check_end_val = end_val; + if (sentinel != nullptr) { + LLVMValueRef usize_one = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, 1, false); + check_end_val = LLVMBuildNUWAdd(g->builder, end_val, usize_one, ""); + } + + add_bounds_check(g, check_end_val, LLVMIntEQ, nullptr, LLVMIntULE, check_prev_end); + } + + bool ptr_has_bits; + if ((err = type_has_bits2(g, ptr_field_type, &ptr_has_bits))) + codegen_report_errors_and_exit(g); + + if (ptr_has_bits) { + const size_t gen_ptr_index = array_type->data.structure.fields[slice_ptr_index]->gen_index; + assert(gen_ptr_index != SIZE_MAX); + + LLVMValueRef src_ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, gen_ptr_index, ""); + LLVMValueRef src_ptr = gen_load_untyped(g, src_ptr_ptr, 0, false, ""); + + if (sentinel != nullptr) { + LLVMValueRef sentinel_elem_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr, &end_val, 1, ""); + add_sentinel_check(g, sentinel_elem_ptr, sentinel); + } + + slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr, &start_val, 1, ""); + } + + len_value = LLVMBuildNUWSub(g->builder, end_val, start_val, ""); + } else { + zig_unreachable(); + } + + bool result_has_bits; + if ((err = type_has_bits2(g, result_type, &result_has_bits))) + codegen_report_errors_and_exit(g); + + // Nothing to do, we're only interested in the bound checks emitted above + if (!result_has_bits) + return nullptr; + + // The starting pointer for the slice may be null in case of zero-sized + // arrays, the length value is always defined. + assert(len_value != nullptr); + + // The slice decays into a pointer to an array, the size is tracked in the + // type itself + if (result_type->id == ZigTypeIdPointer) { + ir_assert(instruction->result_loc == nullptr, &instruction->base); + LLVMTypeRef result_ptr_type = get_llvm_type(g, result_type); + + if (slice_start_ptr != nullptr) { + return LLVMBuildBitCast(g->builder, slice_start_ptr, result_ptr_type, ""); + } + + return LLVMGetUndef(result_ptr_type); + } + + ir_assert(instruction->result_loc != nullptr, &instruction->base); + // Create a new slice + LLVMValueRef tmp_struct_ptr = ir_llvm_value(g, instruction->result_loc); + + ZigType *slice_ptr_type = result_type->data.structure.fields[slice_ptr_index]->type_entry; + + // The slice may not have a pointer at all if it points to a zero-sized type + const size_t gen_ptr_index = result_type->data.structure.fields[slice_ptr_index]->gen_index; + if (gen_ptr_index != SIZE_MAX) { + LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_ptr_index, ""); + if (slice_start_ptr != nullptr) { + gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); + } else if (want_runtime_safety) { + gen_undef_init(g, slice_ptr_type->abi_align, slice_ptr_type, ptr_field_ptr); + } else { + gen_store_untyped(g, LLVMGetUndef(get_llvm_type(g, slice_ptr_type)), ptr_field_ptr, 0, false); + } + } + + const size_t gen_len_index = result_type->data.structure.fields[slice_len_index]->gen_index; + assert(gen_len_index != SIZE_MAX); + + LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, gen_len_index, ""); + gen_store_untyped(g, len_value, len_field_ptr, 0, false); + + return tmp_struct_ptr; +} + +static LLVMValueRef get_trap_fn_val(CodeGen *g) { + if (g->trap_fn_val) + return g->trap_fn_val; + + LLVMTypeRef fn_type = LLVMFunctionType(LLVMVoidType(), nullptr, 0, false); + g->trap_fn_val = LLVMAddFunction(g->module, "llvm.debugtrap", fn_type); + assert(LLVMGetIntrinsicID(g->trap_fn_val)); + + return g->trap_fn_val; +} + + +static LLVMValueRef ir_render_breakpoint(CodeGen *g, IrExecutableGen *executable, IrInstGenBreakpoint *instruction) { + LLVMBuildCall(g->builder, get_trap_fn_val(g), nullptr, 0, ""); + return nullptr; +} + +static LLVMValueRef ir_render_return_address(CodeGen *g, IrExecutableGen *executable, + IrInstGenReturnAddress *instruction) +{ + if (target_is_wasm(g->zig_target) && g->zig_target->os != OsEmscripten) { + // I got this error from LLVM 10: + // "Non-Emscripten WebAssembly hasn't implemented __builtin_return_address" + return LLVMConstNull(get_llvm_type(g, instruction->base.value->type)); + } + + LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_i32->llvm_type); + LLVMValueRef ptr_val = LLVMBuildCall(g->builder, get_return_address_fn_val(g), &zero, 1, ""); + return LLVMBuildPtrToInt(g->builder, ptr_val, g->builtin_types.entry_usize->llvm_type, ""); +} + +static LLVMValueRef get_frame_address_fn_val(CodeGen *g) { + if (g->frame_address_fn_val) + return g->frame_address_fn_val; + + ZigType *return_type = get_pointer_to_type(g, g->builtin_types.entry_u8, true); + + LLVMTypeRef fn_type = LLVMFunctionType(get_llvm_type(g, return_type), + &g->builtin_types.entry_i32->llvm_type, 1, false); + g->frame_address_fn_val = LLVMAddFunction(g->module, "llvm.frameaddress.p0i8", fn_type); + assert(LLVMGetIntrinsicID(g->frame_address_fn_val)); + + return g->frame_address_fn_val; +} + +static LLVMValueRef ir_render_frame_address(CodeGen *g, IrExecutableGen *executable, + IrInstGenFrameAddress *instruction) +{ + LLVMValueRef zero = LLVMConstNull(g->builtin_types.entry_i32->llvm_type); + LLVMValueRef ptr_val = LLVMBuildCall(g->builder, get_frame_address_fn_val(g), &zero, 1, ""); + return LLVMBuildPtrToInt(g->builder, ptr_val, g->builtin_types.entry_usize->llvm_type, ""); +} + +static LLVMValueRef ir_render_handle(CodeGen *g, IrExecutableGen *executable, IrInstGenFrameHandle *instruction) { + return g->cur_frame_ptr; +} + +static LLVMValueRef render_shl_with_overflow(CodeGen *g, IrInstGenOverflowOp *instruction) { + ZigType *int_type = instruction->result_ptr_type; + assert(int_type->id == ZigTypeIdInt); + + LLVMValueRef op1 = ir_llvm_value(g, instruction->op1); + LLVMValueRef op2 = ir_llvm_value(g, instruction->op2); + LLVMValueRef ptr_result = ir_llvm_value(g, instruction->result_ptr); + + LLVMValueRef op2_casted = gen_widen_or_shorten(g, false, instruction->op2->value->type, + instruction->op1->value->type, op2); + + LLVMValueRef result = LLVMBuildShl(g->builder, op1, op2_casted, ""); + LLVMValueRef orig_val; + if (int_type->data.integral.is_signed) { + orig_val = LLVMBuildAShr(g->builder, result, op2_casted, ""); + } else { + orig_val = LLVMBuildLShr(g->builder, result, op2_casted, ""); + } + LLVMValueRef overflow_bit = LLVMBuildICmp(g->builder, LLVMIntNE, op1, orig_val, ""); + + gen_store(g, result, ptr_result, instruction->result_ptr->value->type); + + return overflow_bit; +} + +static LLVMValueRef ir_render_overflow_op(CodeGen *g, IrExecutableGen *executable, IrInstGenOverflowOp *instruction) { + AddSubMul add_sub_mul; + switch (instruction->op) { + case IrOverflowOpAdd: + add_sub_mul = AddSubMulAdd; + break; + case IrOverflowOpSub: + add_sub_mul = AddSubMulSub; + break; + case IrOverflowOpMul: + add_sub_mul = AddSubMulMul; + break; + case IrOverflowOpShl: + return render_shl_with_overflow(g, instruction); + } + + ZigType *int_type = instruction->result_ptr_type; + assert(int_type->id == ZigTypeIdInt); + + LLVMValueRef fn_val = get_int_overflow_fn(g, int_type, add_sub_mul); + + LLVMValueRef op1 = ir_llvm_value(g, instruction->op1); + LLVMValueRef op2 = ir_llvm_value(g, instruction->op2); + LLVMValueRef ptr_result = ir_llvm_value(g, instruction->result_ptr); + + LLVMValueRef params[] = { + op1, + op2, + }; + + LLVMValueRef result_struct = LLVMBuildCall(g->builder, fn_val, params, 2, ""); + LLVMValueRef result = LLVMBuildExtractValue(g->builder, result_struct, 0, ""); + LLVMValueRef overflow_bit = LLVMBuildExtractValue(g->builder, result_struct, 1, ""); + gen_store(g, result, ptr_result, instruction->result_ptr->value->type); + + return overflow_bit; +} + +static LLVMValueRef ir_render_test_err(CodeGen *g, IrExecutableGen *executable, IrInstGenTestErr *instruction) { + ZigType *err_union_type = instruction->err_union->value->type; + ZigType *payload_type = err_union_type->data.error_union.payload_type; + LLVMValueRef err_union_handle = ir_llvm_value(g, instruction->err_union); + + LLVMValueRef err_val; + if (type_has_bits(g, payload_type)) { + LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, err_union_handle, err_union_err_index, ""); + err_val = gen_load_untyped(g, err_val_ptr, 0, false, ""); + } else { + err_val = err_union_handle; + } + + LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, g->err_tag_type)); + return LLVMBuildICmp(g->builder, LLVMIntNE, err_val, zero, ""); +} + +static LLVMValueRef ir_render_unwrap_err_code(CodeGen *g, IrExecutableGen *executable, + IrInstGenUnwrapErrCode *instruction) +{ + if (instruction->base.value->special != ConstValSpecialRuntime) + return nullptr; + + ZigType *ptr_type = instruction->err_union_ptr->value->type; + assert(ptr_type->id == ZigTypeIdPointer); + ZigType *err_union_type = ptr_type->data.pointer.child_type; + ZigType *payload_type = err_union_type->data.error_union.payload_type; + LLVMValueRef err_union_ptr = ir_llvm_value(g, instruction->err_union_ptr); + if (!type_has_bits(g, payload_type)) { + return err_union_ptr; + } else { + // TODO assign undef to the payload + LLVMValueRef err_union_handle = get_handle_value(g, err_union_ptr, err_union_type, ptr_type); + return LLVMBuildStructGEP(g->builder, err_union_handle, err_union_err_index, ""); + } +} + +static LLVMValueRef ir_render_unwrap_err_payload(CodeGen *g, IrExecutableGen *executable, + IrInstGenUnwrapErrPayload *instruction) +{ + Error err; + + if (instruction->base.value->special != ConstValSpecialRuntime) + return nullptr; + + bool want_safety = instruction->safety_check_on && ir_want_runtime_safety(g, &instruction->base) && + g->errors_by_index.length > 1; + + ZigType *ptr_type = instruction->value->value->type; + assert(ptr_type->id == ZigTypeIdPointer); + ZigType *err_union_type = ptr_type->data.pointer.child_type; + ZigType *payload_type = err_union_type->data.error_union.payload_type; + LLVMValueRef err_union_ptr = ir_llvm_value(g, instruction->value); + + LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, g->err_tag_type)); + bool value_has_bits; + if ((err = type_has_bits2(g, instruction->base.value->type, &value_has_bits))) + codegen_report_errors_and_exit(g); + if (!want_safety && !value_has_bits) { + if (instruction->initializing) { + gen_store_untyped(g, zero, err_union_ptr, 0, false); + } + return nullptr; + } + + + LLVMValueRef err_union_handle = get_handle_value(g, err_union_ptr, err_union_type, ptr_type); + + if (!type_has_bits(g, err_union_type->data.error_union.err_set_type)) { + return err_union_handle; + } + + if (want_safety) { + LLVMValueRef err_val; + if (type_has_bits(g, payload_type)) { + LLVMValueRef err_val_ptr = LLVMBuildStructGEP(g->builder, err_union_handle, err_union_err_index, ""); + err_val = gen_load_untyped(g, err_val_ptr, 0, false, ""); + } else { + err_val = err_union_handle; + } + LLVMValueRef cond_val = LLVMBuildICmp(g->builder, LLVMIntEQ, err_val, zero, ""); + LLVMBasicBlockRef err_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapErrError"); + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "UnwrapErrOk"); + LLVMBuildCondBr(g->builder, cond_val, ok_block, err_block); + + LLVMPositionBuilderAtEnd(g->builder, err_block); + gen_safety_crash_for_err(g, err_val, instruction->base.base.scope); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + } + + if (type_has_bits(g, payload_type)) { + if (instruction->initializing) { + LLVMValueRef err_tag_ptr = LLVMBuildStructGEP(g->builder, err_union_handle, err_union_err_index, ""); + LLVMValueRef ok_err_val = LLVMConstNull(get_llvm_type(g, g->err_tag_type)); + gen_store_untyped(g, ok_err_val, err_tag_ptr, 0, false); + } + return LLVMBuildStructGEP(g->builder, err_union_handle, err_union_payload_index, ""); + } else { + if (instruction->initializing) { + gen_store_untyped(g, zero, err_union_ptr, 0, false); + } + return nullptr; + } +} + +static LLVMValueRef ir_render_optional_wrap(CodeGen *g, IrExecutableGen *executable, IrInstGenOptionalWrap *instruction) { + ZigType *wanted_type = instruction->base.value->type; + + assert(wanted_type->id == ZigTypeIdOptional); + + ZigType *child_type = wanted_type->data.maybe.child_type; + + if (!type_has_bits(g, child_type)) { + LLVMValueRef result = LLVMConstAllOnes(LLVMInt1Type()); + if (instruction->result_loc != nullptr) { + LLVMValueRef result_loc = ir_llvm_value(g, instruction->result_loc); + gen_store_untyped(g, result, result_loc, 0, false); + } + return result; + } + + LLVMValueRef payload_val = ir_llvm_value(g, instruction->operand); + if (!handle_is_ptr(g, wanted_type)) { + if (instruction->result_loc != nullptr) { + LLVMValueRef result_loc = ir_llvm_value(g, instruction->result_loc); + gen_store_untyped(g, payload_val, result_loc, 0, false); + } + return payload_val; + } + + LLVMValueRef result_loc = ir_llvm_value(g, instruction->result_loc); + + LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, result_loc, maybe_child_index, ""); + // child_type and instruction->value->value->type may differ by constness + gen_assign_raw(g, val_ptr, get_pointer_to_type(g, child_type, false), payload_val); + LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, result_loc, maybe_null_index, ""); + gen_store_untyped(g, LLVMConstAllOnes(LLVMInt1Type()), maybe_ptr, 0, false); + + return result_loc; +} + +static LLVMValueRef ir_render_err_wrap_code(CodeGen *g, IrExecutableGen *executable, IrInstGenErrWrapCode *instruction) { + ZigType *wanted_type = instruction->base.value->type; + + assert(wanted_type->id == ZigTypeIdErrorUnion); + + LLVMValueRef err_val = ir_llvm_value(g, instruction->operand); + + if (!handle_is_ptr(g, wanted_type)) + return err_val; + + LLVMValueRef result_loc = ir_llvm_value(g, instruction->result_loc); + + LLVMValueRef err_tag_ptr = LLVMBuildStructGEP(g->builder, result_loc, err_union_err_index, ""); + gen_store_untyped(g, err_val, err_tag_ptr, 0, false); + + // TODO store undef to the payload + + return result_loc; +} + +static LLVMValueRef ir_render_err_wrap_payload(CodeGen *g, IrExecutableGen *executable, IrInstGenErrWrapPayload *instruction) { + ZigType *wanted_type = instruction->base.value->type; + + assert(wanted_type->id == ZigTypeIdErrorUnion); + + ZigType *payload_type = wanted_type->data.error_union.payload_type; + ZigType *err_set_type = wanted_type->data.error_union.err_set_type; + + if (!type_has_bits(g, err_set_type)) { + return ir_llvm_value(g, instruction->operand); + } + + LLVMValueRef ok_err_val = LLVMConstNull(get_llvm_type(g, g->err_tag_type)); + + if (!type_has_bits(g, payload_type)) + return ok_err_val; + + + LLVMValueRef result_loc = ir_llvm_value(g, instruction->result_loc); + + LLVMValueRef payload_val = ir_llvm_value(g, instruction->operand); + + LLVMValueRef err_tag_ptr = LLVMBuildStructGEP(g->builder, result_loc, err_union_err_index, ""); + gen_store_untyped(g, ok_err_val, err_tag_ptr, 0, false); + + LLVMValueRef payload_ptr = LLVMBuildStructGEP(g->builder, result_loc, err_union_payload_index, ""); + gen_assign_raw(g, payload_ptr, get_pointer_to_type(g, payload_type, false), payload_val); + + return result_loc; +} + +static LLVMValueRef ir_render_union_tag(CodeGen *g, IrExecutableGen *executable, IrInstGenUnionTag *instruction) { + ZigType *union_type = instruction->value->value->type; + + ZigType *tag_type = union_type->data.unionation.tag_type; + if (!type_has_bits(g, tag_type)) + return nullptr; + + LLVMValueRef union_val = ir_llvm_value(g, instruction->value); + if (union_type->data.unionation.gen_field_count == 0) + return union_val; + + assert(union_type->data.unionation.gen_tag_index != SIZE_MAX); + LLVMValueRef tag_field_ptr = LLVMBuildStructGEP(g->builder, union_val, + union_type->data.unionation.gen_tag_index, ""); + ZigType *ptr_type = get_pointer_to_type(g, tag_type, false); + return get_handle_value(g, tag_field_ptr, tag_type, ptr_type); +} + +static LLVMValueRef ir_render_panic(CodeGen *g, IrExecutableGen *executable, IrInstGenPanic *instruction) { + bool is_llvm_alloca; + LLVMValueRef err_ret_trace_val = get_cur_err_ret_trace_val(g, instruction->base.base.scope, &is_llvm_alloca); + gen_panic(g, ir_llvm_value(g, instruction->msg), err_ret_trace_val, is_llvm_alloca); + return nullptr; +} + +static LLVMValueRef ir_render_atomic_rmw(CodeGen *g, IrExecutableGen *executable, + IrInstGenAtomicRmw *instruction) +{ + bool is_signed; + ZigType *operand_type = instruction->operand->value->type; + bool is_float = operand_type->id == ZigTypeIdFloat; + if (operand_type->id == ZigTypeIdInt) { + is_signed = operand_type->data.integral.is_signed; + } else { + is_signed = false; + } + enum ZigLLVM_AtomicRMWBinOp op = to_ZigLLVMAtomicRMWBinOp(instruction->op, is_signed, is_float); + LLVMAtomicOrdering ordering = to_LLVMAtomicOrdering(instruction->ordering); + LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr); + LLVMValueRef operand = ir_llvm_value(g, instruction->operand); + + LLVMTypeRef actual_abi_type = get_atomic_abi_type(g, instruction->ptr); + if (actual_abi_type != nullptr) { + // operand needs widening and truncating + LLVMValueRef casted_ptr = LLVMBuildBitCast(g->builder, ptr, + LLVMPointerType(actual_abi_type, 0), ""); + LLVMValueRef casted_operand; + if (operand_type->data.integral.is_signed) { + casted_operand = LLVMBuildSExt(g->builder, operand, actual_abi_type, ""); + } else { + casted_operand = LLVMBuildZExt(g->builder, operand, actual_abi_type, ""); + } + LLVMValueRef uncasted_result = ZigLLVMBuildAtomicRMW(g->builder, op, casted_ptr, casted_operand, ordering, + g->is_single_threaded); + return LLVMBuildTrunc(g->builder, uncasted_result, get_llvm_type(g, operand_type), ""); + } + + if (get_codegen_ptr_type_bail(g, operand_type) == nullptr) { + return ZigLLVMBuildAtomicRMW(g->builder, op, ptr, operand, ordering, g->is_single_threaded); + } + + // it's a pointer but we need to treat it as an int + LLVMValueRef casted_ptr = LLVMBuildBitCast(g->builder, ptr, + LLVMPointerType(g->builtin_types.entry_usize->llvm_type, 0), ""); + LLVMValueRef casted_operand = LLVMBuildPtrToInt(g->builder, operand, g->builtin_types.entry_usize->llvm_type, ""); + LLVMValueRef uncasted_result = ZigLLVMBuildAtomicRMW(g->builder, op, casted_ptr, casted_operand, ordering, + g->is_single_threaded); + return LLVMBuildIntToPtr(g->builder, uncasted_result, get_llvm_type(g, operand_type), ""); +} + +static LLVMValueRef ir_render_atomic_load(CodeGen *g, IrExecutableGen *executable, + IrInstGenAtomicLoad *instruction) +{ + LLVMAtomicOrdering ordering = to_LLVMAtomicOrdering(instruction->ordering); + LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr); + + ZigType *operand_type = instruction->ptr->value->type->data.pointer.child_type; + LLVMTypeRef actual_abi_type = get_atomic_abi_type(g, instruction->ptr); + if (actual_abi_type != nullptr) { + // operand needs widening and truncating + ptr = LLVMBuildBitCast(g->builder, ptr, + LLVMPointerType(actual_abi_type, 0), ""); + LLVMValueRef load_inst = gen_load(g, ptr, instruction->ptr->value->type, ""); + LLVMSetOrdering(load_inst, ordering); + return LLVMBuildTrunc(g->builder, load_inst, get_llvm_type(g, operand_type), ""); + } + LLVMValueRef load_inst = gen_load(g, ptr, instruction->ptr->value->type, ""); + LLVMSetOrdering(load_inst, ordering); + return load_inst; +} + +static LLVMValueRef ir_render_atomic_store(CodeGen *g, IrExecutableGen *executable, + IrInstGenAtomicStore *instruction) +{ + LLVMAtomicOrdering ordering = to_LLVMAtomicOrdering(instruction->ordering); + LLVMValueRef ptr = ir_llvm_value(g, instruction->ptr); + LLVMValueRef value = ir_llvm_value(g, instruction->value); + + LLVMTypeRef actual_abi_type = get_atomic_abi_type(g, instruction->ptr); + if (actual_abi_type != nullptr) { + // operand needs widening + ptr = LLVMBuildBitCast(g->builder, ptr, + LLVMPointerType(actual_abi_type, 0), ""); + if (instruction->value->value->type->data.integral.is_signed) { + value = LLVMBuildSExt(g->builder, value, actual_abi_type, ""); + } else { + value = LLVMBuildZExt(g->builder, value, actual_abi_type, ""); + } + } + LLVMValueRef store_inst = gen_store(g, value, ptr, instruction->ptr->value->type); + LLVMSetOrdering(store_inst, ordering); + return nullptr; +} + +static LLVMValueRef ir_render_float_op(CodeGen *g, IrExecutableGen *executable, IrInstGenFloatOp *instruction) { + LLVMValueRef operand = ir_llvm_value(g, instruction->operand); + LLVMValueRef fn_val = get_float_fn(g, instruction->base.value->type, ZigLLVMFnIdFloatOp, instruction->fn_id); + return LLVMBuildCall(g->builder, fn_val, &operand, 1, ""); +} + +static LLVMValueRef ir_render_mul_add(CodeGen *g, IrExecutableGen *executable, IrInstGenMulAdd *instruction) { + LLVMValueRef op1 = ir_llvm_value(g, instruction->op1); + LLVMValueRef op2 = ir_llvm_value(g, instruction->op2); + LLVMValueRef op3 = ir_llvm_value(g, instruction->op3); + assert(instruction->base.value->type->id == ZigTypeIdFloat || + instruction->base.value->type->id == ZigTypeIdVector); + LLVMValueRef fn_val = get_float_fn(g, instruction->base.value->type, ZigLLVMFnIdFMA, BuiltinFnIdMulAdd); + LLVMValueRef args[3] = { + op1, + op2, + op3, + }; + return LLVMBuildCall(g->builder, fn_val, args, 3, ""); +} + +static LLVMValueRef ir_render_bswap(CodeGen *g, IrExecutableGen *executable, IrInstGenBswap *instruction) { + LLVMValueRef op = ir_llvm_value(g, instruction->op); + ZigType *expr_type = instruction->base.value->type; + bool is_vector = expr_type->id == ZigTypeIdVector; + ZigType *int_type = is_vector ? expr_type->data.vector.elem_type : expr_type; + assert(int_type->id == ZigTypeIdInt); + if (int_type->data.integral.bit_count % 16 == 0) { + LLVMValueRef fn_val = get_int_builtin_fn(g, expr_type, BuiltinFnIdBswap); + return LLVMBuildCall(g->builder, fn_val, &op, 1, ""); + } + // Not an even number of bytes, so we zext 1 byte, then bswap, shift right 1 byte, truncate + ZigType *extended_type = get_int_type(g, int_type->data.integral.is_signed, + int_type->data.integral.bit_count + 8); + LLVMValueRef shift_amt = LLVMConstInt(get_llvm_type(g, extended_type), 8, false); + if (is_vector) { + extended_type = get_vector_type(g, expr_type->data.vector.len, extended_type); + LLVMValueRef *values = heap::c_allocator.allocate_nonzero(expr_type->data.vector.len); + for (uint32_t i = 0; i < expr_type->data.vector.len; i += 1) { + values[i] = shift_amt; + } + shift_amt = LLVMConstVector(values, expr_type->data.vector.len); + heap::c_allocator.deallocate(values, expr_type->data.vector.len); + } + // aabbcc + LLVMValueRef extended = LLVMBuildZExt(g->builder, op, get_llvm_type(g, extended_type), ""); + // 00aabbcc + LLVMValueRef fn_val = get_int_builtin_fn(g, extended_type, BuiltinFnIdBswap); + LLVMValueRef swapped = LLVMBuildCall(g->builder, fn_val, &extended, 1, ""); + // ccbbaa00 + LLVMValueRef shifted = ZigLLVMBuildLShrExact(g->builder, swapped, shift_amt, ""); + // 00ccbbaa + return LLVMBuildTrunc(g->builder, shifted, get_llvm_type(g, expr_type), ""); +} + +static LLVMValueRef ir_render_bit_reverse(CodeGen *g, IrExecutableGen *executable, IrInstGenBitReverse *instruction) { + LLVMValueRef op = ir_llvm_value(g, instruction->op); + ZigType *int_type = instruction->base.value->type; + assert(int_type->id == ZigTypeIdInt); + LLVMValueRef fn_val = get_int_builtin_fn(g, instruction->base.value->type, BuiltinFnIdBitReverse); + return LLVMBuildCall(g->builder, fn_val, &op, 1, ""); +} + +static LLVMValueRef ir_render_vector_to_array(CodeGen *g, IrExecutableGen *executable, + IrInstGenVectorToArray *instruction) +{ + ZigType *array_type = instruction->base.value->type; + assert(array_type->id == ZigTypeIdArray); + assert(handle_is_ptr(g, array_type)); + LLVMValueRef result_loc = ir_llvm_value(g, instruction->result_loc); + LLVMValueRef vector = ir_llvm_value(g, instruction->vector); + + ZigType *elem_type = array_type->data.array.child_type; + bool bitcast_ok = elem_type->size_in_bits == elem_type->abi_size * 8; + if (bitcast_ok) { + LLVMValueRef casted_ptr = LLVMBuildBitCast(g->builder, result_loc, + LLVMPointerType(get_llvm_type(g, instruction->vector->value->type), 0), ""); + uint32_t alignment = get_ptr_align(g, instruction->result_loc->value->type); + gen_store_untyped(g, vector, casted_ptr, alignment, false); + } else { + // If the ABI size of the element type is not evenly divisible by size_in_bits, a simple bitcast + // will not work, and we fall back to extractelement. + LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; + LLVMTypeRef u32_type_ref = LLVMInt32Type(); + LLVMValueRef zero = LLVMConstInt(usize_type_ref, 0, false); + for (uintptr_t i = 0; i < instruction->vector->value->type->data.vector.len; i++) { + LLVMValueRef index_usize = LLVMConstInt(usize_type_ref, i, false); + LLVMValueRef index_u32 = LLVMConstInt(u32_type_ref, i, false); + LLVMValueRef indexes[] = { zero, index_usize }; + LLVMValueRef elem_ptr = LLVMBuildInBoundsGEP(g->builder, result_loc, indexes, 2, ""); + LLVMValueRef elem = LLVMBuildExtractElement(g->builder, vector, index_u32, ""); + LLVMBuildStore(g->builder, elem, elem_ptr); + } + } + return result_loc; +} + +static LLVMValueRef ir_render_array_to_vector(CodeGen *g, IrExecutableGen *executable, + IrInstGenArrayToVector *instruction) +{ + ZigType *vector_type = instruction->base.value->type; + assert(vector_type->id == ZigTypeIdVector); + assert(!handle_is_ptr(g, vector_type)); + LLVMValueRef array_ptr = ir_llvm_value(g, instruction->array); + LLVMTypeRef vector_type_ref = get_llvm_type(g, vector_type); + + ZigType *elem_type = vector_type->data.vector.elem_type; + bool bitcast_ok = elem_type->size_in_bits == elem_type->abi_size * 8; + if (bitcast_ok) { + LLVMValueRef casted_ptr = LLVMBuildBitCast(g->builder, array_ptr, + LLVMPointerType(vector_type_ref, 0), ""); + ZigType *array_type = instruction->array->value->type; + assert(array_type->id == ZigTypeIdArray); + uint32_t alignment = get_abi_alignment(g, array_type->data.array.child_type); + return gen_load_untyped(g, casted_ptr, alignment, false, ""); + } else { + // If the ABI size of the element type is not evenly divisible by size_in_bits, a simple bitcast + // will not work, and we fall back to insertelement. + LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; + LLVMTypeRef u32_type_ref = LLVMInt32Type(); + LLVMValueRef zero = LLVMConstInt(usize_type_ref, 0, false); + LLVMValueRef vector = LLVMGetUndef(vector_type_ref); + for (uintptr_t i = 0; i < instruction->base.value->type->data.vector.len; i++) { + LLVMValueRef index_usize = LLVMConstInt(usize_type_ref, i, false); + LLVMValueRef index_u32 = LLVMConstInt(u32_type_ref, i, false); + LLVMValueRef indexes[] = { zero, index_usize }; + LLVMValueRef elem_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indexes, 2, ""); + LLVMValueRef elem = LLVMBuildLoad(g->builder, elem_ptr, ""); + vector = LLVMBuildInsertElement(g->builder, vector, elem, index_u32, ""); + } + return vector; + } +} + +static LLVMValueRef ir_render_assert_zero(CodeGen *g, IrExecutableGen *executable, + IrInstGenAssertZero *instruction) +{ + LLVMValueRef target = ir_llvm_value(g, instruction->target); + ZigType *int_type = instruction->target->value->type; + if (ir_want_runtime_safety(g, &instruction->base)) { + return gen_assert_zero(g, target, int_type); + } + return nullptr; +} + +static LLVMValueRef ir_render_assert_non_null(CodeGen *g, IrExecutableGen *executable, + IrInstGenAssertNonNull *instruction) +{ + LLVMValueRef target = ir_llvm_value(g, instruction->target); + ZigType *target_type = instruction->target->value->type; + + if (target_type->id == ZigTypeIdPointer) { + assert(target_type->data.pointer.ptr_len == PtrLenC); + LLVMValueRef non_null_bit = LLVMBuildICmp(g->builder, LLVMIntNE, target, + LLVMConstNull(get_llvm_type(g, target_type)), ""); + + LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "AssertNonNullFail"); + LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "AssertNonNullOk"); + LLVMBuildCondBr(g->builder, non_null_bit, ok_block, fail_block); + + LLVMPositionBuilderAtEnd(g->builder, fail_block); + gen_assertion(g, PanicMsgIdUnwrapOptionalFail, &instruction->base); + + LLVMPositionBuilderAtEnd(g->builder, ok_block); + } else { + zig_unreachable(); + } + return nullptr; +} + +static LLVMValueRef ir_render_suspend_begin(CodeGen *g, IrExecutableGen *executable, + IrInstGenSuspendBegin *instruction) +{ + if (fn_is_async(g->cur_fn)) { + instruction->resume_bb = gen_suspend_begin(g, "SuspendResume"); + } + return nullptr; +} + +static LLVMValueRef ir_render_suspend_finish(CodeGen *g, IrExecutableGen *executable, + IrInstGenSuspendFinish *instruction) +{ + LLVMBuildRetVoid(g->builder); + + LLVMPositionBuilderAtEnd(g->builder, instruction->begin->resume_bb); + if (ir_want_runtime_safety(g, &instruction->base)) { + LLVMBuildStore(g->builder, g->cur_bad_not_suspended_index, g->cur_async_resume_index_ptr); + } + render_async_var_decls(g, instruction->base.base.scope); + return nullptr; +} + +static LLVMValueRef gen_await_early_return(CodeGen *g, IrInstGen *source_instr, + LLVMValueRef target_frame_ptr, ZigType *result_type, ZigType *ptr_result_type, + LLVMValueRef result_loc, bool non_async) +{ + LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; + LLVMValueRef their_result_ptr = nullptr; + if (type_has_bits(g, result_type) && (non_async || result_loc != nullptr)) { + LLVMValueRef their_result_ptr_ptr = LLVMBuildStructGEP(g->builder, target_frame_ptr, frame_ret_start, ""); + their_result_ptr = LLVMBuildLoad(g->builder, their_result_ptr_ptr, ""); + if (result_loc != nullptr) { + LLVMTypeRef ptr_u8 = LLVMPointerType(LLVMInt8Type(), 0); + LLVMValueRef dest_ptr_casted = LLVMBuildBitCast(g->builder, result_loc, ptr_u8, ""); + LLVMValueRef src_ptr_casted = LLVMBuildBitCast(g->builder, their_result_ptr, ptr_u8, ""); + bool is_volatile = false; + uint32_t abi_align = get_abi_alignment(g, result_type); + LLVMValueRef byte_count_val = LLVMConstInt(usize_type_ref, type_size(g, result_type), false); + ZigLLVMBuildMemCpy(g->builder, + dest_ptr_casted, abi_align, + src_ptr_casted, abi_align, byte_count_val, is_volatile); + } + } + if (codegen_fn_has_err_ret_tracing_arg(g, result_type)) { + LLVMValueRef their_trace_ptr_ptr = LLVMBuildStructGEP(g->builder, target_frame_ptr, + frame_index_trace_arg(g, result_type), ""); + LLVMValueRef src_trace_ptr = LLVMBuildLoad(g->builder, their_trace_ptr_ptr, ""); + bool is_llvm_alloca; + LLVMValueRef dest_trace_ptr = get_cur_err_ret_trace_val(g, source_instr->base.scope, &is_llvm_alloca); + LLVMValueRef args[] = { dest_trace_ptr, src_trace_ptr }; + ZigLLVMBuildCall(g->builder, get_merge_err_ret_traces_fn_val(g), args, 2, + get_llvm_cc(g, CallingConventionUnspecified), ZigLLVM_CallAttrAuto, ""); + } + if (non_async && type_has_bits(g, result_type)) { + LLVMValueRef result_ptr = (result_loc == nullptr) ? their_result_ptr : result_loc; + return get_handle_value(g, result_ptr, result_type, ptr_result_type); + } else { + return nullptr; + } +} + +static LLVMValueRef ir_render_await(CodeGen *g, IrExecutableGen *executable, IrInstGenAwait *instruction) { + LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; + LLVMValueRef zero = LLVMConstNull(usize_type_ref); + LLVMValueRef target_frame_ptr = ir_llvm_value(g, instruction->frame); + ZigType *result_type = instruction->base.value->type; + ZigType *ptr_result_type = get_pointer_to_type(g, result_type, true); + + LLVMValueRef result_loc = (instruction->result_loc == nullptr) ? + nullptr : ir_llvm_value(g, instruction->result_loc); + + if (instruction->is_nosuspend || + (instruction->target_fn != nullptr && !fn_is_async(instruction->target_fn))) + { + return gen_await_early_return(g, &instruction->base, target_frame_ptr, result_type, + ptr_result_type, result_loc, true); + } + + // Prepare to be suspended + LLVMBasicBlockRef resume_bb = gen_suspend_begin(g, "AwaitResume"); + LLVMBasicBlockRef end_bb = LLVMAppendBasicBlock(g->cur_fn_val, "AwaitEnd"); + + // At this point resuming the function will continue from resume_bb. + // This code is as if it is running inside the suspend block. + + // supply the awaiter return pointer + if (type_has_bits(g, result_type)) { + LLVMValueRef awaiter_ret_ptr_ptr = LLVMBuildStructGEP(g->builder, target_frame_ptr, frame_ret_start + 1, ""); + if (result_loc == nullptr) { + // no copy needed + LLVMBuildStore(g->builder, LLVMConstNull(LLVMGetElementType(LLVMTypeOf(awaiter_ret_ptr_ptr))), + awaiter_ret_ptr_ptr); + } else { + LLVMBuildStore(g->builder, result_loc, awaiter_ret_ptr_ptr); + } + } + + // supply the error return trace pointer + if (codegen_fn_has_err_ret_tracing_arg(g, result_type)) { + bool is_llvm_alloca; + LLVMValueRef my_err_ret_trace_val = get_cur_err_ret_trace_val(g, instruction->base.base.scope, &is_llvm_alloca); + assert(my_err_ret_trace_val != nullptr); + LLVMValueRef err_ret_trace_ptr_ptr = LLVMBuildStructGEP(g->builder, target_frame_ptr, + frame_index_trace_arg(g, result_type) + 1, ""); + LLVMBuildStore(g->builder, my_err_ret_trace_val, err_ret_trace_ptr_ptr); + } + + // caller's own frame pointer + LLVMValueRef awaiter_init_val = LLVMBuildPtrToInt(g->builder, g->cur_frame_ptr, usize_type_ref, ""); + LLVMValueRef awaiter_ptr = LLVMBuildStructGEP(g->builder, target_frame_ptr, frame_awaiter_index, ""); + LLVMValueRef prev_val = gen_maybe_atomic_op(g, LLVMAtomicRMWBinOpXchg, awaiter_ptr, awaiter_init_val, + LLVMAtomicOrderingRelease); + + LLVMBasicBlockRef bad_await_block = LLVMAppendBasicBlock(g->cur_fn_val, "BadAwait"); + LLVMBasicBlockRef complete_suspend_block = LLVMAppendBasicBlock(g->cur_fn_val, "CompleteSuspend"); + LLVMBasicBlockRef early_return_block = LLVMAppendBasicBlock(g->cur_fn_val, "EarlyReturn"); + + LLVMValueRef all_ones = LLVMConstAllOnes(usize_type_ref); + LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, prev_val, bad_await_block, 2); + + LLVMAddCase(switch_instr, zero, complete_suspend_block); + LLVMAddCase(switch_instr, all_ones, early_return_block); + + // We discovered that another awaiter was already here. + LLVMPositionBuilderAtEnd(g->builder, bad_await_block); + gen_assertion(g, PanicMsgIdBadAwait, &instruction->base); + + // Rely on the target to resume us from suspension. + LLVMPositionBuilderAtEnd(g->builder, complete_suspend_block); + LLVMBuildRetVoid(g->builder); + + // Early return: The async function has already completed. We must copy the result and + // the error return trace if applicable. + LLVMPositionBuilderAtEnd(g->builder, early_return_block); + gen_await_early_return(g, &instruction->base, target_frame_ptr, result_type, ptr_result_type, + result_loc, false); + LLVMBuildBr(g->builder, end_bb); + + LLVMPositionBuilderAtEnd(g->builder, resume_bb); + gen_assert_resume_id(g, &instruction->base, ResumeIdReturn, PanicMsgIdResumedAnAwaitingFn, nullptr); + LLVMBuildBr(g->builder, end_bb); + + LLVMPositionBuilderAtEnd(g->builder, end_bb); + // Rely on the spill for the llvm_value to be populated. + // See the implementation of ir_llvm_value. + return nullptr; +} + +static LLVMValueRef ir_render_resume(CodeGen *g, IrExecutableGen *executable, IrInstGenResume *instruction) { + LLVMValueRef frame = ir_llvm_value(g, instruction->frame); + ZigType *frame_type = instruction->frame->value->type; + assert(frame_type->id == ZigTypeIdAnyFrame); + + gen_resume(g, nullptr, frame, ResumeIdManual); + return nullptr; +} + +static LLVMValueRef ir_render_frame_size(CodeGen *g, IrExecutableGen *executable, + IrInstGenFrameSize *instruction) +{ + LLVMValueRef fn_val = ir_llvm_value(g, instruction->fn); + return gen_frame_size(g, fn_val); +} + +static LLVMValueRef ir_render_spill_begin(CodeGen *g, IrExecutableGen *executable, + IrInstGenSpillBegin *instruction) +{ + if (!fn_is_async(g->cur_fn)) + return nullptr; + + switch (instruction->spill_id) { + case SpillIdInvalid: + zig_unreachable(); + case SpillIdRetErrCode: { + LLVMValueRef operand = ir_llvm_value(g, instruction->operand); + LLVMValueRef ptr = ir_llvm_value(g, g->cur_fn->err_code_spill); + LLVMBuildStore(g->builder, operand, ptr); + return nullptr; + } + + } + zig_unreachable(); +} + +static LLVMValueRef ir_render_spill_end(CodeGen *g, IrExecutableGen *executable, IrInstGenSpillEnd *instruction) { + if (!fn_is_async(g->cur_fn)) + return ir_llvm_value(g, instruction->begin->operand); + + switch (instruction->begin->spill_id) { + case SpillIdInvalid: + zig_unreachable(); + case SpillIdRetErrCode: { + LLVMValueRef ptr = ir_llvm_value(g, g->cur_fn->err_code_spill); + return LLVMBuildLoad(g->builder, ptr, ""); + } + + } + zig_unreachable(); +} + +static LLVMValueRef ir_render_vector_extract_elem(CodeGen *g, IrExecutableGen *executable, + IrInstGenVectorExtractElem *instruction) +{ + LLVMValueRef vector = ir_llvm_value(g, instruction->vector); + LLVMValueRef index = ir_llvm_value(g, instruction->index); + return LLVMBuildExtractElement(g->builder, vector, index, ""); +} + +static void set_debug_location(CodeGen *g, IrInstGen *instruction) { + AstNode *source_node = instruction->base.source_node; + Scope *scope = instruction->base.scope; + + assert(source_node); + assert(scope); + + ZigLLVMSetCurrentDebugLocation(g->builder, (int)source_node->line + 1, + (int)source_node->column + 1, get_di_scope(g, scope)); +} + +static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutableGen *executable, IrInstGen *instruction) { + switch (instruction->id) { + case IrInstGenIdInvalid: + case IrInstGenIdConst: + case IrInstGenIdAlloca: + zig_unreachable(); + + case IrInstGenIdDeclVar: + return ir_render_decl_var(g, executable, (IrInstGenDeclVar *)instruction); + case IrInstGenIdReturn: + return ir_render_return(g, executable, (IrInstGenReturn *)instruction); + case IrInstGenIdBinOp: + return ir_render_bin_op(g, executable, (IrInstGenBinOp *)instruction); + case IrInstGenIdCast: + return ir_render_cast(g, executable, (IrInstGenCast *)instruction); + case IrInstGenIdUnreachable: + return ir_render_unreachable(g, executable, (IrInstGenUnreachable *)instruction); + case IrInstGenIdCondBr: + return ir_render_cond_br(g, executable, (IrInstGenCondBr *)instruction); + case IrInstGenIdBr: + return ir_render_br(g, executable, (IrInstGenBr *)instruction); + case IrInstGenIdBinaryNot: + return ir_render_binary_not(g, executable, (IrInstGenBinaryNot *)instruction); + case IrInstGenIdNegation: + return ir_render_negation(g, executable, (IrInstGenNegation *)instruction); + case IrInstGenIdNegationWrapping: + return ir_render_negation_wrapping(g, executable, (IrInstGenNegationWrapping *)instruction); + case IrInstGenIdLoadPtr: + return ir_render_load_ptr(g, executable, (IrInstGenLoadPtr *)instruction); + case IrInstGenIdStorePtr: + return ir_render_store_ptr(g, executable, (IrInstGenStorePtr *)instruction); + case IrInstGenIdVectorStoreElem: + return ir_render_vector_store_elem(g, executable, (IrInstGenVectorStoreElem *)instruction); + case IrInstGenIdVarPtr: + return ir_render_var_ptr(g, executable, (IrInstGenVarPtr *)instruction); + case IrInstGenIdReturnPtr: + return ir_render_return_ptr(g, executable, (IrInstGenReturnPtr *)instruction); + case IrInstGenIdElemPtr: + return ir_render_elem_ptr(g, executable, (IrInstGenElemPtr *)instruction); + case IrInstGenIdCall: + return ir_render_call(g, executable, (IrInstGenCall *)instruction); + case IrInstGenIdStructFieldPtr: + return ir_render_struct_field_ptr(g, executable, (IrInstGenStructFieldPtr *)instruction); + case IrInstGenIdUnionFieldPtr: + return ir_render_union_field_ptr(g, executable, (IrInstGenUnionFieldPtr *)instruction); + case IrInstGenIdAsm: + return ir_render_asm_gen(g, executable, (IrInstGenAsm *)instruction); + case IrInstGenIdTestNonNull: + return ir_render_test_non_null(g, executable, (IrInstGenTestNonNull *)instruction); + case IrInstGenIdOptionalUnwrapPtr: + return ir_render_optional_unwrap_ptr(g, executable, (IrInstGenOptionalUnwrapPtr *)instruction); + case IrInstGenIdClz: + return ir_render_clz(g, executable, (IrInstGenClz *)instruction); + case IrInstGenIdCtz: + return ir_render_ctz(g, executable, (IrInstGenCtz *)instruction); + case IrInstGenIdPopCount: + return ir_render_pop_count(g, executable, (IrInstGenPopCount *)instruction); + case IrInstGenIdSwitchBr: + return ir_render_switch_br(g, executable, (IrInstGenSwitchBr *)instruction); + case IrInstGenIdBswap: + return ir_render_bswap(g, executable, (IrInstGenBswap *)instruction); + case IrInstGenIdBitReverse: + return ir_render_bit_reverse(g, executable, (IrInstGenBitReverse *)instruction); + case IrInstGenIdPhi: + return ir_render_phi(g, executable, (IrInstGenPhi *)instruction); + case IrInstGenIdRef: + return ir_render_ref(g, executable, (IrInstGenRef *)instruction); + case IrInstGenIdErrName: + return ir_render_err_name(g, executable, (IrInstGenErrName *)instruction); + case IrInstGenIdCmpxchg: + return ir_render_cmpxchg(g, executable, (IrInstGenCmpxchg *)instruction); + case IrInstGenIdFence: + return ir_render_fence(g, executable, (IrInstGenFence *)instruction); + case IrInstGenIdTruncate: + return ir_render_truncate(g, executable, (IrInstGenTruncate *)instruction); + case IrInstGenIdBoolNot: + return ir_render_bool_not(g, executable, (IrInstGenBoolNot *)instruction); + case IrInstGenIdMemset: + return ir_render_memset(g, executable, (IrInstGenMemset *)instruction); + case IrInstGenIdMemcpy: + return ir_render_memcpy(g, executable, (IrInstGenMemcpy *)instruction); + case IrInstGenIdSlice: + return ir_render_slice(g, executable, (IrInstGenSlice *)instruction); + case IrInstGenIdBreakpoint: + return ir_render_breakpoint(g, executable, (IrInstGenBreakpoint *)instruction); + case IrInstGenIdReturnAddress: + return ir_render_return_address(g, executable, (IrInstGenReturnAddress *)instruction); + case IrInstGenIdFrameAddress: + return ir_render_frame_address(g, executable, (IrInstGenFrameAddress *)instruction); + case IrInstGenIdFrameHandle: + return ir_render_handle(g, executable, (IrInstGenFrameHandle *)instruction); + case IrInstGenIdOverflowOp: + return ir_render_overflow_op(g, executable, (IrInstGenOverflowOp *)instruction); + case IrInstGenIdTestErr: + return ir_render_test_err(g, executable, (IrInstGenTestErr *)instruction); + case IrInstGenIdUnwrapErrCode: + return ir_render_unwrap_err_code(g, executable, (IrInstGenUnwrapErrCode *)instruction); + case IrInstGenIdUnwrapErrPayload: + return ir_render_unwrap_err_payload(g, executable, (IrInstGenUnwrapErrPayload *)instruction); + case IrInstGenIdOptionalWrap: + return ir_render_optional_wrap(g, executable, (IrInstGenOptionalWrap *)instruction); + case IrInstGenIdErrWrapCode: + return ir_render_err_wrap_code(g, executable, (IrInstGenErrWrapCode *)instruction); + case IrInstGenIdErrWrapPayload: + return ir_render_err_wrap_payload(g, executable, (IrInstGenErrWrapPayload *)instruction); + case IrInstGenIdUnionTag: + return ir_render_union_tag(g, executable, (IrInstGenUnionTag *)instruction); + case IrInstGenIdPtrCast: + return ir_render_ptr_cast(g, executable, (IrInstGenPtrCast *)instruction); + case IrInstGenIdBitCast: + return ir_render_bit_cast(g, executable, (IrInstGenBitCast *)instruction); + case IrInstGenIdWidenOrShorten: + return ir_render_widen_or_shorten(g, executable, (IrInstGenWidenOrShorten *)instruction); + case IrInstGenIdPtrToInt: + return ir_render_ptr_to_int(g, executable, (IrInstGenPtrToInt *)instruction); + case IrInstGenIdIntToPtr: + return ir_render_int_to_ptr(g, executable, (IrInstGenIntToPtr *)instruction); + case IrInstGenIdIntToEnum: + return ir_render_int_to_enum(g, executable, (IrInstGenIntToEnum *)instruction); + case IrInstGenIdIntToErr: + return ir_render_int_to_err(g, executable, (IrInstGenIntToErr *)instruction); + case IrInstGenIdErrToInt: + return ir_render_err_to_int(g, executable, (IrInstGenErrToInt *)instruction); + case IrInstGenIdPanic: + return ir_render_panic(g, executable, (IrInstGenPanic *)instruction); + case IrInstGenIdTagName: + return ir_render_enum_tag_name(g, executable, (IrInstGenTagName *)instruction); + case IrInstGenIdFieldParentPtr: + return ir_render_field_parent_ptr(g, executable, (IrInstGenFieldParentPtr *)instruction); + case IrInstGenIdAlignCast: + return ir_render_align_cast(g, executable, (IrInstGenAlignCast *)instruction); + case IrInstGenIdErrorReturnTrace: + return ir_render_error_return_trace(g, executable, (IrInstGenErrorReturnTrace *)instruction); + case IrInstGenIdAtomicRmw: + return ir_render_atomic_rmw(g, executable, (IrInstGenAtomicRmw *)instruction); + case IrInstGenIdAtomicLoad: + return ir_render_atomic_load(g, executable, (IrInstGenAtomicLoad *)instruction); + case IrInstGenIdAtomicStore: + return ir_render_atomic_store(g, executable, (IrInstGenAtomicStore *)instruction); + case IrInstGenIdSaveErrRetAddr: + return ir_render_save_err_ret_addr(g, executable, (IrInstGenSaveErrRetAddr *)instruction); + case IrInstGenIdFloatOp: + return ir_render_float_op(g, executable, (IrInstGenFloatOp *)instruction); + case IrInstGenIdMulAdd: + return ir_render_mul_add(g, executable, (IrInstGenMulAdd *)instruction); + case IrInstGenIdArrayToVector: + return ir_render_array_to_vector(g, executable, (IrInstGenArrayToVector *)instruction); + case IrInstGenIdVectorToArray: + return ir_render_vector_to_array(g, executable, (IrInstGenVectorToArray *)instruction); + case IrInstGenIdAssertZero: + return ir_render_assert_zero(g, executable, (IrInstGenAssertZero *)instruction); + case IrInstGenIdAssertNonNull: + return ir_render_assert_non_null(g, executable, (IrInstGenAssertNonNull *)instruction); + case IrInstGenIdPtrOfArrayToSlice: + return ir_render_ptr_of_array_to_slice(g, executable, (IrInstGenPtrOfArrayToSlice *)instruction); + case IrInstGenIdSuspendBegin: + return ir_render_suspend_begin(g, executable, (IrInstGenSuspendBegin *)instruction); + case IrInstGenIdSuspendFinish: + return ir_render_suspend_finish(g, executable, (IrInstGenSuspendFinish *)instruction); + case IrInstGenIdResume: + return ir_render_resume(g, executable, (IrInstGenResume *)instruction); + case IrInstGenIdFrameSize: + return ir_render_frame_size(g, executable, (IrInstGenFrameSize *)instruction); + case IrInstGenIdAwait: + return ir_render_await(g, executable, (IrInstGenAwait *)instruction); + case IrInstGenIdSpillBegin: + return ir_render_spill_begin(g, executable, (IrInstGenSpillBegin *)instruction); + case IrInstGenIdSpillEnd: + return ir_render_spill_end(g, executable, (IrInstGenSpillEnd *)instruction); + case IrInstGenIdShuffleVector: + return ir_render_shuffle_vector(g, executable, (IrInstGenShuffleVector *) instruction); + case IrInstGenIdSplat: + return ir_render_splat(g, executable, (IrInstGenSplat *) instruction); + case IrInstGenIdVectorExtractElem: + return ir_render_vector_extract_elem(g, executable, (IrInstGenVectorExtractElem *) instruction); + case IrInstGenIdWasmMemorySize: + return ir_render_wasm_memory_size(g, executable, (IrInstGenWasmMemorySize *) instruction); + case IrInstGenIdWasmMemoryGrow: + return ir_render_wasm_memory_grow(g, executable, (IrInstGenWasmMemoryGrow *) instruction); + } + zig_unreachable(); +} + +static void ir_render(CodeGen *g, ZigFn *fn_entry) { + assert(fn_entry); + + IrExecutableGen *executable = &fn_entry->analyzed_executable; + assert(executable->basic_block_list.length > 0); + + for (size_t block_i = 0; block_i < executable->basic_block_list.length; block_i += 1) { + IrBasicBlockGen *current_block = executable->basic_block_list.at(block_i); + if (get_scope_typeof(current_block->scope) != nullptr) { + LLVMBuildBr(g->builder, current_block->llvm_block); + } + assert(current_block->llvm_block); + LLVMPositionBuilderAtEnd(g->builder, current_block->llvm_block); + for (size_t instr_i = 0; instr_i < current_block->instruction_list.length; instr_i += 1) { + IrInstGen *instruction = current_block->instruction_list.at(instr_i); + if (instruction->base.ref_count == 0 && !ir_inst_gen_has_side_effects(instruction)) + continue; + if (get_scope_typeof(instruction->base.scope) != nullptr) + continue; + + if (!g->strip_debug_symbols) { + set_debug_location(g, instruction); + } + instruction->llvm_value = ir_render_instruction(g, executable, instruction); + if (instruction->spill != nullptr && instruction->llvm_value != nullptr) { + LLVMValueRef spill_ptr = ir_llvm_value(g, instruction->spill); + gen_assign_raw(g, spill_ptr, instruction->spill->value->type, instruction->llvm_value); + instruction->llvm_value = nullptr; + } + } + current_block->llvm_exit_block = LLVMGetInsertBlock(g->builder); + } +} + +static LLVMValueRef gen_const_ptr_struct_recursive(CodeGen *g, ZigValue *struct_const_val, size_t field_index); +static LLVMValueRef gen_const_ptr_array_recursive(CodeGen *g, ZigValue *array_const_val, size_t index); +static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ZigValue *union_const_val); +static LLVMValueRef gen_const_ptr_err_union_code_recursive(CodeGen *g, ZigValue *err_union_const_val); +static LLVMValueRef gen_const_ptr_err_union_payload_recursive(CodeGen *g, ZigValue *err_union_const_val); +static LLVMValueRef gen_const_ptr_optional_payload_recursive(CodeGen *g, ZigValue *optional_const_val); + +static LLVMValueRef gen_parent_ptr(CodeGen *g, ZigValue *val, ConstParent *parent) { + switch (parent->id) { + case ConstParentIdNone: + render_const_val(g, val, ""); + render_const_val_global(g, val, ""); + return val->llvm_global; + case ConstParentIdStruct: + return gen_const_ptr_struct_recursive(g, parent->data.p_struct.struct_val, + parent->data.p_struct.field_index); + case ConstParentIdErrUnionCode: + return gen_const_ptr_err_union_code_recursive(g, parent->data.p_err_union_code.err_union_val); + case ConstParentIdErrUnionPayload: + return gen_const_ptr_err_union_payload_recursive(g, parent->data.p_err_union_payload.err_union_val); + case ConstParentIdOptionalPayload: + return gen_const_ptr_optional_payload_recursive(g, parent->data.p_optional_payload.optional_val); + case ConstParentIdArray: + return gen_const_ptr_array_recursive(g, parent->data.p_array.array_val, + parent->data.p_array.elem_index); + case ConstParentIdUnion: + return gen_const_ptr_union_recursive(g, parent->data.p_union.union_val); + case ConstParentIdScalar: + render_const_val(g, parent->data.p_scalar.scalar_val, ""); + render_const_val_global(g, parent->data.p_scalar.scalar_val, ""); + return parent->data.p_scalar.scalar_val->llvm_global; + } + zig_unreachable(); +} + +static LLVMValueRef gen_const_ptr_array_recursive(CodeGen *g, ZigValue *array_const_val, size_t index) { + expand_undef_array(g, array_const_val); + ConstParent *parent = &array_const_val->parent; + LLVMValueRef base_ptr = gen_parent_ptr(g, array_const_val, parent); + + LLVMTypeKind el_type = LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(base_ptr))); + if (el_type == LLVMArrayTypeKind) { + ZigType *usize = g->builtin_types.entry_usize; + LLVMValueRef indices[] = { + LLVMConstNull(usize->llvm_type), + LLVMConstInt(usize->llvm_type, index, false), + }; + return LLVMConstInBoundsGEP(base_ptr, indices, 2); + } else if (el_type == LLVMStructTypeKind) { + ZigType *u32 = g->builtin_types.entry_u32; + LLVMValueRef indices[] = { + LLVMConstNull(get_llvm_type(g, u32)), + LLVMConstInt(get_llvm_type(g, u32), index, false), + }; + return LLVMConstInBoundsGEP(base_ptr, indices, 2); + } else { + return base_ptr; + } +} + +static LLVMValueRef gen_const_ptr_struct_recursive(CodeGen *g, ZigValue *struct_const_val, size_t field_index) { + ConstParent *parent = &struct_const_val->parent; + LLVMValueRef base_ptr = gen_parent_ptr(g, struct_const_val, parent); + + ZigType *u32 = g->builtin_types.entry_u32; + LLVMValueRef indices[] = { + LLVMConstNull(get_llvm_type(g, u32)), + LLVMConstInt(get_llvm_type(g, u32), field_index, false), + }; + return LLVMConstInBoundsGEP(base_ptr, indices, 2); +} + +static LLVMValueRef gen_const_ptr_err_union_code_recursive(CodeGen *g, ZigValue *err_union_const_val) { + ConstParent *parent = &err_union_const_val->parent; + LLVMValueRef base_ptr = gen_parent_ptr(g, err_union_const_val, parent); + + ZigType *u32 = g->builtin_types.entry_u32; + LLVMValueRef indices[] = { + LLVMConstNull(get_llvm_type(g, u32)), + LLVMConstInt(get_llvm_type(g, u32), err_union_err_index, false), + }; + return LLVMConstInBoundsGEP(base_ptr, indices, 2); +} + +static LLVMValueRef gen_const_ptr_err_union_payload_recursive(CodeGen *g, ZigValue *err_union_const_val) { + ConstParent *parent = &err_union_const_val->parent; + LLVMValueRef base_ptr = gen_parent_ptr(g, err_union_const_val, parent); + + ZigType *u32 = g->builtin_types.entry_u32; + LLVMValueRef indices[] = { + LLVMConstNull(get_llvm_type(g, u32)), + LLVMConstInt(get_llvm_type(g, u32), err_union_payload_index, false), + }; + return LLVMConstInBoundsGEP(base_ptr, indices, 2); +} + +static LLVMValueRef gen_const_ptr_optional_payload_recursive(CodeGen *g, ZigValue *optional_const_val) { + ConstParent *parent = &optional_const_val->parent; + LLVMValueRef base_ptr = gen_parent_ptr(g, optional_const_val, parent); + + ZigType *u32 = g->builtin_types.entry_u32; + LLVMValueRef indices[] = { + LLVMConstNull(get_llvm_type(g, u32)), + LLVMConstInt(get_llvm_type(g, u32), maybe_child_index, false), + }; + return LLVMConstInBoundsGEP(base_ptr, indices, 2); +} + +static LLVMValueRef gen_const_ptr_union_recursive(CodeGen *g, ZigValue *union_const_val) { + ConstParent *parent = &union_const_val->parent; + LLVMValueRef base_ptr = gen_parent_ptr(g, union_const_val, parent); + + // Slot in the structure where the payload is stored, if equal to SIZE_MAX + // the union has no tag and a single field and is collapsed into the field + // itself + size_t union_payload_index = union_const_val->type->data.unionation.gen_union_index; + + ZigType *u32 = g->builtin_types.entry_u32; + LLVMValueRef indices[] = { + LLVMConstNull(get_llvm_type(g, u32)), + LLVMConstInt(get_llvm_type(g, u32), union_payload_index, false), + }; + return LLVMConstInBoundsGEP(base_ptr, indices, (union_payload_index != SIZE_MAX) ? 2 : 1); +} + +static LLVMValueRef pack_const_int(CodeGen *g, LLVMTypeRef big_int_type_ref, ZigValue *const_val) { + switch (const_val->special) { + case ConstValSpecialLazy: + case ConstValSpecialRuntime: + zig_unreachable(); + case ConstValSpecialUndef: + return LLVMConstInt(big_int_type_ref, 0, false); + case ConstValSpecialStatic: + break; + } + + ZigType *type_entry = const_val->type; + assert(type_has_bits(g, type_entry)); + switch (type_entry->id) { + case ZigTypeIdInvalid: + case ZigTypeIdMetaType: + case ZigTypeIdUnreachable: + case ZigTypeIdComptimeFloat: + case ZigTypeIdComptimeInt: + case ZigTypeIdEnumLiteral: + case ZigTypeIdUndefined: + case ZigTypeIdNull: + case ZigTypeIdErrorUnion: + case ZigTypeIdErrorSet: + case ZigTypeIdBoundFn: + case ZigTypeIdVoid: + case ZigTypeIdOpaque: + zig_unreachable(); + case ZigTypeIdBool: + return LLVMConstInt(big_int_type_ref, const_val->data.x_bool ? 1 : 0, false); + case ZigTypeIdEnum: + { + assert(type_entry->data.enumeration.decl_node->data.container_decl.init_arg_expr != nullptr); + LLVMValueRef int_val = gen_const_val(g, const_val, ""); + return LLVMConstZExt(int_val, big_int_type_ref); + } + case ZigTypeIdInt: + { + LLVMValueRef int_val = gen_const_val(g, const_val, ""); + return LLVMConstZExt(int_val, big_int_type_ref); + } + case ZigTypeIdFloat: + { + LLVMValueRef float_val = gen_const_val(g, const_val, ""); + LLVMValueRef int_val = LLVMConstFPToUI(float_val, + LLVMIntType((unsigned)type_entry->data.floating.bit_count)); + return LLVMConstZExt(int_val, big_int_type_ref); + } + case ZigTypeIdPointer: + case ZigTypeIdFn: + case ZigTypeIdOptional: + { + LLVMValueRef ptr_val = gen_const_val(g, const_val, ""); + LLVMValueRef ptr_size_int_val = LLVMConstPtrToInt(ptr_val, g->builtin_types.entry_usize->llvm_type); + return LLVMConstZExt(ptr_size_int_val, big_int_type_ref); + } + case ZigTypeIdArray: { + LLVMValueRef val = LLVMConstInt(big_int_type_ref, 0, false); + if (const_val->data.x_array.special == ConstArraySpecialUndef) { + return val; + } + expand_undef_array(g, const_val); + bool is_big_endian = g->is_big_endian; // TODO get endianness from struct type + uint32_t packed_bits_size = type_size_bits(g, type_entry->data.array.child_type); + size_t used_bits = 0; + for (size_t i = 0; i < type_entry->data.array.len; i += 1) { + ZigValue *elem_val = &const_val->data.x_array.data.s_none.elements[i]; + LLVMValueRef child_val = pack_const_int(g, big_int_type_ref, elem_val); + + if (is_big_endian) { + LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, packed_bits_size, false); + val = LLVMConstShl(val, shift_amt); + val = LLVMConstOr(val, child_val); + } else { + LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, used_bits, false); + LLVMValueRef child_val_shifted = LLVMConstShl(child_val, shift_amt); + val = LLVMConstOr(val, child_val_shifted); + used_bits += packed_bits_size; + } + } + + if (type_entry->data.array.sentinel != nullptr) { + ZigValue *elem_val = type_entry->data.array.sentinel; + LLVMValueRef child_val = pack_const_int(g, big_int_type_ref, elem_val); + + if (is_big_endian) { + LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, packed_bits_size, false); + val = LLVMConstShl(val, shift_amt); + val = LLVMConstOr(val, child_val); + } else { + LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, used_bits, false); + LLVMValueRef child_val_shifted = LLVMConstShl(child_val, shift_amt); + val = LLVMConstOr(val, child_val_shifted); + used_bits += packed_bits_size; + } + } + return val; + } + case ZigTypeIdVector: + zig_panic("TODO bit pack a vector"); + case ZigTypeIdUnion: + zig_panic("TODO bit pack a union"); + case ZigTypeIdStruct: + { + assert(type_entry->data.structure.layout == ContainerLayoutPacked); + bool is_big_endian = g->is_big_endian; // TODO get endianness from struct type + + LLVMValueRef val = LLVMConstInt(big_int_type_ref, 0, false); + size_t used_bits = 0; + 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->gen_index == SIZE_MAX) { + continue; + } + LLVMValueRef child_val = pack_const_int(g, big_int_type_ref, const_val->data.x_struct.fields[i]); + uint32_t packed_bits_size = type_size_bits(g, field->type_entry); + if (is_big_endian) { + LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, packed_bits_size, false); + val = LLVMConstShl(val, shift_amt); + val = LLVMConstOr(val, child_val); + } else { + LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, used_bits, false); + LLVMValueRef child_val_shifted = LLVMConstShl(child_val, shift_amt); + val = LLVMConstOr(val, child_val_shifted); + used_bits += packed_bits_size; + } + } + return val; + } + case ZigTypeIdFnFrame: + zig_panic("TODO bit pack an async function frame"); + case ZigTypeIdAnyFrame: + zig_panic("TODO bit pack an anyframe"); + } + zig_unreachable(); +} + +// We have this because union constants can't be represented by the official union type, +// and this property bubbles up in whatever aggregate type contains a union constant +static bool is_llvm_value_unnamed_type(CodeGen *g, ZigType *type_entry, LLVMValueRef val) { + return LLVMTypeOf(val) != get_llvm_type(g, type_entry); +} + +static LLVMValueRef gen_const_val_ptr(CodeGen *g, ZigValue *const_val, const char *name) { + switch (const_val->data.x_ptr.special) { + case ConstPtrSpecialInvalid: + case ConstPtrSpecialDiscard: + zig_unreachable(); + case ConstPtrSpecialRef: + { + ZigValue *pointee = const_val->data.x_ptr.data.ref.pointee; + render_const_val(g, pointee, ""); + render_const_val_global(g, pointee, ""); + const_val->llvm_value = LLVMConstBitCast(pointee->llvm_global, + get_llvm_type(g, const_val->type)); + return const_val->llvm_value; + } + case ConstPtrSpecialBaseArray: + case ConstPtrSpecialSubArray: + { + ZigValue *array_const_val = const_val->data.x_ptr.data.base_array.array_val; + assert(array_const_val->type->id == ZigTypeIdArray); + if (!type_has_bits(g, array_const_val->type)) { + // make this a null pointer + ZigType *usize = g->builtin_types.entry_usize; + const_val->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->llvm_type), + get_llvm_type(g, const_val->type)); + return const_val->llvm_value; + } + size_t elem_index = const_val->data.x_ptr.data.base_array.elem_index; + LLVMValueRef uncasted_ptr_val = gen_const_ptr_array_recursive(g, array_const_val, elem_index); + LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, get_llvm_type(g, const_val->type)); + const_val->llvm_value = ptr_val; + return ptr_val; + } + case ConstPtrSpecialBaseStruct: + { + ZigValue *struct_const_val = const_val->data.x_ptr.data.base_struct.struct_val; + assert(struct_const_val->type->id == ZigTypeIdStruct); + if (!type_has_bits(g, struct_const_val->type)) { + // make this a null pointer + ZigType *usize = g->builtin_types.entry_usize; + const_val->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->llvm_type), + get_llvm_type(g, const_val->type)); + return const_val->llvm_value; + } + size_t src_field_index = const_val->data.x_ptr.data.base_struct.field_index; + size_t gen_field_index = struct_const_val->type->data.structure.fields[src_field_index]->gen_index; + LLVMValueRef uncasted_ptr_val = gen_const_ptr_struct_recursive(g, struct_const_val, + gen_field_index); + LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, get_llvm_type(g, const_val->type)); + const_val->llvm_value = ptr_val; + return ptr_val; + } + case ConstPtrSpecialBaseErrorUnionCode: + { + ZigValue *err_union_const_val = const_val->data.x_ptr.data.base_err_union_code.err_union_val; + assert(err_union_const_val->type->id == ZigTypeIdErrorUnion); + if (!type_has_bits(g, err_union_const_val->type)) { + // make this a null pointer + ZigType *usize = g->builtin_types.entry_usize; + const_val->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->llvm_type), + get_llvm_type(g, const_val->type)); + return const_val->llvm_value; + } + LLVMValueRef uncasted_ptr_val = gen_const_ptr_err_union_code_recursive(g, err_union_const_val); + LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, get_llvm_type(g, const_val->type)); + const_val->llvm_value = ptr_val; + return ptr_val; + } + case ConstPtrSpecialBaseErrorUnionPayload: + { + ZigValue *err_union_const_val = const_val->data.x_ptr.data.base_err_union_payload.err_union_val; + assert(err_union_const_val->type->id == ZigTypeIdErrorUnion); + if (!type_has_bits(g, err_union_const_val->type)) { + // make this a null pointer + ZigType *usize = g->builtin_types.entry_usize; + const_val->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->llvm_type), + get_llvm_type(g, const_val->type)); + return const_val->llvm_value; + } + LLVMValueRef uncasted_ptr_val = gen_const_ptr_err_union_payload_recursive(g, err_union_const_val); + LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, get_llvm_type(g, const_val->type)); + const_val->llvm_value = ptr_val; + return ptr_val; + } + case ConstPtrSpecialBaseOptionalPayload: + { + ZigValue *optional_const_val = const_val->data.x_ptr.data.base_optional_payload.optional_val; + assert(optional_const_val->type->id == ZigTypeIdOptional); + if (!type_has_bits(g, optional_const_val->type)) { + // make this a null pointer + ZigType *usize = g->builtin_types.entry_usize; + const_val->llvm_value = LLVMConstIntToPtr(LLVMConstNull(usize->llvm_type), + get_llvm_type(g, const_val->type)); + return const_val->llvm_value; + } + LLVMValueRef uncasted_ptr_val = gen_const_ptr_optional_payload_recursive(g, optional_const_val); + LLVMValueRef ptr_val = LLVMConstBitCast(uncasted_ptr_val, get_llvm_type(g, const_val->type)); + const_val->llvm_value = ptr_val; + return ptr_val; + } + case ConstPtrSpecialHardCodedAddr: + { + uint64_t addr_value = const_val->data.x_ptr.data.hard_coded_addr.addr; + ZigType *usize = g->builtin_types.entry_usize; + const_val->llvm_value = LLVMConstIntToPtr( + LLVMConstInt(usize->llvm_type, addr_value, false), get_llvm_type(g, const_val->type)); + return const_val->llvm_value; + } + case ConstPtrSpecialFunction: + return LLVMConstBitCast(fn_llvm_value(g, const_val->data.x_ptr.data.fn.fn_entry), + get_llvm_type(g, const_val->type)); + case ConstPtrSpecialNull: + return LLVMConstNull(get_llvm_type(g, const_val->type)); + } + zig_unreachable(); +} + +static LLVMValueRef gen_const_val_err_set(CodeGen *g, ZigValue *const_val, const char *name) { + uint64_t value = (const_val->data.x_err_set == nullptr) ? 0 : const_val->data.x_err_set->value; + return LLVMConstInt(get_llvm_type(g, g->builtin_types.entry_global_error_set), value, false); +} + +static LLVMValueRef gen_const_val(CodeGen *g, ZigValue *const_val, const char *name) { + Error err; + + ZigType *type_entry = const_val->type; + assert(type_has_bits(g, type_entry)); + + if (const_val->special == ConstValSpecialLazy && + (err = ir_resolve_lazy(g, nullptr, const_val))) + codegen_report_errors_and_exit(g); + + switch (const_val->special) { + case ConstValSpecialLazy: + case ConstValSpecialRuntime: + zig_unreachable(); + case ConstValSpecialUndef: + return LLVMGetUndef(get_llvm_type(g, type_entry)); + case ConstValSpecialStatic: + break; + } + + if ((err = type_resolve(g, type_entry, ResolveStatusLLVMFull))) + zig_unreachable(); + + switch (type_entry->id) { + case ZigTypeIdInt: + return bigint_to_llvm_const(get_llvm_type(g, type_entry), &const_val->data.x_bigint); + case ZigTypeIdErrorSet: + return gen_const_val_err_set(g, const_val, name); + case ZigTypeIdFloat: + switch (type_entry->data.floating.bit_count) { + case 16: + return LLVMConstReal(get_llvm_type(g, type_entry), zig_f16_to_double(const_val->data.x_f16)); + case 32: + return LLVMConstReal(get_llvm_type(g, type_entry), const_val->data.x_f32); + case 64: + return LLVMConstReal(get_llvm_type(g, type_entry), const_val->data.x_f64); + case 128: + { + // TODO make sure this is correct on big endian targets too + uint8_t buf[16]; + memcpy(buf, &const_val->data.x_f128, 16); + LLVMValueRef as_int = LLVMConstIntOfArbitraryPrecision(LLVMInt128Type(), 2, + (uint64_t*)buf); + return LLVMConstBitCast(as_int, get_llvm_type(g, type_entry)); + } + default: + zig_unreachable(); + } + case ZigTypeIdBool: + if (const_val->data.x_bool) { + return LLVMConstAllOnes(LLVMInt1Type()); + } else { + return LLVMConstNull(LLVMInt1Type()); + } + case ZigTypeIdOptional: + { + ZigType *child_type = type_entry->data.maybe.child_type; + + if (get_src_ptr_type(type_entry) != nullptr) { + bool has_bits; + if ((err = type_has_bits2(g, child_type, &has_bits))) + codegen_report_errors_and_exit(g); + + if (has_bits) + return gen_const_val_ptr(g, const_val, name); + + // No bits, treat this value as a boolean + const unsigned bool_val = optional_value_is_null(const_val) ? 0 : 1; + return LLVMConstInt(LLVMInt1Type(), bool_val, false); + } else if (child_type->id == ZigTypeIdErrorSet) { + return gen_const_val_err_set(g, const_val, name); + } else if (!type_has_bits(g, child_type)) { + return LLVMConstInt(LLVMInt1Type(), const_val->data.x_optional ? 1 : 0, false); + } else { + LLVMValueRef child_val; + LLVMValueRef maybe_val; + bool make_unnamed_struct; + if (const_val->data.x_optional) { + child_val = gen_const_val(g, const_val->data.x_optional, ""); + maybe_val = LLVMConstAllOnes(LLVMInt1Type()); + + make_unnamed_struct = is_llvm_value_unnamed_type(g, const_val->type, child_val); + } else { + child_val = LLVMGetUndef(get_llvm_type(g, child_type)); + maybe_val = LLVMConstNull(LLVMInt1Type()); + + make_unnamed_struct = false; + } + + LLVMValueRef fields[] = { + child_val, + maybe_val, + nullptr, + }; + if (make_unnamed_struct) { + LLVMValueRef result = LLVMConstStruct(fields, 2, false); + uint64_t last_field_offset = LLVMOffsetOfElement(g->target_data_ref, LLVMTypeOf(result), 1); + uint64_t end_offset = last_field_offset + + LLVMStoreSizeOfType(g->target_data_ref, LLVMTypeOf(fields[1])); + uint64_t expected_sz = LLVMABISizeOfType(g->target_data_ref, get_llvm_type(g, type_entry)); + unsigned pad_sz = expected_sz - end_offset; + if (pad_sz != 0) { + fields[2] = LLVMGetUndef(LLVMArrayType(LLVMInt8Type(), pad_sz)); + result = LLVMConstStruct(fields, 3, false); + } + uint64_t actual_sz = LLVMStoreSizeOfType(g->target_data_ref, LLVMTypeOf(result)); + assert(actual_sz == expected_sz); + return result; + } else { + return LLVMConstNamedStruct(get_llvm_type(g, type_entry), fields, 2); + } + } + } + case ZigTypeIdStruct: + { + LLVMValueRef *fields = heap::c_allocator.allocate(type_entry->data.structure.gen_field_count); + size_t src_field_count = type_entry->data.structure.src_field_count; + bool make_unnamed_struct = false; + assert(type_entry->data.structure.resolve_status == ResolveStatusLLVMFull); + if (type_entry->data.structure.layout == ContainerLayoutPacked) { + size_t src_field_index = 0; + while (src_field_index < src_field_count) { + TypeStructField *type_struct_field = type_entry->data.structure.fields[src_field_index]; + if (type_struct_field->gen_index == SIZE_MAX) { + src_field_index += 1; + continue; + } + + size_t src_field_index_end = src_field_index + 1; + for (; src_field_index_end < src_field_count; src_field_index_end += 1) { + TypeStructField *it_field = type_entry->data.structure.fields[src_field_index_end]; + if (it_field->gen_index != type_struct_field->gen_index) + break; + } + + if (src_field_index + 1 == src_field_index_end) { + ZigValue *field_val = const_val->data.x_struct.fields[src_field_index]; + LLVMValueRef val = gen_const_val(g, field_val, ""); + fields[type_struct_field->gen_index] = val; + make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(g, field_val->type, val); + } else { + bool is_big_endian = g->is_big_endian; // TODO get endianness from struct type + LLVMTypeRef field_ty = LLVMStructGetTypeAtIndex(get_llvm_type(g, type_entry), + (unsigned)type_struct_field->gen_index); + const size_t size_in_bytes = LLVMStoreSizeOfType(g->target_data_ref, field_ty); + const size_t size_in_bits = size_in_bytes * 8; + LLVMTypeRef big_int_type_ref = LLVMIntType(size_in_bits); + LLVMValueRef val = LLVMConstInt(big_int_type_ref, 0, false); + size_t used_bits = 0; + for (size_t i = src_field_index; i < src_field_index_end; i += 1) { + TypeStructField *it_field = type_entry->data.structure.fields[i]; + if (it_field->gen_index == SIZE_MAX) { + continue; + } + LLVMValueRef child_val = pack_const_int(g, big_int_type_ref, + const_val->data.x_struct.fields[i]); + uint32_t packed_bits_size = type_size_bits(g, it_field->type_entry); + if (is_big_endian) { + LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, + size_in_bits - used_bits - packed_bits_size, false); + LLVMValueRef child_val_shifted = LLVMConstShl(child_val, shift_amt); + val = LLVMConstOr(val, child_val_shifted); + } else { + LLVMValueRef shift_amt = LLVMConstInt(big_int_type_ref, used_bits, false); + LLVMValueRef child_val_shifted = LLVMConstShl(child_val, shift_amt); + val = LLVMConstOr(val, child_val_shifted); + } + used_bits += packed_bits_size; + } + assert(size_in_bits >= used_bits); + if (LLVMGetTypeKind(field_ty) != LLVMArrayTypeKind) { + assert(LLVMGetTypeKind(field_ty) == LLVMIntegerTypeKind); + fields[type_struct_field->gen_index] = val; + } else { + const LLVMValueRef AMT = LLVMConstInt(LLVMTypeOf(val), 8, false); + + LLVMValueRef *values = heap::c_allocator.allocate(size_in_bytes); + for (size_t i = 0; i < size_in_bytes; i++) { + const size_t idx = is_big_endian ? size_in_bytes - 1 - i : i; + values[idx] = LLVMConstTruncOrBitCast(val, LLVMInt8Type()); + val = LLVMConstLShr(val, AMT); + } + + fields[type_struct_field->gen_index] = LLVMConstArray(LLVMInt8Type(), values, size_in_bytes); + } + } + + src_field_index = src_field_index_end; + } + } else { + for (uint32_t i = 0; i < src_field_count; i += 1) { + TypeStructField *type_struct_field = type_entry->data.structure.fields[i]; + if (type_struct_field->gen_index == SIZE_MAX) { + continue; + } + ZigValue *field_val = const_val->data.x_struct.fields[i]; + if (field_val == nullptr) { + add_node_error(g, type_struct_field->decl_node, + buf_sprintf("compiler bug: generating const value for struct field '%s'", + buf_ptr(type_struct_field->name))); + codegen_report_errors_and_exit(g); + } + ZigType *field_type = field_val->type; + assert(field_type != nullptr); + if ((err = ensure_const_val_repr(nullptr, g, nullptr, field_val, field_type))) { + zig_unreachable(); + } + + LLVMValueRef val = gen_const_val(g, field_val, ""); + make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(g, field_type, val); + + // Find the next runtime field + size_t next_rt_gen_index = type_entry->data.structure.gen_field_count; + size_t next_offset = type_entry->abi_size; + for (size_t j = i + 1; j < src_field_count; j++) { + const size_t index = type_entry->data.structure.fields[j]->gen_index; + const size_t offset = type_entry->data.structure.fields[j]->offset; + + if (index != SIZE_MAX) { + next_rt_gen_index = index; + next_offset = offset; + break; + } + } + + // How much padding is needed to reach the next field + const size_t pad_bytes = next_offset - + (type_struct_field->offset + LLVMABISizeOfType(g->target_data_ref, LLVMTypeOf(val))); + // Catch underflow + assert((ssize_t)pad_bytes >= 0); + + if (type_struct_field->gen_index + 1 != next_rt_gen_index) { + // If there's a hole between this field and the next + // we have an alignment gap to fill + fields[type_struct_field->gen_index] = val; + fields[type_struct_field->gen_index + 1] = LLVMGetUndef(LLVMArrayType(LLVMInt8Type(), pad_bytes)); + } else if (pad_bytes != 0) { + LLVMValueRef padded_val[] = { + val, + LLVMGetUndef(LLVMArrayType(LLVMInt8Type(), pad_bytes)), + }; + fields[type_struct_field->gen_index] = LLVMConstStruct(padded_val, 2, true); + make_unnamed_struct = true; + } else { + fields[type_struct_field->gen_index] = val; + } + } + } + if (make_unnamed_struct) { + return LLVMConstStruct(fields, type_entry->data.structure.gen_field_count, + type_entry->data.structure.layout == ContainerLayoutPacked); + } else { + return LLVMConstNamedStruct(get_llvm_type(g, type_entry), fields, type_entry->data.structure.gen_field_count); + } + } + case ZigTypeIdArray: + { + uint64_t len = type_entry->data.array.len; + switch (const_val->data.x_array.special) { + case ConstArraySpecialUndef: + return LLVMGetUndef(get_llvm_type(g, type_entry)); + case ConstArraySpecialNone: { + uint64_t extra_len_from_sentinel = (type_entry->data.array.sentinel != nullptr) ? 1 : 0; + uint64_t full_len = len + extra_len_from_sentinel; + LLVMValueRef *values = heap::c_allocator.allocate(full_len); + LLVMTypeRef element_type_ref = get_llvm_type(g, type_entry->data.array.child_type); + bool make_unnamed_struct = false; + for (uint64_t i = 0; i < len; i += 1) { + ZigValue *elem_value = &const_val->data.x_array.data.s_none.elements[i]; + LLVMValueRef val = gen_const_val(g, elem_value, ""); + values[i] = val; + make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(g, elem_value->type, val); + } + if (type_entry->data.array.sentinel != nullptr) { + values[len] = gen_const_val(g, type_entry->data.array.sentinel, ""); + } + if (make_unnamed_struct) { + return LLVMConstStruct(values, full_len, true); + } else { + return LLVMConstArray(element_type_ref, values, (unsigned)full_len); + } + } + case ConstArraySpecialBuf: { + Buf *buf = const_val->data.x_array.data.s_buf; + return LLVMConstString(buf_ptr(buf), (unsigned)buf_len(buf), + type_entry->data.array.sentinel == nullptr); + } + } + zig_unreachable(); + } + case ZigTypeIdVector: { + uint32_t len = type_entry->data.vector.len; + switch (const_val->data.x_array.special) { + case ConstArraySpecialUndef: + return LLVMGetUndef(get_llvm_type(g, type_entry)); + case ConstArraySpecialNone: { + LLVMValueRef *values = heap::c_allocator.allocate(len); + for (uint64_t i = 0; i < len; i += 1) { + ZigValue *elem_value = &const_val->data.x_array.data.s_none.elements[i]; + values[i] = gen_const_val(g, elem_value, ""); + } + return LLVMConstVector(values, len); + } + case ConstArraySpecialBuf: { + Buf *buf = const_val->data.x_array.data.s_buf; + assert(buf_len(buf) == len); + LLVMValueRef *values = heap::c_allocator.allocate(len); + for (uint64_t i = 0; i < len; i += 1) { + values[i] = LLVMConstInt(g->builtin_types.entry_u8->llvm_type, buf_ptr(buf)[i], false); + } + return LLVMConstVector(values, len); + } + } + zig_unreachable(); + } + case ZigTypeIdUnion: + { + // Force type_entry->data.unionation.union_llvm_type to get resolved + (void)get_llvm_type(g, type_entry); + + if (type_entry->data.unionation.gen_field_count == 0) { + if (type_entry->data.unionation.tag_type == nullptr) { + return nullptr; + } else { + return bigint_to_llvm_const(get_llvm_type(g, type_entry->data.unionation.tag_type), + &const_val->data.x_union.tag); + } + } + + LLVMTypeRef union_type_ref = type_entry->data.unionation.union_llvm_type; + assert(union_type_ref != nullptr); + + LLVMValueRef union_value_ref; + bool make_unnamed_struct; + ZigValue *payload_value = const_val->data.x_union.payload; + if (payload_value == nullptr || !type_has_bits(g, payload_value->type)) { + if (type_entry->data.unionation.gen_tag_index == SIZE_MAX) + return LLVMGetUndef(get_llvm_type(g, type_entry)); + + union_value_ref = LLVMGetUndef(union_type_ref); + make_unnamed_struct = false; + } else { + uint64_t field_type_bytes = LLVMABISizeOfType(g->target_data_ref, + get_llvm_type(g, payload_value->type)); + uint64_t pad_bytes = type_entry->data.unionation.union_abi_size - field_type_bytes; + LLVMValueRef correctly_typed_value = gen_const_val(g, payload_value, ""); + make_unnamed_struct = is_llvm_value_unnamed_type(g, payload_value->type, correctly_typed_value) || + payload_value->type != type_entry->data.unionation.most_aligned_union_member->type_entry; + + { + if (pad_bytes == 0) { + union_value_ref = correctly_typed_value; + } else { + LLVMValueRef fields[2]; + fields[0] = correctly_typed_value; + fields[1] = LLVMGetUndef(LLVMArrayType(LLVMInt8Type(), (unsigned)pad_bytes)); + if (make_unnamed_struct || type_entry->data.unionation.gen_tag_index != SIZE_MAX) { + union_value_ref = LLVMConstStruct(fields, 2, false); + } else { + union_value_ref = LLVMConstNamedStruct(union_type_ref, fields, 2); + } + } + } + + if (type_entry->data.unionation.gen_tag_index == SIZE_MAX) { + return union_value_ref; + } + } + + LLVMValueRef tag_value = bigint_to_llvm_const( + get_llvm_type(g, type_entry->data.unionation.tag_type), + &const_val->data.x_union.tag); + + LLVMValueRef fields[3]; + fields[type_entry->data.unionation.gen_union_index] = union_value_ref; + fields[type_entry->data.unionation.gen_tag_index] = tag_value; + + if (make_unnamed_struct) { + LLVMValueRef result = LLVMConstStruct(fields, 2, false); + uint64_t last_field_offset = LLVMOffsetOfElement(g->target_data_ref, LLVMTypeOf(result), 1); + uint64_t end_offset = last_field_offset + + LLVMStoreSizeOfType(g->target_data_ref, LLVMTypeOf(fields[1])); + uint64_t expected_sz = LLVMABISizeOfType(g->target_data_ref, get_llvm_type(g, type_entry)); + unsigned pad_sz = expected_sz - end_offset; + if (pad_sz != 0) { + fields[2] = LLVMGetUndef(LLVMArrayType(LLVMInt8Type(), pad_sz)); + result = LLVMConstStruct(fields, 3, false); + } + uint64_t actual_sz = LLVMStoreSizeOfType(g->target_data_ref, LLVMTypeOf(result)); + assert(actual_sz == expected_sz); + return result; + } else { + return LLVMConstNamedStruct(get_llvm_type(g, type_entry), fields, 2); + } + + } + + case ZigTypeIdEnum: + return bigint_to_llvm_const(get_llvm_type(g, type_entry), &const_val->data.x_enum_tag); + case ZigTypeIdFn: + if (const_val->data.x_ptr.special == ConstPtrSpecialFunction && + const_val->data.x_ptr.mut != ConstPtrMutComptimeConst) { + zig_unreachable(); + } + // Treat it the same as we do for pointers + return gen_const_val_ptr(g, const_val, name); + case ZigTypeIdPointer: + return gen_const_val_ptr(g, const_val, name); + case ZigTypeIdErrorUnion: + { + ZigType *payload_type = type_entry->data.error_union.payload_type; + ZigType *err_set_type = type_entry->data.error_union.err_set_type; + if (!type_has_bits(g, payload_type)) { + assert(type_has_bits(g, err_set_type)); + ErrorTableEntry *err_set = const_val->data.x_err_union.error_set->data.x_err_set; + uint64_t value = (err_set == nullptr) ? 0 : err_set->value; + return LLVMConstInt(get_llvm_type(g, g->err_tag_type), value, false); + } else if (!type_has_bits(g, err_set_type)) { + assert(type_has_bits(g, payload_type)); + return gen_const_val(g, const_val->data.x_err_union.payload, ""); + } else { + LLVMValueRef err_tag_value; + LLVMValueRef err_payload_value; + bool make_unnamed_struct; + ErrorTableEntry *err_set = const_val->data.x_err_union.error_set->data.x_err_set; + if (err_set != nullptr) { + err_tag_value = LLVMConstInt(get_llvm_type(g, g->err_tag_type), err_set->value, false); + err_payload_value = LLVMConstNull(get_llvm_type(g, payload_type)); + make_unnamed_struct = false; + } else { + err_tag_value = LLVMConstNull(get_llvm_type(g, g->err_tag_type)); + ZigValue *payload_val = const_val->data.x_err_union.payload; + err_payload_value = gen_const_val(g, payload_val, ""); + make_unnamed_struct = is_llvm_value_unnamed_type(g, payload_val->type, err_payload_value); + } + LLVMValueRef fields[3]; + fields[err_union_err_index] = err_tag_value; + fields[err_union_payload_index] = err_payload_value; + size_t field_count = 2; + if (type_entry->data.error_union.pad_llvm_type != nullptr) { + fields[2] = LLVMGetUndef(type_entry->data.error_union.pad_llvm_type); + field_count = 3; + } + if (make_unnamed_struct) { + return LLVMConstStruct(fields, field_count, false); + } else { + return LLVMConstNamedStruct(get_llvm_type(g, type_entry), fields, field_count); + } + } + } + case ZigTypeIdVoid: + return nullptr; + case ZigTypeIdInvalid: + case ZigTypeIdMetaType: + case ZigTypeIdUnreachable: + case ZigTypeIdComptimeFloat: + case ZigTypeIdComptimeInt: + case ZigTypeIdEnumLiteral: + case ZigTypeIdUndefined: + case ZigTypeIdNull: + case ZigTypeIdBoundFn: + case ZigTypeIdOpaque: + zig_unreachable(); + case ZigTypeIdFnFrame: + zig_panic("TODO"); + case ZigTypeIdAnyFrame: + zig_panic("TODO"); + } + zig_unreachable(); +} + +static void render_const_val(CodeGen *g, ZigValue *const_val, const char *name) { + if (!const_val->llvm_value) + const_val->llvm_value = gen_const_val(g, const_val, name); + + if (const_val->llvm_global) + LLVMSetInitializer(const_val->llvm_global, const_val->llvm_value); +} + +static void render_const_val_global(CodeGen *g, ZigValue *const_val, const char *name) { + if (!const_val->llvm_global) { + LLVMTypeRef type_ref = const_val->llvm_value ? + LLVMTypeOf(const_val->llvm_value) : get_llvm_type(g, const_val->type); + LLVMValueRef global_value = LLVMAddGlobal(g->module, type_ref, name); + LLVMSetLinkage(global_value, (name == nullptr) ? LLVMPrivateLinkage : LLVMInternalLinkage); + LLVMSetGlobalConstant(global_value, true); + LLVMSetUnnamedAddr(global_value, true); + LLVMSetAlignment(global_value, (const_val->llvm_align == 0) ? + get_abi_alignment(g, const_val->type) : const_val->llvm_align); + + const_val->llvm_global = global_value; + } + + if (const_val->llvm_value) + LLVMSetInitializer(const_val->llvm_global, const_val->llvm_value); +} + +static void generate_error_name_table(CodeGen *g) { + if (g->err_name_table != nullptr || !g->generate_error_name_table || g->errors_by_index.length == 1) { + return; + } + + assert(g->errors_by_index.length > 0); + + ZigType *u8_ptr_type = get_pointer_to_type_extra(g, g->builtin_types.entry_u8, true, false, + PtrLenUnknown, get_abi_alignment(g, g->builtin_types.entry_u8), 0, 0, false); + ZigType *str_type = get_slice_type(g, u8_ptr_type); + + LLVMValueRef *values = heap::c_allocator.allocate(g->errors_by_index.length); + values[0] = LLVMGetUndef(get_llvm_type(g, str_type)); + for (size_t i = 1; i < g->errors_by_index.length; i += 1) { + ErrorTableEntry *err_entry = g->errors_by_index.at(i); + Buf *name = &err_entry->name; + + g->largest_err_name_len = max(g->largest_err_name_len, buf_len(name)); + + LLVMValueRef str_init = LLVMConstString(buf_ptr(name), (unsigned)buf_len(name), true); + LLVMValueRef str_global = LLVMAddGlobal(g->module, LLVMTypeOf(str_init), ""); + LLVMSetInitializer(str_global, str_init); + LLVMSetLinkage(str_global, LLVMPrivateLinkage); + LLVMSetGlobalConstant(str_global, true); + LLVMSetUnnamedAddr(str_global, true); + LLVMSetAlignment(str_global, LLVMABIAlignmentOfType(g->target_data_ref, LLVMTypeOf(str_init))); + + LLVMValueRef fields[] = { + LLVMConstBitCast(str_global, get_llvm_type(g, u8_ptr_type)), + LLVMConstInt(g->builtin_types.entry_usize->llvm_type, buf_len(name), false), + }; + values[i] = LLVMConstNamedStruct(get_llvm_type(g, str_type), fields, 2); + } + + LLVMValueRef err_name_table_init = LLVMConstArray(get_llvm_type(g, str_type), values, (unsigned)g->errors_by_index.length); + + g->err_name_table = LLVMAddGlobal(g->module, LLVMTypeOf(err_name_table_init), + get_mangled_name(g, buf_ptr(buf_create_from_str("__zig_err_name_table")))); + LLVMSetInitializer(g->err_name_table, err_name_table_init); + LLVMSetLinkage(g->err_name_table, LLVMPrivateLinkage); + LLVMSetGlobalConstant(g->err_name_table, true); + LLVMSetUnnamedAddr(g->err_name_table, true); + LLVMSetAlignment(g->err_name_table, LLVMABIAlignmentOfType(g->target_data_ref, LLVMTypeOf(err_name_table_init))); +} + +static void build_all_basic_blocks(CodeGen *g, ZigFn *fn) { + IrExecutableGen *executable = &fn->analyzed_executable; + assert(executable->basic_block_list.length > 0); + LLVMValueRef fn_val = fn_llvm_value(g, fn); + LLVMBasicBlockRef first_bb = nullptr; + if (fn_is_async(fn)) { + first_bb = LLVMAppendBasicBlock(fn_val, "AsyncSwitch"); + g->cur_preamble_llvm_block = first_bb; + } + for (size_t block_i = 0; block_i < executable->basic_block_list.length; block_i += 1) { + IrBasicBlockGen *bb = executable->basic_block_list.at(block_i); + bb->llvm_block = LLVMAppendBasicBlock(fn_val, bb->name_hint); + } + if (first_bb == nullptr) { + first_bb = executable->basic_block_list.at(0)->llvm_block; + } + LLVMPositionBuilderAtEnd(g->builder, first_bb); +} + +static void gen_global_var(CodeGen *g, ZigVar *var, LLVMValueRef init_val, + ZigType *type_entry) +{ + if (g->strip_debug_symbols) { + return; + } + + assert(var->gen_is_const); + assert(type_entry); + + ZigType *import = get_scope_import(var->parent_scope); + assert(import); + + bool is_local_to_unit = true; + ZigLLVMCreateGlobalVariable(g->dbuilder, get_di_scope(g, var->parent_scope), var->name, + var->name, import->data.structure.root_struct->di_file, + (unsigned)(var->decl_node->line + 1), + get_llvm_di_type(g, type_entry), is_local_to_unit); + + // TODO ^^ make an actual global variable +} + +static void set_global_tls(CodeGen *g, ZigVar *var, LLVMValueRef global_value) { + bool is_extern = var->decl_node->data.variable_declaration.is_extern; + bool is_export = var->decl_node->data.variable_declaration.is_export; + bool is_internal_linkage = !is_extern && !is_export; + if (var->is_thread_local && (!g->is_single_threaded || !is_internal_linkage)) { + LLVMSetThreadLocalMode(global_value, LLVMGeneralDynamicTLSModel); + } +} + +static void do_code_gen(CodeGen *g) { + Error err; + assert(!g->errors.length); + + generate_error_name_table(g); + + // Generate module level variables + for (size_t i = 0; i < g->global_vars.length; i += 1) { + TldVar *tld_var = g->global_vars.at(i); + ZigVar *var = tld_var->var; + + if (var->var_type->id == ZigTypeIdComptimeFloat) { + // Generate debug info for it but that's it. + ZigValue *const_val = var->const_value; + assert(const_val->special != ConstValSpecialRuntime); + if ((err = ir_resolve_lazy(g, var->decl_node, const_val))) + zig_unreachable(); + if (const_val->type != var->var_type) { + zig_panic("TODO debug info for var with ptr casted value"); + } + ZigType *var_type = g->builtin_types.entry_f128; + ZigValue coerced_value = {}; + coerced_value.special = ConstValSpecialStatic; + coerced_value.type = var_type; + coerced_value.data.x_f128 = bigfloat_to_f128(&const_val->data.x_bigfloat); + LLVMValueRef init_val = gen_const_val(g, &coerced_value, ""); + gen_global_var(g, var, init_val, var_type); + continue; + } + + if (var->var_type->id == ZigTypeIdComptimeInt) { + // Generate debug info for it but that's it. + ZigValue *const_val = var->const_value; + assert(const_val->special != ConstValSpecialRuntime); + if ((err = ir_resolve_lazy(g, var->decl_node, const_val))) + zig_unreachable(); + if (const_val->type != var->var_type) { + zig_panic("TODO debug info for var with ptr casted value"); + } + size_t bits_needed = bigint_bits_needed(&const_val->data.x_bigint); + if (bits_needed < 8) { + bits_needed = 8; + } + ZigType *var_type = get_int_type(g, const_val->data.x_bigint.is_negative, bits_needed); + LLVMValueRef init_val = bigint_to_llvm_const(get_llvm_type(g, var_type), &const_val->data.x_bigint); + gen_global_var(g, var, init_val, var_type); + continue; + } + + if (!type_has_bits(g, var->var_type)) + continue; + + assert(var->decl_node); + + GlobalLinkageId linkage; + const char *unmangled_name = var->name; + const char *symbol_name; + if (var->export_list.length == 0) { + if (var->decl_node->data.variable_declaration.is_extern) { + symbol_name = unmangled_name; + linkage = GlobalLinkageIdStrong; + } else { + symbol_name = get_mangled_name(g, unmangled_name); + linkage = GlobalLinkageIdInternal; + } + } else { + GlobalExport *global_export = &var->export_list.items[0]; + symbol_name = buf_ptr(&global_export->name); + linkage = global_export->linkage; + } + + LLVMValueRef global_value; + bool externally_initialized = var->decl_node->data.variable_declaration.expr == nullptr; + if (externally_initialized) { + LLVMValueRef existing_llvm_var = LLVMGetNamedGlobal(g->module, symbol_name); + if (existing_llvm_var) { + global_value = LLVMConstBitCast(existing_llvm_var, + LLVMPointerType(get_llvm_type(g, var->var_type), 0)); + } else { + global_value = LLVMAddGlobal(g->module, get_llvm_type(g, var->var_type), symbol_name); + // TODO debug info for the extern variable + + LLVMSetLinkage(global_value, to_llvm_linkage(linkage, true)); + maybe_import_dll(g, global_value, GlobalLinkageIdStrong); + LLVMSetAlignment(global_value, var->align_bytes); + LLVMSetGlobalConstant(global_value, var->gen_is_const); + set_global_tls(g, var, global_value); + } + } else { + bool exported = (linkage != GlobalLinkageIdInternal); + render_const_val(g, var->const_value, symbol_name); + render_const_val_global(g, var->const_value, symbol_name); + global_value = var->const_value->llvm_global; + + if (exported) { + LLVMSetLinkage(global_value, to_llvm_linkage(linkage, false)); + maybe_export_dll(g, global_value, GlobalLinkageIdStrong); + } + if (var->section_name) { + LLVMSetSection(global_value, buf_ptr(var->section_name)); + } + LLVMSetAlignment(global_value, var->align_bytes); + + // TODO debug info for function pointers + // Here we use const_value->type because that's the type of the llvm global, + // which we const ptr cast upon use to whatever it needs to be. + if (var->gen_is_const && var->const_value->type->id != ZigTypeIdFn) { + gen_global_var(g, var, var->const_value->llvm_value, var->const_value->type); + } + + LLVMSetGlobalConstant(global_value, var->gen_is_const); + set_global_tls(g, var, global_value); + } + + var->value_ref = global_value; + + for (size_t export_i = 1; export_i < var->export_list.length; export_i += 1) { + GlobalExport *global_export = &var->export_list.items[export_i]; + LLVMAddAlias(g->module, LLVMTypeOf(var->value_ref), var->value_ref, buf_ptr(&global_export->name)); + } + } + + // Generate function definitions. + stage2_progress_update_node(g->sub_progress_node, 0, g->fn_defs.length); + for (size_t fn_i = 0; fn_i < g->fn_defs.length; fn_i += 1) { + ZigFn *fn_table_entry = g->fn_defs.at(fn_i); + Stage2ProgressNode *fn_prog_node = stage2_progress_start(g->sub_progress_node, + buf_ptr(&fn_table_entry->symbol_name), buf_len(&fn_table_entry->symbol_name), 0); + + FnTypeId *fn_type_id = &fn_table_entry->type_entry->data.fn.fn_type_id; + CallingConvention cc = fn_type_id->cc; + bool is_c_abi = !calling_convention_allows_zig_types(cc); + bool want_sret = want_first_arg_sret(g, fn_type_id); + + LLVMValueRef fn = fn_llvm_value(g, fn_table_entry); + g->cur_fn = fn_table_entry; + g->cur_fn_val = fn; + + build_all_basic_blocks(g, fn_table_entry); + clear_debug_source_node(g); + + bool is_async = fn_is_async(fn_table_entry); + + if (is_async) { + g->cur_frame_ptr = LLVMGetParam(fn, 0); + } else { + if (want_sret) { + g->cur_ret_ptr = LLVMGetParam(fn, 0); + } else if (type_has_bits(g, fn_type_id->return_type)) { + g->cur_ret_ptr = build_alloca(g, fn_type_id->return_type, "result", 0); + // TODO add debug info variable for this + } else { + g->cur_ret_ptr = nullptr; + } + } + + uint32_t err_ret_trace_arg_index = get_err_ret_trace_arg_index(g, fn_table_entry); + bool have_err_ret_trace_arg = err_ret_trace_arg_index != UINT32_MAX; + if (have_err_ret_trace_arg) { + g->cur_err_ret_trace_val_arg = LLVMGetParam(fn, err_ret_trace_arg_index); + } else { + g->cur_err_ret_trace_val_arg = nullptr; + } + + // error return tracing setup + bool have_err_ret_trace_stack = g->have_err_ret_tracing && fn_table_entry->calls_or_awaits_errorable_fn && + !is_async && !have_err_ret_trace_arg; + LLVMValueRef err_ret_array_val = nullptr; + if (have_err_ret_trace_stack) { + ZigType *array_type = get_array_type(g, g->builtin_types.entry_usize, stack_trace_ptr_count, nullptr); + err_ret_array_val = build_alloca(g, array_type, "error_return_trace_addresses", get_abi_alignment(g, array_type)); + + (void)get_llvm_type(g, get_stack_trace_type(g)); + g->cur_err_ret_trace_val_stack = build_alloca(g, get_stack_trace_type(g), "error_return_trace", + get_abi_alignment(g, g->stack_trace_type)); + } else { + g->cur_err_ret_trace_val_stack = nullptr; + } + + if (!is_async) { + // allocate async frames for nosuspend calls & awaits to async functions + ZigType *largest_call_frame_type = nullptr; + IrInstGen *all_calls_alloca = ir_create_alloca(g, &fn_table_entry->fndef_scope->base, + fn_table_entry->body_node, fn_table_entry, g->builtin_types.entry_void, "@async_call_frame"); + for (size_t i = 0; i < fn_table_entry->call_list.length; i += 1) { + IrInstGenCall *call = fn_table_entry->call_list.at(i); + if (call->fn_entry == nullptr) + continue; + if (!fn_is_async(call->fn_entry)) + continue; + if (call->modifier != CallModifierNoSuspend) + continue; + if (call->frame_result_loc != nullptr) + continue; + ZigType *callee_frame_type = get_fn_frame_type(g, call->fn_entry); + 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); + } + // allocate temporary stack data + for (size_t alloca_i = 0; alloca_i < fn_table_entry->alloca_gen_list.length; alloca_i += 1) { + IrInstGenAlloca *instruction = fn_table_entry->alloca_gen_list.at(alloca_i); + ZigType *ptr_type = instruction->base.value->type; + assert(ptr_type->id == ZigTypeIdPointer); + ZigType *child_type = ptr_type->data.pointer.child_type; + if (type_resolve(g, child_type, ResolveStatusSizeKnown)) + zig_unreachable(); + 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; + } + } + if (type_resolve(g, child_type, ResolveStatusLLVMFull)) + zig_unreachable(); + instruction->base.llvm_value = build_alloca(g, child_type, instruction->name_hint, + get_ptr_align(g, ptr_type)); + } + } + + ZigType *import = get_scope_import(&fn_table_entry->fndef_scope->base); + unsigned gen_i_init = want_sret ? 1 : 0; + + // create debug variable declarations for variables and allocate all local variables + FnWalk fn_walk_var = {}; + fn_walk_var.id = FnWalkIdVars; + fn_walk_var.data.vars.import = import; + fn_walk_var.data.vars.fn = fn_table_entry; + fn_walk_var.data.vars.llvm_fn = fn; + fn_walk_var.data.vars.gen_i = gen_i_init; + for (size_t var_i = 0; var_i < fn_table_entry->variable_list.length; var_i += 1) { + ZigVar *var = fn_table_entry->variable_list.at(var_i); + + if (!type_has_bits(g, var->var_type)) { + continue; + } + if (ir_get_var_is_comptime(var)) + continue; + switch (type_requires_comptime(g, var->var_type)) { + case ReqCompTimeInvalid: + zig_unreachable(); + case ReqCompTimeYes: + continue; + case ReqCompTimeNo: + break; + } + + if (var->src_arg_index == SIZE_MAX) { + var->di_loc_var = ZigLLVMCreateAutoVariable(g->dbuilder, get_di_scope(g, var->parent_scope), + var->name, import->data.structure.root_struct->di_file, (unsigned)(var->decl_node->line + 1), + get_llvm_di_type(g, var->var_type), !g->strip_debug_symbols, 0); + + } else if (is_c_abi) { + fn_walk_var.data.vars.var = var; + iter_function_params_c_abi(g, fn_table_entry->type_entry, &fn_walk_var, var->src_arg_index); + } else if (!is_async) { + ZigType *gen_type; + FnGenParamInfo *gen_info = &fn_table_entry->type_entry->data.fn.gen_param_info[var->src_arg_index]; + assert(gen_info->gen_index != SIZE_MAX); + + if (handle_is_ptr(g, var->var_type)) { + if (gen_info->is_byval) { + gen_type = var->var_type; + } else { + gen_type = gen_info->type; + } + var->value_ref = LLVMGetParam(fn, gen_info->gen_index); + } else { + gen_type = var->var_type; + var->value_ref = build_alloca(g, var->var_type, var->name, var->align_bytes); + } + if (var->decl_node) { + var->di_loc_var = ZigLLVMCreateParameterVariable(g->dbuilder, get_di_scope(g, var->parent_scope), + var->name, import->data.structure.root_struct->di_file, + (unsigned)(var->decl_node->line + 1), + get_llvm_di_type(g, gen_type), !g->strip_debug_symbols, 0, (unsigned)(gen_info->gen_index+1)); + } + + } + } + + // finishing error return trace setup. we have to do this after all the allocas. + if (have_err_ret_trace_stack) { + ZigType *usize = g->builtin_types.entry_usize; + size_t index_field_index = g->stack_trace_type->data.structure.fields[0]->gen_index; + LLVMValueRef index_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val_stack, (unsigned)index_field_index, ""); + gen_store_untyped(g, LLVMConstNull(usize->llvm_type), index_field_ptr, 0, false); + + size_t addresses_field_index = g->stack_trace_type->data.structure.fields[1]->gen_index; + LLVMValueRef addresses_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_err_ret_trace_val_stack, (unsigned)addresses_field_index, ""); + + ZigType *slice_type = g->stack_trace_type->data.structure.fields[1]->type_entry; + size_t ptr_field_index = slice_type->data.structure.fields[slice_ptr_index]->gen_index; + LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)ptr_field_index, ""); + LLVMValueRef zero = LLVMConstNull(usize->llvm_type); + LLVMValueRef indices[] = {zero, zero}; + LLVMValueRef err_ret_array_val_elem0_ptr = LLVMBuildInBoundsGEP(g->builder, err_ret_array_val, + indices, 2, ""); + ZigType *ptr_ptr_usize_type = get_pointer_to_type(g, get_pointer_to_type(g, usize, false), false); + gen_store(g, err_ret_array_val_elem0_ptr, ptr_field_ptr, ptr_ptr_usize_type); + + size_t len_field_index = slice_type->data.structure.fields[slice_len_index]->gen_index; + LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, addresses_field_ptr, (unsigned)len_field_index, ""); + gen_store(g, LLVMConstInt(usize->llvm_type, stack_trace_ptr_count, false), len_field_ptr, get_pointer_to_type(g, usize, false)); + } + + if (is_async) { + (void)get_llvm_type(g, fn_table_entry->frame_type); + g->cur_resume_block_count = 0; + + LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type; + LLVMValueRef size_val = LLVMConstInt(usize_type_ref, fn_table_entry->frame_type->abi_size, false); + if (g->need_frame_size_prefix_data) { + ZigLLVMFunctionSetPrefixData(fn_table_entry->llvm_value, size_val); + } + + if (!g->strip_debug_symbols) { + AstNode *source_node = fn_table_entry->proto_node; + ZigLLVMSetCurrentDebugLocation(g->builder, (int)source_node->line + 1, + (int)source_node->column + 1, get_di_scope(g, fn_table_entry->child_scope)); + } + IrExecutableGen *executable = &fn_table_entry->analyzed_executable; + LLVMBasicBlockRef bad_resume_block = LLVMAppendBasicBlock(g->cur_fn_val, "BadResume"); + LLVMPositionBuilderAtEnd(g->builder, bad_resume_block); + gen_assertion_scope(g, PanicMsgIdBadResume, fn_table_entry->child_scope); + + LLVMPositionBuilderAtEnd(g->builder, g->cur_preamble_llvm_block); + render_async_spills(g); + g->cur_async_awaiter_ptr = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr, frame_awaiter_index, ""); + LLVMValueRef resume_index_ptr = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr, frame_resume_index, ""); + g->cur_async_resume_index_ptr = resume_index_ptr; + + if (type_has_bits(g, fn_type_id->return_type)) { + LLVMValueRef cur_ret_ptr_ptr = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr, frame_ret_start, ""); + g->cur_ret_ptr = LLVMBuildLoad(g->builder, cur_ret_ptr_ptr, ""); + } + uint32_t trace_field_index_stack = UINT32_MAX; + if (codegen_fn_has_err_ret_tracing_stack(g, fn_table_entry, true)) { + trace_field_index_stack = frame_index_trace_stack(g, fn_table_entry); + g->cur_err_ret_trace_val_stack = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr, + trace_field_index_stack, ""); + } + + LLVMValueRef resume_index = LLVMBuildLoad(g->builder, resume_index_ptr, ""); + LLVMValueRef switch_instr = LLVMBuildSwitch(g->builder, resume_index, bad_resume_block, 4); + g->cur_async_switch_instr = switch_instr; + + LLVMValueRef zero = LLVMConstNull(usize_type_ref); + IrBasicBlockGen *entry_block = executable->basic_block_list.at(0); + LLVMAddCase(switch_instr, zero, entry_block->llvm_block); + g->cur_resume_block_count += 1; + + { + LLVMBasicBlockRef bad_not_suspended_bb = LLVMAppendBasicBlock(g->cur_fn_val, "NotSuspended"); + size_t new_block_index = g->cur_resume_block_count; + g->cur_resume_block_count += 1; + g->cur_bad_not_suspended_index = LLVMConstInt(usize_type_ref, new_block_index, false); + LLVMAddCase(g->cur_async_switch_instr, g->cur_bad_not_suspended_index, bad_not_suspended_bb); + + LLVMPositionBuilderAtEnd(g->builder, bad_not_suspended_bb); + gen_assertion_scope(g, PanicMsgIdResumeNotSuspendedFn, fn_table_entry->child_scope); + } + + LLVMPositionBuilderAtEnd(g->builder, entry_block->llvm_block); + LLVMBuildStore(g->builder, g->cur_bad_not_suspended_index, g->cur_async_resume_index_ptr); + if (trace_field_index_stack != UINT32_MAX) { + if (codegen_fn_has_err_ret_tracing_arg(g, fn_type_id->return_type)) { + LLVMValueRef trace_ptr_ptr = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr, + frame_index_trace_arg(g, fn_type_id->return_type), ""); + LLVMValueRef zero_ptr = LLVMConstNull(LLVMGetElementType(LLVMTypeOf(trace_ptr_ptr))); + LLVMBuildStore(g->builder, zero_ptr, trace_ptr_ptr); + } + + LLVMValueRef trace_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr, + trace_field_index_stack, ""); + LLVMValueRef addrs_field_ptr = LLVMBuildStructGEP(g->builder, g->cur_frame_ptr, + trace_field_index_stack + 1, ""); + + gen_init_stack_trace(g, trace_field_ptr, addrs_field_ptr); + } + render_async_var_decls(g, entry_block->instruction_list.at(0)->base.scope); + } else { + // create debug variable declarations for parameters + // rely on the first variables in the variable_list being parameters. + FnWalk fn_walk_init = {}; + fn_walk_init.id = FnWalkIdInits; + fn_walk_init.data.inits.fn = fn_table_entry; + fn_walk_init.data.inits.llvm_fn = fn; + fn_walk_init.data.inits.gen_i = gen_i_init; + walk_function_params(g, fn_table_entry->type_entry, &fn_walk_init); + } + + ir_render(g, fn_table_entry); + + stage2_progress_end(fn_prog_node); + } + + assert(!g->errors.length); + + if (buf_len(&g->global_asm) != 0) { + LLVMSetModuleInlineAsm(g->module, buf_ptr(&g->global_asm)); + } + + while (g->type_resolve_stack.length != 0) { + ZigType *ty = g->type_resolve_stack.last(); + if (type_resolve(g, ty, ResolveStatusLLVMFull)) + zig_unreachable(); + } + + ZigLLVMDIBuilderFinalize(g->dbuilder); + + if (g->verbose_llvm_ir) { + fflush(stderr); + LLVMDumpModule(g->module); + } + + char *error = nullptr; + if (LLVMVerifyModule(g->module, LLVMReturnStatusAction, &error)) { + zig_panic("broken LLVM module found: %s\nThis is a bug in the Zig compiler.", error); + } +} + +static void zig_llvm_emit_output(CodeGen *g) { + g->pass1_arena->destruct(&heap::c_allocator); + g->pass1_arena = nullptr; + + bool is_small = g->build_mode == BuildModeSmallRelease; + + char *err_msg = nullptr; + const char *asm_filename = nullptr; + const char *bin_filename = nullptr; + const char *llvm_ir_filename = nullptr; + + if (buf_len(&g->o_file_output_path) != 0) bin_filename = buf_ptr(&g->o_file_output_path); + if (buf_len(&g->asm_file_output_path) != 0) asm_filename = buf_ptr(&g->asm_file_output_path); + if (buf_len(&g->llvm_ir_file_output_path) != 0) llvm_ir_filename = buf_ptr(&g->llvm_ir_file_output_path); + + // Unfortunately, LLVM shits the bed when we ask for both binary and assembly. So we call the entire + // pipeline multiple times if this is requested. + if (asm_filename != nullptr && bin_filename != nullptr) { + if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, &err_msg, g->build_mode == BuildModeDebug, + is_small, g->enable_time_report, nullptr, bin_filename, llvm_ir_filename)) + { + fprintf(stderr, "LLVM failed to emit file: %s\n", err_msg); + exit(1); + } + bin_filename = nullptr; + llvm_ir_filename = nullptr; + } + + if (ZigLLVMTargetMachineEmitToFile(g->target_machine, g->module, &err_msg, g->build_mode == BuildModeDebug, + is_small, g->enable_time_report, asm_filename, bin_filename, llvm_ir_filename)) + { + fprintf(stderr, "LLVM failed to emit file: %s\n", err_msg); + exit(1); + } + + LLVMDisposeModule(g->module); + g->module = nullptr; + LLVMDisposeTargetData(g->target_data_ref); + g->target_data_ref = nullptr; + LLVMDisposeTargetMachine(g->target_machine); + g->target_machine = nullptr; +} + +struct CIntTypeInfo { + CIntType id; + const char *name; + bool is_signed; +}; + +static const CIntTypeInfo c_int_type_infos[] = { + {CIntTypeShort, "c_short", true}, + {CIntTypeUShort, "c_ushort", false}, + {CIntTypeInt, "c_int", true}, + {CIntTypeUInt, "c_uint", false}, + {CIntTypeLong, "c_long", true}, + {CIntTypeULong, "c_ulong", false}, + {CIntTypeLongLong, "c_longlong", true}, + {CIntTypeULongLong, "c_ulonglong", false}, +}; + +static const bool is_signed_list[] = { false, true, }; + +struct GlobalLinkageValue { + GlobalLinkageId id; + const char *name; +}; + +static void add_fp_entry(CodeGen *g, const char *name, uint32_t bit_count, LLVMTypeRef type_ref, + ZigType **field) +{ + ZigType *entry = new_type_table_entry(ZigTypeIdFloat); + entry->llvm_type = type_ref; + entry->size_in_bits = 8*LLVMStoreSizeOfType(g->target_data_ref, entry->llvm_type); + entry->abi_size = LLVMABISizeOfType(g->target_data_ref, entry->llvm_type); + entry->abi_align = LLVMABIAlignmentOfType(g->target_data_ref, entry->llvm_type); + buf_init_from_str(&entry->name, name); + entry->data.floating.bit_count = bit_count; + + entry->llvm_di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), + entry->size_in_bits, ZigLLVMEncoding_DW_ATE_float()); + *field = entry; + g->primitive_type_table.put(&entry->name, entry); +} + +static void define_builtin_types(CodeGen *g) { + { + // if this type is anywhere in the AST, we should never hit codegen. + ZigType *entry = new_type_table_entry(ZigTypeIdInvalid); + buf_init_from_str(&entry->name, "(invalid)"); + g->builtin_types.entry_invalid = entry; + } + { + ZigType *entry = new_type_table_entry(ZigTypeIdComptimeFloat); + buf_init_from_str(&entry->name, "comptime_float"); + g->builtin_types.entry_num_lit_float = entry; + g->primitive_type_table.put(&entry->name, entry); + } + { + ZigType *entry = new_type_table_entry(ZigTypeIdComptimeInt); + buf_init_from_str(&entry->name, "comptime_int"); + g->builtin_types.entry_num_lit_int = entry; + g->primitive_type_table.put(&entry->name, entry); + } + { + ZigType *entry = new_type_table_entry(ZigTypeIdEnumLiteral); + buf_init_from_str(&entry->name, "(enum literal)"); + g->builtin_types.entry_enum_literal = entry; + } + { + ZigType *entry = new_type_table_entry(ZigTypeIdUndefined); + buf_init_from_str(&entry->name, "(undefined)"); + g->builtin_types.entry_undef = entry; + } + { + ZigType *entry = new_type_table_entry(ZigTypeIdNull); + buf_init_from_str(&entry->name, "(null)"); + g->builtin_types.entry_null = entry; + } + { + ZigType *entry = new_type_table_entry(ZigTypeIdOpaque); + buf_init_from_str(&entry->name, "(anytype)"); + g->builtin_types.entry_anytype = entry; + } + + for (size_t i = 0; i < array_length(c_int_type_infos); i += 1) { + const CIntTypeInfo *info = &c_int_type_infos[i]; + uint32_t size_in_bits = target_c_type_size_in_bits(g->zig_target, info->id); + bool is_signed = info->is_signed; + + ZigType *entry = new_type_table_entry(ZigTypeIdInt); + entry->llvm_type = LLVMIntType(size_in_bits); + entry->size_in_bits = 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); + + buf_init_from_str(&entry->name, info->name); + + entry->llvm_di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), + 8*LLVMStoreSizeOfType(g->target_data_ref, entry->llvm_type), + is_signed ? ZigLLVMEncoding_DW_ATE_signed() : ZigLLVMEncoding_DW_ATE_unsigned()); + entry->data.integral.is_signed = is_signed; + entry->data.integral.bit_count = size_in_bits; + g->primitive_type_table.put(&entry->name, entry); + + get_c_int_type_ptr(g, info->id)[0] = entry; + } + + { + ZigType *entry = new_type_table_entry(ZigTypeIdBool); + entry->llvm_type = LLVMInt1Type(); + entry->size_in_bits = 1; + entry->abi_size = LLVMABISizeOfType(g->target_data_ref, entry->llvm_type); + entry->abi_align = LLVMABIAlignmentOfType(g->target_data_ref, entry->llvm_type); + buf_init_from_str(&entry->name, "bool"); + entry->llvm_di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), + 8*LLVMStoreSizeOfType(g->target_data_ref, entry->llvm_type), + ZigLLVMEncoding_DW_ATE_boolean()); + g->builtin_types.entry_bool = entry; + g->primitive_type_table.put(&entry->name, entry); + } + + for (size_t sign_i = 0; sign_i < array_length(is_signed_list); sign_i += 1) { + bool is_signed = is_signed_list[sign_i]; + + ZigType *entry = new_type_table_entry(ZigTypeIdInt); + entry->llvm_type = LLVMIntType(g->pointer_size_bytes * 8); + entry->size_in_bits = g->pointer_size_bytes * 8; + entry->abi_size = LLVMABISizeOfType(g->target_data_ref, entry->llvm_type); + entry->abi_align = LLVMABIAlignmentOfType(g->target_data_ref, entry->llvm_type); + + const char u_or_i = is_signed ? 'i' : 'u'; + buf_resize(&entry->name, 0); + buf_appendf(&entry->name, "%csize", u_or_i); + + entry->data.integral.is_signed = is_signed; + entry->data.integral.bit_count = g->pointer_size_bytes * 8; + + entry->llvm_di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), + 8*LLVMStoreSizeOfType(g->target_data_ref, entry->llvm_type), + is_signed ? ZigLLVMEncoding_DW_ATE_signed() : ZigLLVMEncoding_DW_ATE_unsigned()); + g->primitive_type_table.put(&entry->name, entry); + + if (is_signed) { + g->builtin_types.entry_isize = entry; + } else { + g->builtin_types.entry_usize = entry; + } + } + + add_fp_entry(g, "f16", 16, LLVMHalfType(), &g->builtin_types.entry_f16); + add_fp_entry(g, "f32", 32, LLVMFloatType(), &g->builtin_types.entry_f32); + add_fp_entry(g, "f64", 64, LLVMDoubleType(), &g->builtin_types.entry_f64); + add_fp_entry(g, "f128", 128, LLVMFP128Type(), &g->builtin_types.entry_f128); + add_fp_entry(g, "c_longdouble", 80, LLVMX86FP80Type(), &g->builtin_types.entry_c_longdouble); + + { + ZigType *entry = new_type_table_entry(ZigTypeIdVoid); + entry->llvm_type = LLVMVoidType(); + buf_init_from_str(&entry->name, "void"); + entry->llvm_di_type = ZigLLVMCreateDebugBasicType(g->dbuilder, buf_ptr(&entry->name), + 0, + ZigLLVMEncoding_DW_ATE_signed()); + g->builtin_types.entry_void = entry; + g->primitive_type_table.put(&entry->name, entry); + } + { + ZigType *entry = new_type_table_entry(ZigTypeIdUnreachable); + entry->llvm_type = LLVMVoidType(); + buf_init_from_str(&entry->name, "noreturn"); + entry->llvm_di_type = g->builtin_types.entry_void->llvm_di_type; + g->builtin_types.entry_unreachable = entry; + g->primitive_type_table.put(&entry->name, entry); + } + { + ZigType *entry = new_type_table_entry(ZigTypeIdMetaType); + buf_init_from_str(&entry->name, "type"); + g->builtin_types.entry_type = entry; + g->primitive_type_table.put(&entry->name, entry); + } + + g->builtin_types.entry_u8 = get_int_type(g, false, 8); + g->builtin_types.entry_u16 = get_int_type(g, false, 16); + g->builtin_types.entry_u29 = get_int_type(g, false, 29); + g->builtin_types.entry_u32 = get_int_type(g, false, 32); + g->builtin_types.entry_u64 = get_int_type(g, false, 64); + g->builtin_types.entry_i8 = get_int_type(g, true, 8); + g->builtin_types.entry_i32 = get_int_type(g, true, 32); + g->builtin_types.entry_i64 = get_int_type(g, true, 64); + + { + g->builtin_types.entry_c_void = get_opaque_type(g, nullptr, nullptr, "c_void", + buf_create_from_str("c_void")); + g->primitive_type_table.put(&g->builtin_types.entry_c_void->name, g->builtin_types.entry_c_void); + } + + { + ZigType *entry = new_type_table_entry(ZigTypeIdErrorSet); + buf_init_from_str(&entry->name, "anyerror"); + entry->data.error_set.err_count = UINT32_MAX; + + // TODO https://github.com/ziglang/zig/issues/786 + g->err_tag_type = g->builtin_types.entry_u16; + + entry->size_in_bits = g->err_tag_type->size_in_bits; + entry->abi_align = g->err_tag_type->abi_align; + entry->abi_size = g->err_tag_type->abi_size; + + g->builtin_types.entry_global_error_set = entry; + + g->errors_by_index.append(nullptr); + + g->primitive_type_table.put(&entry->name, entry); + } +} + +static void define_intern_values(CodeGen *g) { + { + auto& value = g->intern.x_undefined; + value.type = g->builtin_types.entry_undef; + value.special = ConstValSpecialStatic; + } + { + auto& value = g->intern.x_void; + value.type = g->builtin_types.entry_void; + value.special = ConstValSpecialStatic; + } + { + auto& value = g->intern.x_null; + value.type = g->builtin_types.entry_null; + value.special = ConstValSpecialStatic; + } + { + auto& value = g->intern.x_unreachable; + value.type = g->builtin_types.entry_unreachable; + value.special = ConstValSpecialStatic; + } + { + auto& value = g->intern.zero_byte; + value.type = g->builtin_types.entry_u8; + value.special = ConstValSpecialStatic; + bigint_init_unsigned(&value.data.x_bigint, 0); + } +} + +static BuiltinFnEntry *create_builtin_fn(CodeGen *g, BuiltinFnId id, const char *name, size_t count) { + BuiltinFnEntry *builtin_fn = heap::c_allocator.create(); + buf_init_from_str(&builtin_fn->name, name); + builtin_fn->id = id; + builtin_fn->param_count = count; + g->builtin_fn_table.put(&builtin_fn->name, builtin_fn); + return builtin_fn; +} + +static void define_builtin_fns(CodeGen *g) { + create_builtin_fn(g, BuiltinFnIdBreakpoint, "breakpoint", 0); + create_builtin_fn(g, BuiltinFnIdReturnAddress, "returnAddress", 0); + create_builtin_fn(g, BuiltinFnIdMemcpy, "memcpy", 3); + create_builtin_fn(g, BuiltinFnIdMemset, "memset", 3); + create_builtin_fn(g, BuiltinFnIdSizeof, "sizeOf", 1); + create_builtin_fn(g, BuiltinFnIdAlignOf, "alignOf", 1); + create_builtin_fn(g, BuiltinFnIdField, "field", 2); + create_builtin_fn(g, BuiltinFnIdTypeInfo, "typeInfo", 1); + create_builtin_fn(g, BuiltinFnIdType, "Type", 1); + create_builtin_fn(g, BuiltinFnIdHasField, "hasField", 2); + create_builtin_fn(g, BuiltinFnIdTypeof, "TypeOf", SIZE_MAX); + create_builtin_fn(g, BuiltinFnIdAddWithOverflow, "addWithOverflow", 4); + create_builtin_fn(g, BuiltinFnIdSubWithOverflow, "subWithOverflow", 4); + create_builtin_fn(g, BuiltinFnIdMulWithOverflow, "mulWithOverflow", 4); + create_builtin_fn(g, BuiltinFnIdShlWithOverflow, "shlWithOverflow", 4); + create_builtin_fn(g, BuiltinFnIdCInclude, "cInclude", 1); + create_builtin_fn(g, BuiltinFnIdCDefine, "cDefine", 2); + create_builtin_fn(g, BuiltinFnIdCUndef, "cUndef", 1); + create_builtin_fn(g, BuiltinFnIdCtz, "ctz", 2); + create_builtin_fn(g, BuiltinFnIdClz, "clz", 2); + create_builtin_fn(g, BuiltinFnIdPopCount, "popCount", 2); + create_builtin_fn(g, BuiltinFnIdBswap, "byteSwap", 2); + create_builtin_fn(g, BuiltinFnIdBitReverse, "bitReverse", 2); + create_builtin_fn(g, BuiltinFnIdImport, "import", 1); + create_builtin_fn(g, BuiltinFnIdCImport, "cImport", 1); + create_builtin_fn(g, BuiltinFnIdErrName, "errorName", 1); + create_builtin_fn(g, BuiltinFnIdTypeName, "typeName", 1); + create_builtin_fn(g, BuiltinFnIdEmbedFile, "embedFile", 1); + create_builtin_fn(g, BuiltinFnIdCmpxchgWeak, "cmpxchgWeak", 6); + create_builtin_fn(g, BuiltinFnIdCmpxchgStrong, "cmpxchgStrong", 6); + create_builtin_fn(g, BuiltinFnIdFence, "fence", 1); + create_builtin_fn(g, BuiltinFnIdTruncate, "truncate", 2); + create_builtin_fn(g, BuiltinFnIdIntCast, "intCast", 2); + create_builtin_fn(g, BuiltinFnIdFloatCast, "floatCast", 2); + create_builtin_fn(g, BuiltinFnIdIntToFloat, "intToFloat", 2); + create_builtin_fn(g, BuiltinFnIdFloatToInt, "floatToInt", 2); + create_builtin_fn(g, BuiltinFnIdBoolToInt, "boolToInt", 1); + create_builtin_fn(g, BuiltinFnIdErrToInt, "errorToInt", 1); + create_builtin_fn(g, BuiltinFnIdIntToErr, "intToError", 1); + create_builtin_fn(g, BuiltinFnIdEnumToInt, "enumToInt", 1); + create_builtin_fn(g, BuiltinFnIdIntToEnum, "intToEnum", 2); + create_builtin_fn(g, BuiltinFnIdCompileErr, "compileError", 1); + create_builtin_fn(g, BuiltinFnIdCompileLog, "compileLog", SIZE_MAX); + create_builtin_fn(g, BuiltinFnIdVectorType, "Vector", 2); + create_builtin_fn(g, BuiltinFnIdShuffle, "shuffle", 4); + create_builtin_fn(g, BuiltinFnIdSplat, "splat", 2); + create_builtin_fn(g, BuiltinFnIdSetCold, "setCold", 1); + create_builtin_fn(g, BuiltinFnIdSetRuntimeSafety, "setRuntimeSafety", 1); + create_builtin_fn(g, BuiltinFnIdSetFloatMode, "setFloatMode", 1); + create_builtin_fn(g, BuiltinFnIdPanic, "panic", 1); + create_builtin_fn(g, BuiltinFnIdPtrCast, "ptrCast", 2); + create_builtin_fn(g, BuiltinFnIdBitCast, "bitCast", 2); + create_builtin_fn(g, BuiltinFnIdIntToPtr, "intToPtr", 2); + create_builtin_fn(g, BuiltinFnIdPtrToInt, "ptrToInt", 1); + create_builtin_fn(g, BuiltinFnIdTagName, "tagName", 1); + create_builtin_fn(g, BuiltinFnIdTagType, "TagType", 1); + create_builtin_fn(g, BuiltinFnIdFieldParentPtr, "fieldParentPtr", 3); + create_builtin_fn(g, BuiltinFnIdByteOffsetOf, "byteOffsetOf", 2); + create_builtin_fn(g, BuiltinFnIdBitOffsetOf, "bitOffsetOf", 2); + create_builtin_fn(g, BuiltinFnIdDivExact, "divExact", 2); + create_builtin_fn(g, BuiltinFnIdDivTrunc, "divTrunc", 2); + create_builtin_fn(g, BuiltinFnIdDivFloor, "divFloor", 2); + create_builtin_fn(g, BuiltinFnIdRem, "rem", 2); + create_builtin_fn(g, BuiltinFnIdMod, "mod", 2); + create_builtin_fn(g, BuiltinFnIdSqrt, "sqrt", 1); + create_builtin_fn(g, BuiltinFnIdSin, "sin", 1); + create_builtin_fn(g, BuiltinFnIdCos, "cos", 1); + create_builtin_fn(g, BuiltinFnIdExp, "exp", 1); + create_builtin_fn(g, BuiltinFnIdExp2, "exp2", 1); + create_builtin_fn(g, BuiltinFnIdLog, "log", 1); + create_builtin_fn(g, BuiltinFnIdLog2, "log2", 1); + create_builtin_fn(g, BuiltinFnIdLog10, "log10", 1); + create_builtin_fn(g, BuiltinFnIdFabs, "fabs", 1); + create_builtin_fn(g, BuiltinFnIdFloor, "floor", 1); + create_builtin_fn(g, BuiltinFnIdCeil, "ceil", 1); + create_builtin_fn(g, BuiltinFnIdTrunc, "trunc", 1); + create_builtin_fn(g, BuiltinFnIdNearbyInt, "nearbyInt", 1); + create_builtin_fn(g, BuiltinFnIdRound, "round", 1); + create_builtin_fn(g, BuiltinFnIdMulAdd, "mulAdd", 4); + create_builtin_fn(g, BuiltinFnIdAsyncCall, "asyncCall", SIZE_MAX); + create_builtin_fn(g, BuiltinFnIdShlExact, "shlExact", 2); + create_builtin_fn(g, BuiltinFnIdShrExact, "shrExact", 2); + create_builtin_fn(g, BuiltinFnIdSetEvalBranchQuota, "setEvalBranchQuota", 1); + create_builtin_fn(g, BuiltinFnIdAlignCast, "alignCast", 2); + create_builtin_fn(g, BuiltinFnIdSetAlignStack, "setAlignStack", 1); + create_builtin_fn(g, BuiltinFnIdExport, "export", 2); + create_builtin_fn(g, BuiltinFnIdErrorReturnTrace, "errorReturnTrace", 0); + create_builtin_fn(g, BuiltinFnIdAtomicRmw, "atomicRmw", 5); + create_builtin_fn(g, BuiltinFnIdAtomicLoad, "atomicLoad", 3); + create_builtin_fn(g, BuiltinFnIdAtomicStore, "atomicStore", 4); + create_builtin_fn(g, BuiltinFnIdErrSetCast, "errSetCast", 2); + create_builtin_fn(g, BuiltinFnIdThis, "This", 0); + create_builtin_fn(g, BuiltinFnIdHasDecl, "hasDecl", 2); + create_builtin_fn(g, BuiltinFnIdUnionInit, "unionInit", 3); + create_builtin_fn(g, BuiltinFnIdFrameHandle, "frame", 0); + create_builtin_fn(g, BuiltinFnIdFrameType, "Frame", 1); + create_builtin_fn(g, BuiltinFnIdFrameAddress, "frameAddress", 0); + create_builtin_fn(g, BuiltinFnIdFrameSize, "frameSize", 1); + create_builtin_fn(g, BuiltinFnIdAs, "as", 2); + create_builtin_fn(g, BuiltinFnIdCall, "call", 3); + create_builtin_fn(g, BuiltinFnIdBitSizeof, "bitSizeOf", 1); + create_builtin_fn(g, BuiltinFnIdWasmMemorySize, "wasmMemorySize", 1); + create_builtin_fn(g, BuiltinFnIdWasmMemoryGrow, "wasmMemoryGrow", 2); + create_builtin_fn(g, BuiltinFnIdSrc, "src", 0); +} + +static const char *bool_to_str(bool b) { + return b ? "true" : "false"; +} + +static const char *build_mode_to_str(BuildMode build_mode) { + switch (build_mode) { + case BuildModeDebug: return "Mode.Debug"; + case BuildModeSafeRelease: return "Mode.ReleaseSafe"; + case BuildModeFastRelease: return "Mode.ReleaseFast"; + case BuildModeSmallRelease: return "Mode.ReleaseSmall"; + } + zig_unreachable(); +} + +static const char *subsystem_to_str(TargetSubsystem subsystem) { + switch (subsystem) { + case TargetSubsystemConsole: return "Console"; + case TargetSubsystemWindows: return "Windows"; + case TargetSubsystemPosix: return "Posix"; + case TargetSubsystemNative: return "Native"; + case TargetSubsystemEfiApplication: return "EfiApplication"; + case TargetSubsystemEfiBootServiceDriver: return "EfiBootServiceDriver"; + case TargetSubsystemEfiRom: return "EfiRom"; + case TargetSubsystemEfiRuntimeDriver: return "EfiRuntimeDriver"; + case TargetSubsystemAuto: zig_unreachable(); + } + zig_unreachable(); +} + +// Returns TargetSubsystemAuto to mean "no subsystem" +TargetSubsystem detect_subsystem(CodeGen *g) { + if (g->subsystem != TargetSubsystemAuto) + return g->subsystem; + if (g->zig_target->os == OsWindows) { + if (g->stage1.have_dllmain_crt_startup) + return TargetSubsystemAuto; + if (g->stage1.have_c_main || g->is_test_build || g->stage1.have_winmain_crt_startup || g->stage1.have_wwinmain_crt_startup) + return TargetSubsystemConsole; + if (g->stage1.have_winmain || g->stage1.have_wwinmain) + return TargetSubsystemWindows; + } else if (g->zig_target->os == OsUefi) { + return TargetSubsystemEfiApplication; + } + return TargetSubsystemAuto; +} + +static bool detect_err_ret_tracing(CodeGen *g) { + return !g->strip_debug_symbols && + g->build_mode != BuildModeFastRelease && + g->build_mode != BuildModeSmallRelease; +} + +static LLVMCodeModel to_llvm_code_model(CodeGen *g) { + switch (g->code_model) { + case CodeModelDefault: + return LLVMCodeModelDefault; + case CodeModelTiny: + return LLVMCodeModelTiny; + case CodeModelSmall: + return LLVMCodeModelSmall; + case CodeModelKernel: + return LLVMCodeModelKernel; + case CodeModelMedium: + return LLVMCodeModelMedium; + case CodeModelLarge: + return LLVMCodeModelLarge; + } + + zig_unreachable(); +} + +Buf *codegen_generate_builtin_source(CodeGen *g) { + // Note that this only runs when zig0 is building the self-hosted zig compiler code, + // so it makes a few assumption that are always true for that case. Once we have + // built the stage2 zig components then zig is in charge of generating the builtin.zig + // file. + + g->have_err_ret_tracing = detect_err_ret_tracing(g); + + Buf *contents = buf_alloc(); + buf_appendf(contents, "usingnamespace @import(\"std\").builtin;\n\n"); + + const char *cur_os = nullptr; + { + uint32_t field_count = (uint32_t)target_os_count(); + for (uint32_t i = 0; i < field_count; i += 1) { + Os os_type = target_os_enum(i); + const char *name = target_os_name(os_type); + + if (os_type == g->zig_target->os) { + cur_os = name; + } + } + } + assert(cur_os != nullptr); + + const char *cur_arch = nullptr; + { + uint32_t field_count = (uint32_t)target_arch_count(); + for (uint32_t arch_i = 0; arch_i < field_count; arch_i += 1) { + ZigLLVM_ArchType arch = target_arch_enum(arch_i); + const char *arch_name = target_arch_name(arch); + if (arch == g->zig_target->arch) { + cur_arch = arch_name; + } + } + } + assert(cur_arch != nullptr); + + const char *cur_abi = nullptr; + { + uint32_t field_count = (uint32_t)target_abi_count(); + for (uint32_t i = 0; i < field_count; i += 1) { + ZigLLVM_EnvironmentType abi = target_abi_enum(i); + const char *name = target_abi_name(abi); + + if (abi == g->zig_target->abi) { + cur_abi = name; + } + } + } + assert(cur_abi != nullptr); + + const char *cur_obj_fmt = nullptr; + { + uint32_t field_count = (uint32_t)target_oformat_count(); + for (uint32_t i = 0; i < field_count; i += 1) { + ZigLLVM_ObjectFormatType oformat = target_oformat_enum(i); + const char *name = target_oformat_name(oformat); + + ZigLLVM_ObjectFormatType target_oformat = target_object_format(g->zig_target); + if (oformat == target_oformat) { + cur_obj_fmt = name; + } + } + + } + assert(cur_obj_fmt != nullptr); + + // If any of these asserts trip then you need to either fix the internal compiler enum + // or the corresponding one in std.Target or std.builtin. + static_assert(ContainerLayoutAuto == 0, ""); + static_assert(ContainerLayoutExtern == 1, ""); + static_assert(ContainerLayoutPacked == 2, ""); + + static_assert(CallingConventionUnspecified == 0, ""); + static_assert(CallingConventionC == 1, ""); + static_assert(CallingConventionCold == 2, ""); + static_assert(CallingConventionNaked == 3, ""); + static_assert(CallingConventionAsync == 4, ""); + static_assert(CallingConventionInterrupt == 5, ""); + static_assert(CallingConventionSignal == 6, ""); + static_assert(CallingConventionStdcall == 7, ""); + static_assert(CallingConventionFastcall == 8, ""); + static_assert(CallingConventionVectorcall == 9, ""); + static_assert(CallingConventionThiscall == 10, ""); + static_assert(CallingConventionAPCS == 11, ""); + static_assert(CallingConventionAAPCS == 12, ""); + static_assert(CallingConventionAAPCSVFP == 13, ""); + + static_assert(FnInlineAuto == 0, ""); + static_assert(FnInlineAlways == 1, ""); + static_assert(FnInlineNever == 2, ""); + + static_assert(BuiltinPtrSizeOne == 0, ""); + static_assert(BuiltinPtrSizeMany == 1, ""); + static_assert(BuiltinPtrSizeSlice == 2, ""); + static_assert(BuiltinPtrSizeC == 3, ""); + + static_assert(TargetSubsystemConsole == 0, ""); + static_assert(TargetSubsystemWindows == 1, ""); + static_assert(TargetSubsystemPosix == 2, ""); + static_assert(TargetSubsystemNative == 3, ""); + static_assert(TargetSubsystemEfiApplication == 4, ""); + static_assert(TargetSubsystemEfiBootServiceDriver == 5, ""); + static_assert(TargetSubsystemEfiRom == 6, ""); + static_assert(TargetSubsystemEfiRuntimeDriver == 7, ""); + + buf_append_str(contents, "/// Deprecated: use `std.Target.current.cpu.arch`\n"); + buf_append_str(contents, "pub const arch = Target.current.cpu.arch;\n"); + buf_append_str(contents, "/// Deprecated: use `std.Target.current.cpu.arch.endian()`\n"); + buf_append_str(contents, "pub const endian = Target.current.cpu.arch.endian();\n"); + buf_appendf(contents, "pub const output_mode = OutputMode.Obj;\n"); + buf_appendf(contents, "pub const link_mode = LinkMode.Static;\n"); + buf_appendf(contents, "pub const is_test = false;\n"); + buf_appendf(contents, "pub const single_threaded = %s;\n", bool_to_str(g->is_single_threaded)); + buf_appendf(contents, "pub const abi = Abi.%s;\n", cur_abi); + buf_appendf(contents, "pub const cpu: Cpu = Target.Cpu.baseline(.%s);\n", cur_arch); + buf_appendf(contents, "pub const os = Target.Os.Tag.defaultVersionRange(.%s);\n", cur_os); + buf_appendf(contents, "pub const object_format = ObjectFormat.%s;\n", cur_obj_fmt); + buf_appendf(contents, "pub const mode = %s;\n", build_mode_to_str(g->build_mode)); + buf_appendf(contents, "pub const link_libc = %s;\n", bool_to_str(g->link_libc)); + buf_appendf(contents, "pub const link_libcpp = %s;\n", bool_to_str(g->link_libcpp)); + buf_appendf(contents, "pub const have_error_return_tracing = %s;\n", bool_to_str(g->have_err_ret_tracing)); + buf_appendf(contents, "pub const valgrind_support = false;\n"); + buf_appendf(contents, "pub const position_independent_code = %s;\n", bool_to_str(g->have_pic)); + buf_appendf(contents, "pub const strip_debug_info = %s;\n", bool_to_str(g->strip_debug_symbols)); + buf_appendf(contents, "pub const code_model = CodeModel.default;\n"); + + { + TargetSubsystem detected_subsystem = detect_subsystem(g); + if (detected_subsystem != TargetSubsystemAuto) { + buf_appendf(contents, "pub const explicit_subsystem = SubSystem.%s;\n", subsystem_to_str(detected_subsystem)); + } + } + + return contents; +} + +static ZigPackage *create_test_runner_pkg(CodeGen *g) { + return codegen_create_package(g, buf_ptr(g->zig_std_special_dir), "test_runner.zig", "std.special"); +} + +static Error define_builtin_compile_vars(CodeGen *g) { + Error err; + + if (g->std_package == nullptr) + return ErrorNone; + + assert(g->main_pkg); + + const char *builtin_zig_basename = "builtin.zig"; + + Buf *contents; + if (g->builtin_zig_path == nullptr) { + // Then this is zig0 building stage2. We can make many assumptions about the compilation. + Buf *out_dir = buf_alloc(); + os_path_split(&g->o_file_output_path, out_dir, nullptr); + g->builtin_zig_path = buf_alloc(); + os_path_join(out_dir, buf_create_from_str(builtin_zig_basename), g->builtin_zig_path); + + Buf *resolve_paths[] = { g->builtin_zig_path, }; + *g->builtin_zig_path = os_path_resolve(resolve_paths, 1); + + contents = codegen_generate_builtin_source(g); + if ((err = os_write_file(g->builtin_zig_path, contents))) { + fprintf(stderr, "Unable to write file '%s': %s\n", buf_ptr(g->builtin_zig_path), err_str(err)); + exit(1); + } + + g->compile_var_package = new_package(buf_ptr(out_dir), builtin_zig_basename, "builtin"); + } else { + Buf *resolve_paths[] = { g->builtin_zig_path, }; + *g->builtin_zig_path = os_path_resolve(resolve_paths, 1); + + contents = buf_alloc(); + if ((err = os_fetch_file_path(g->builtin_zig_path, contents))) { + fprintf(stderr, "unable to open '%s': %s\n", buf_ptr(g->builtin_zig_path), err_str(err)); + exit(1); + } + Buf builtin_dirname = BUF_INIT; + os_path_dirname(g->builtin_zig_path, &builtin_dirname); + g->compile_var_package = new_package(buf_ptr(&builtin_dirname), builtin_zig_basename, "builtin"); + } + + if (g->is_test_build) { + if (g->test_runner_package == nullptr) { + g->test_runner_package = create_test_runner_pkg(g); + } + g->root_pkg = g->test_runner_package; + } else { + g->root_pkg = g->main_pkg; + } + g->compile_var_package->package_table.put(buf_create_from_str("std"), g->std_package); + g->main_pkg->package_table.put(buf_create_from_str("builtin"), g->compile_var_package); + g->main_pkg->package_table.put(buf_create_from_str("root"), g->root_pkg); + g->std_package->package_table.put(buf_create_from_str("builtin"), g->compile_var_package); + g->std_package->package_table.put(buf_create_from_str("std"), g->std_package); + g->std_package->package_table.put(buf_create_from_str("root"), g->root_pkg); + g->compile_var_import = add_source_file(g, g->compile_var_package, g->builtin_zig_path, contents, + SourceKindPkgMain); + + return ErrorNone; +} + +static void init(CodeGen *g) { + if (g->module) + return; + + codegen_add_time_event(g, "Initialize"); + { + const char *progress_name = "Initialize"; + codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node, + progress_name, strlen(progress_name), 0)); + } + + g->have_err_ret_tracing = detect_err_ret_tracing(g); + + assert(g->root_out_name); + g->module = LLVMModuleCreateWithName(buf_ptr(g->root_out_name)); + + LLVMSetTarget(g->module, buf_ptr(&g->llvm_triple_str)); + + if (target_object_format(g->zig_target) == ZigLLVM_COFF) { + ZigLLVMAddModuleCodeViewFlag(g->module); + } else { + ZigLLVMAddModuleDebugInfoFlag(g->module); + } + + LLVMTargetRef target_ref; + char *err_msg = nullptr; + if (LLVMGetTargetFromTriple(buf_ptr(&g->llvm_triple_str), &target_ref, &err_msg)) { + fprintf(stderr, + "Zig is expecting LLVM to understand this target: '%s'\n" + "However LLVM responded with: \"%s\"\n" + "Zig is unable to continue. This is a bug in Zig:\n" + "https://github.com/ziglang/zig/issues/438\n" + , buf_ptr(&g->llvm_triple_str), err_msg); + exit(1); + } + + bool is_optimized = g->build_mode != BuildModeDebug; + LLVMCodeGenOptLevel opt_level = is_optimized ? LLVMCodeGenLevelAggressive : LLVMCodeGenLevelNone; + + LLVMRelocMode reloc_mode; + if (g->have_pic) { + reloc_mode = LLVMRelocPIC; + } else if (g->link_mode_dynamic) { + reloc_mode = LLVMRelocDynamicNoPic; + } else { + reloc_mode = LLVMRelocStatic; + } + + const char *target_specific_cpu_args = ""; + const char *target_specific_features = ""; + + if (g->zig_target->is_native_cpu) { + target_specific_cpu_args = ZigLLVMGetHostCPUName(); + target_specific_features = ZigLLVMGetNativeFeatures(); + } + + // Override CPU and features if defined by user. + if (g->zig_target->llvm_cpu_name != nullptr) { + target_specific_cpu_args = g->zig_target->llvm_cpu_name; + } + if (g->zig_target->llvm_cpu_features != nullptr) { + target_specific_features = g->zig_target->llvm_cpu_features; + } + if (g->verbose_llvm_cpu_features) { + fprintf(stderr, "name=%s triple=%s\n", buf_ptr(g->root_out_name), buf_ptr(&g->llvm_triple_str)); + fprintf(stderr, "name=%s target_specific_cpu_args=%s\n", buf_ptr(g->root_out_name), target_specific_cpu_args); + fprintf(stderr, "name=%s target_specific_features=%s\n", buf_ptr(g->root_out_name), target_specific_features); + } + + // TODO handle float ABI better- it should depend on the ABI portion of std.Target + ZigLLVMABIType float_abi = ZigLLVMABITypeDefault; + + // TODO a way to override this as part of std.Target ABI? + const char *abi_name = nullptr; + if (target_is_riscv(g->zig_target)) { + // RISC-V Linux defaults to ilp32d/lp64d + if (g->zig_target->os == OsLinux) { + abi_name = (g->zig_target->arch == ZigLLVM_riscv32) ? "ilp32d" : "lp64d"; + } else { + abi_name = (g->zig_target->arch == ZigLLVM_riscv32) ? "ilp32" : "lp64"; + } + } + + g->target_machine = ZigLLVMCreateTargetMachine(target_ref, buf_ptr(&g->llvm_triple_str), + target_specific_cpu_args, target_specific_features, opt_level, reloc_mode, + to_llvm_code_model(g), g->function_sections, float_abi, abi_name); + + g->target_data_ref = LLVMCreateTargetDataLayout(g->target_machine); + + char *layout_str = LLVMCopyStringRepOfTargetData(g->target_data_ref); + LLVMSetDataLayout(g->module, layout_str); + + + assert(g->pointer_size_bytes == LLVMPointerSize(g->target_data_ref)); + g->is_big_endian = (LLVMByteOrder(g->target_data_ref) == LLVMBigEndian); + + g->builder = LLVMCreateBuilder(); + g->dbuilder = ZigLLVMCreateDIBuilder(g->module, true); + + // Don't use ZIG_VERSION_STRING here, llvm misparses it when it includes + // the git revision. + Buf *producer = buf_sprintf("zig %d.%d.%d", ZIG_VERSION_MAJOR, ZIG_VERSION_MINOR, ZIG_VERSION_PATCH); + const char *flags = ""; + unsigned runtime_version = 0; + + // For macOS stack traces, we want to avoid having to parse the compilation unit debug + // info. As long as each debug info file has a path independent of the compilation unit + // directory (DW_AT_comp_dir), then we never have to look at the compilation unit debug + // info. If we provide an absolute path to LLVM here for the compilation unit debug info, + // LLVM will emit DWARF info that depends on DW_AT_comp_dir. To avoid this, we pass "." + // for the compilation unit directory. This forces each debug file to have a directory + // rather than be relative to DW_AT_comp_dir. According to DWARF 5, debug files will + // no longer reference DW_AT_comp_dir, for the purpose of being able to support the + // common practice of stripping all but the line number sections from an executable. + const char *compile_unit_dir = target_os_is_darwin(g->zig_target->os) ? "." : + buf_ptr(&g->main_pkg->root_src_dir); + + ZigLLVMDIFile *compile_unit_file = ZigLLVMCreateFile(g->dbuilder, buf_ptr(g->root_out_name), + compile_unit_dir); + g->compile_unit = ZigLLVMCreateCompileUnit(g->dbuilder, ZigLLVMLang_DW_LANG_C99(), + compile_unit_file, buf_ptr(producer), is_optimized, flags, runtime_version, + "", 0, !g->strip_debug_symbols); + + // This is for debug stuff that doesn't have a real file. + g->dummy_di_file = nullptr; + + define_builtin_types(g); + define_intern_values(g); + + IrInstGen *sentinel_instructions = heap::c_allocator.allocate(2); + g->invalid_inst_gen = &sentinel_instructions[0]; + g->invalid_inst_gen->value = g->pass1_arena->create(); + g->invalid_inst_gen->value->type = g->builtin_types.entry_invalid; + + g->unreach_instruction = &sentinel_instructions[1]; + g->unreach_instruction->value = g->pass1_arena->create(); + g->unreach_instruction->value->type = g->builtin_types.entry_unreachable; + + g->invalid_inst_src = heap::c_allocator.create(); + + define_builtin_fns(g); + Error err; + if ((err = define_builtin_compile_vars(g))) { + fprintf(stderr, "Unable to create builtin.zig: %s\n", err_str(err)); + exit(1); + } +} + +static void update_test_functions_builtin_decl(CodeGen *g) { + Error err; + + assert(g->is_test_build); + + if (g->test_fns.length == 0) { + fprintf(stderr, "No tests to run.\n"); + exit(0); + } + + ZigType *fn_type = get_test_fn_type(g); + + ZigValue *test_fn_type_val = get_builtin_value(g, "TestFn"); + assert(test_fn_type_val->type->id == ZigTypeIdMetaType); + ZigType *struct_type = test_fn_type_val->data.x_type; + if ((err = type_resolve(g, struct_type, ResolveStatusSizeKnown))) + zig_unreachable(); + + ZigValue *test_fn_array = g->pass1_arena->create(); + test_fn_array->type = get_array_type(g, struct_type, g->test_fns.length, nullptr); + test_fn_array->special = ConstValSpecialStatic; + test_fn_array->data.x_array.data.s_none.elements = g->pass1_arena->allocate(g->test_fns.length); + + for (size_t i = 0; i < g->test_fns.length; i += 1) { + ZigFn *test_fn_entry = g->test_fns.at(i); + + ZigValue *this_val = &test_fn_array->data.x_array.data.s_none.elements[i]; + this_val->special = ConstValSpecialStatic; + this_val->type = struct_type; + this_val->parent.id = ConstParentIdArray; + this_val->parent.data.p_array.array_val = test_fn_array; + this_val->parent.data.p_array.elem_index = i; + this_val->data.x_struct.fields = alloc_const_vals_ptrs(g, 3); + + ZigValue *name_field = this_val->data.x_struct.fields[0]; + ZigValue *name_array_val = create_const_str_lit(g, &test_fn_entry->symbol_name)->data.x_ptr.data.ref.pointee; + init_const_slice(g, name_field, name_array_val, 0, buf_len(&test_fn_entry->symbol_name), true); + + ZigValue *fn_field = this_val->data.x_struct.fields[1]; + fn_field->type = fn_type; + fn_field->special = ConstValSpecialStatic; + fn_field->data.x_ptr.special = ConstPtrSpecialFunction; + fn_field->data.x_ptr.mut = ConstPtrMutComptimeConst; + fn_field->data.x_ptr.data.fn.fn_entry = test_fn_entry; + + ZigValue *frame_size_field = this_val->data.x_struct.fields[2]; + frame_size_field->type = get_optional_type(g, g->builtin_types.entry_usize); + frame_size_field->special = ConstValSpecialStatic; + frame_size_field->data.x_optional = nullptr; + + if (fn_is_async(test_fn_entry)) { + frame_size_field->data.x_optional = g->pass1_arena->create(); + frame_size_field->data.x_optional->special = ConstValSpecialStatic; + frame_size_field->data.x_optional->type = g->builtin_types.entry_usize; + bigint_init_unsigned(&frame_size_field->data.x_optional->data.x_bigint, + test_fn_entry->frame_type->abi_size); + } + } + report_errors_and_maybe_exit(g); + + ZigValue *test_fn_slice = create_const_slice(g, test_fn_array, 0, g->test_fns.length, true); + + update_compile_var(g, buf_create_from_str("test_functions"), test_fn_slice); + assert(g->test_runner_package != nullptr); +} + +static Buf *get_resolved_root_src_path(CodeGen *g) { + // TODO memoize + if (buf_len(&g->main_pkg->root_src_path) == 0) + return nullptr; + + Buf rel_full_path = BUF_INIT; + os_path_join(&g->main_pkg->root_src_dir, &g->main_pkg->root_src_path, &rel_full_path); + + Buf *resolved_path = buf_alloc(); + Buf *resolve_paths[] = {&rel_full_path}; + *resolved_path = os_path_resolve(resolve_paths, 1); + + return resolved_path; +} + +static void gen_root_source(CodeGen *g) { + Buf *resolved_path = get_resolved_root_src_path(g); + if (resolved_path == nullptr) + return; + + Buf *source_code = buf_alloc(); + Error err; + // No need for using the caching system for this file fetch because it is handled + // separately. + if ((err = os_fetch_file_path(resolved_path, source_code))) { + fprintf(stderr, "unable to open '%s': %s\n", buf_ptr(resolved_path), err_str(err)); + exit(1); + } + + ZigType *root_import_alias = add_source_file(g, g->main_pkg, resolved_path, source_code, SourceKindRoot); + assert(root_import_alias == g->root_import); + + assert(g->root_out_name); + + // Zig has lazy top level definitions. Here we semantically analyze the panic function. + Buf *import_target_path; + Buf full_path = BUF_INIT; + ZigType *std_import; + if ((err = analyze_import(g, g->root_import, buf_create_from_str("std"), &std_import, + &import_target_path, &full_path))) + { + if (err == ErrorFileNotFound) { + fprintf(stderr, "unable to find '%s'", buf_ptr(import_target_path)); + } else { + fprintf(stderr, "unable to open '%s': %s\n", buf_ptr(&full_path), err_str(err)); + } + exit(1); + } + + Tld *builtin_tld = find_decl(g, &get_container_scope(std_import)->base, + buf_create_from_str("builtin")); + assert(builtin_tld != nullptr); + resolve_top_level_decl(g, builtin_tld, nullptr, false); + report_errors_and_maybe_exit(g); + assert(builtin_tld->id == TldIdVar); + TldVar *builtin_tld_var = (TldVar*)builtin_tld; + ZigValue *builtin_val = builtin_tld_var->var->const_value; + assert(builtin_val->type->id == ZigTypeIdMetaType); + ZigType *builtin_type = builtin_val->data.x_type; + + Tld *panic_tld = find_decl(g, &get_container_scope(builtin_type)->base, + buf_create_from_str("panic")); + assert(panic_tld != nullptr); + resolve_top_level_decl(g, panic_tld, nullptr, false); + report_errors_and_maybe_exit(g); + assert(panic_tld->id == TldIdVar); + TldVar *panic_tld_var = (TldVar*)panic_tld; + ZigValue *panic_fn_val = panic_tld_var->var->const_value; + assert(panic_fn_val->type->id == ZigTypeIdFn); + assert(panic_fn_val->data.x_ptr.special == ConstPtrSpecialFunction); + g->panic_fn = panic_fn_val->data.x_ptr.data.fn.fn_entry; + assert(g->panic_fn != nullptr); + + if (!g->error_during_imports) { + semantic_analyze(g); + } + report_errors_and_maybe_exit(g); + + if (g->is_test_build) { + update_test_functions_builtin_decl(g); + if (!g->error_during_imports) { + semantic_analyze(g); + } + } + + report_errors_and_maybe_exit(g); + +} + +void codegen_print_timing_report(CodeGen *g, FILE *f) { + double start_time = g->timing_events.at(0).time; + double end_time = g->timing_events.last().time; + double total = end_time - start_time; + fprintf(f, "%20s%12s%12s%12s%12s\n", "Name", "Start", "End", "Duration", "Percent"); + for (size_t i = 0; i < g->timing_events.length - 1; i += 1) { + TimeEvent *te = &g->timing_events.at(i); + TimeEvent *next_te = &g->timing_events.at(i + 1); + fprintf(f, "%20s%12.4f%12.4f%12.4f%12.4f\n", te->name, + te->time - start_time, + next_te->time - start_time, + next_te->time - te->time, + (next_te->time - te->time) / total); + } + fprintf(f, "%20s%12.4f%12.4f%12.4f%12.4f\n", "Total", 0.0, total, total, 1.0); +} + +void codegen_add_time_event(CodeGen *g, const char *name) { + OsTimeStamp timestamp = os_timestamp_monotonic(); + double seconds = (double)timestamp.sec; + seconds += ((double)timestamp.nsec) / 1000000000.0; + g->timing_events.append({seconds, name}); +} + +void codegen_build_object(CodeGen *g) { + g->have_err_ret_tracing = detect_err_ret_tracing(g); + + init(g); + + codegen_add_time_event(g, "Semantic Analysis"); + const char *progress_name = "Semantic Analysis"; + codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node, + progress_name, strlen(progress_name), 0)); + + gen_root_source(g); + + if (buf_len(&g->analysis_json_output_path) != 0) { + const char *analysis_json_filename = buf_ptr(&g->analysis_json_output_path); + FILE *f = fopen(analysis_json_filename, "wb"); + if (f == nullptr) { + fprintf(stderr, "Unable to open '%s': %s\n", analysis_json_filename, strerror(errno)); + exit(1); + } + zig_print_analysis_dump(g, f, " ", "\n"); + if (fclose(f) != 0) { + fprintf(stderr, "Unable to write '%s': %s\n", analysis_json_filename, strerror(errno)); + exit(1); + } + } + if (buf_len(&g->docs_output_path) != 0) { + Error err; + Buf *doc_dir_path = &g->docs_output_path; + if ((err = os_make_path(doc_dir_path))) { + fprintf(stderr, "Unable to create directory %s: %s\n", buf_ptr(doc_dir_path), err_str(err)); + exit(1); + } + Buf *index_html_src_path = buf_sprintf("%s" OS_SEP "special" OS_SEP "docs" OS_SEP "index.html", + buf_ptr(g->zig_std_dir)); + Buf *index_html_dest_path = buf_sprintf("%s" OS_SEP "index.html", buf_ptr(doc_dir_path)); + Buf *main_js_src_path = buf_sprintf("%s" OS_SEP "special" OS_SEP "docs" OS_SEP "main.js", + buf_ptr(g->zig_std_dir)); + Buf *main_js_dest_path = buf_sprintf("%s" OS_SEP "main.js", buf_ptr(doc_dir_path)); + + if ((err = os_copy_file(index_html_src_path, index_html_dest_path))) { + fprintf(stderr, "Unable to copy %s to %s: %s\n", buf_ptr(index_html_src_path), + buf_ptr(index_html_dest_path), err_str(err)); + exit(1); + } + if ((err = os_copy_file(main_js_src_path, main_js_dest_path))) { + fprintf(stderr, "Unable to copy %s to %s: %s\n", buf_ptr(main_js_src_path), + buf_ptr(main_js_dest_path), err_str(err)); + exit(1); + } + const char *data_js_filename = buf_ptr(buf_sprintf("%s" OS_SEP "data.js", buf_ptr(doc_dir_path))); + FILE *f = fopen(data_js_filename, "wb"); + if (f == nullptr) { + fprintf(stderr, "Unable to open '%s': %s\n", data_js_filename, strerror(errno)); + exit(1); + } + fprintf(f, "zigAnalysis="); + zig_print_analysis_dump(g, f, "", ""); + fprintf(f, ";"); + if (fclose(f) != 0) { + fprintf(stderr, "Unable to write '%s': %s\n", data_js_filename, strerror(errno)); + exit(1); + } + } + + codegen_add_time_event(g, "Code Generation"); + { + const char *progress_name = "Code Generation"; + codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node, + progress_name, strlen(progress_name), 0)); + } + + do_code_gen(g); + codegen_add_time_event(g, "LLVM Emit Output"); + { + const char *progress_name = "LLVM Emit Output"; + codegen_switch_sub_prog_node(g, stage2_progress_start(g->main_progress_node, + progress_name, strlen(progress_name), 0)); + } + zig_llvm_emit_output(g); + + codegen_add_time_event(g, "Done"); + codegen_switch_sub_prog_node(g, nullptr); +} + +ZigPackage *codegen_create_package(CodeGen *g, const char *root_src_dir, const char *root_src_path, + const char *pkg_path) +{ + init(g); + ZigPackage *pkg = new_package(root_src_dir, root_src_path, pkg_path); + if (g->std_package != nullptr) { + assert(g->compile_var_package != nullptr); + pkg->package_table.put(buf_create_from_str("std"), g->std_package); + + pkg->package_table.put(buf_create_from_str("root"), g->root_pkg); + + pkg->package_table.put(buf_create_from_str("builtin"), g->compile_var_package); + } + return pkg; +} + +void codegen_destroy(CodeGen *g) { + if (g->pass1_arena != nullptr) { + g->pass1_arena->destruct(&heap::c_allocator); + g->pass1_arena = nullptr; + } + heap::c_allocator.destroy(g); +} + +CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget *target, + BuildMode build_mode, Buf *override_lib_dir, + bool is_test_build) +{ + CodeGen *g = heap::c_allocator.create(); + g->pass1_arena = heap::ArenaAllocator::construct(&heap::c_allocator, &heap::c_allocator, "pass1"); + + g->subsystem = TargetSubsystemAuto; + g->zig_target = target; + + assert(override_lib_dir != nullptr); + g->zig_lib_dir = override_lib_dir; + + g->zig_std_dir = buf_alloc(); + os_path_join(g->zig_lib_dir, buf_create_from_str("std"), g->zig_std_dir); + + g->build_mode = build_mode; + g->import_table.init(32); + g->builtin_fn_table.init(32); + g->primitive_type_table.init(32); + g->type_table.init(32); + g->fn_type_table.init(32); + g->error_table.init(16); + g->generic_table.init(16); + g->llvm_fn_table.init(16); + g->memoized_fn_eval_table.init(16); + g->exported_symbol_names.init(8); + g->external_symbol_names.init(8); + g->string_literals_table.init(16); + g->type_info_cache.init(32); + g->one_possible_values.init(32); + g->is_test_build = is_test_build; + g->is_single_threaded = false; + g->code_model = CodeModelDefault; + buf_resize(&g->global_asm, 0); + + for (size_t i = 0; i < array_length(symbols_that_llvm_depends_on); i += 1) { + g->external_symbol_names.put(buf_create_from_str(symbols_that_llvm_depends_on[i]), nullptr); + } + + if (root_src_path) { + Buf *root_pkg_path; + Buf *rel_root_src_path; + if (main_pkg_path == nullptr) { + Buf *src_basename = buf_alloc(); + Buf *src_dir = buf_alloc(); + os_path_split(root_src_path, src_dir, src_basename); + + if (buf_len(src_basename) == 0) { + fprintf(stderr, "Invalid root source path: %s\n", buf_ptr(root_src_path)); + exit(1); + } + root_pkg_path = src_dir; + rel_root_src_path = src_basename; + } else { + Buf resolved_root_src_path = os_path_resolve(&root_src_path, 1); + Buf resolved_main_pkg_path = os_path_resolve(&main_pkg_path, 1); + + if (!buf_starts_with_buf(&resolved_root_src_path, &resolved_main_pkg_path)) { + fprintf(stderr, "Root source path '%s' outside main package path '%s'\n", + buf_ptr(root_src_path), buf_ptr(main_pkg_path)); + exit(1); + } + root_pkg_path = main_pkg_path; + rel_root_src_path = buf_create_from_mem( + buf_ptr(&resolved_root_src_path) + buf_len(&resolved_main_pkg_path) + 1, + buf_len(&resolved_root_src_path) - buf_len(&resolved_main_pkg_path) - 1); + } + + g->main_pkg = new_package(buf_ptr(root_pkg_path), buf_ptr(rel_root_src_path), ""); + g->std_package = new_package(buf_ptr(g->zig_std_dir), "std.zig", "std"); + g->main_pkg->package_table.put(buf_create_from_str("std"), g->std_package); + } else { + g->main_pkg = new_package(".", "", ""); + } + + g->zig_std_special_dir = buf_alloc(); + os_path_join(g->zig_std_dir, buf_sprintf("special"), g->zig_std_special_dir); + + target_triple_llvm(&g->llvm_triple_str, g->zig_target); + g->pointer_size_bytes = target_arch_pointer_bit_width(g->zig_target->arch) / 8; + + if (!target_has_debug_info(g->zig_target)) { + g->strip_debug_symbols = true; + } + + return g; +} + +bool codegen_fn_has_err_ret_tracing_arg(CodeGen *g, ZigType *return_type) { + return g->have_err_ret_tracing && + (return_type->id == ZigTypeIdErrorUnion || + return_type->id == ZigTypeIdErrorSet); +} + +bool codegen_fn_has_err_ret_tracing_stack(CodeGen *g, ZigFn *fn, bool is_async) { + if (is_async) { + return g->have_err_ret_tracing && (fn->calls_or_awaits_errorable_fn || + codegen_fn_has_err_ret_tracing_arg(g, fn->type_entry->data.fn.fn_type_id.return_type)); + } else { + return g->have_err_ret_tracing && fn->calls_or_awaits_errorable_fn && + !codegen_fn_has_err_ret_tracing_arg(g, fn->type_entry->data.fn.fn_type_id.return_type); + } +} + +void codegen_switch_sub_prog_node(CodeGen *g, Stage2ProgressNode *node) { + if (g->sub_progress_node != nullptr) { + stage2_progress_end(g->sub_progress_node); + } + g->sub_progress_node = node; +} + +ZigValue *CodeGen::Intern::for_undefined() { + return &this->x_undefined; +} + +ZigValue *CodeGen::Intern::for_void() { + return &this->x_void; +} + +ZigValue *CodeGen::Intern::for_null() { + return &this->x_null; +} + +ZigValue *CodeGen::Intern::for_unreachable() { + return &this->x_unreachable; +} + +ZigValue *CodeGen::Intern::for_zero_byte() { + return &this->zero_byte; +} diff --git a/src/stage1/codegen.hpp b/src/stage1/codegen.hpp new file mode 100644 index 0000000000000000000000000000000000000000..33b2f74757946ef23f3b93a883ff29007bfddfc9 --- /dev/null +++ b/src/stage1/codegen.hpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2015 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef ZIG_CODEGEN_HPP +#define ZIG_CODEGEN_HPP + +#include "parser.hpp" +#include "errmsg.hpp" +#include "target.hpp" +#include "stage2.h" + +#include + +CodeGen *codegen_create(Buf *main_pkg_path, Buf *root_src_path, const ZigTarget *target, + BuildMode build_mode, Buf *zig_lib_dir, bool is_test_build); + +void codegen_build_object(CodeGen *g); +void codegen_destroy(CodeGen *); + +void codegen_add_time_event(CodeGen *g, const char *name); +void codegen_print_timing_report(CodeGen *g, FILE *f); + +ZigPackage *codegen_create_package(CodeGen *g, const char *root_src_dir, const char *root_src_path, + const char *pkg_path); + +TargetSubsystem detect_subsystem(CodeGen *g); + +bool codegen_fn_has_err_ret_tracing_arg(CodeGen *g, ZigType *return_type); +bool codegen_fn_has_err_ret_tracing_stack(CodeGen *g, ZigFn *fn, bool is_async); + +ATTRIBUTE_NORETURN +void codegen_report_errors_and_exit(CodeGen *g); + +void codegen_switch_sub_prog_node(CodeGen *g, Stage2ProgressNode *node); + +#endif diff --git a/src/stage1/config.h.in b/src/stage1/config.h.in new file mode 100644 index 0000000000000000000000000000000000000000..8c147e7d6535d5b94a251a58ef449d9ee6a166d7 --- /dev/null +++ b/src/stage1/config.h.in @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2016 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef ZIG_CONFIG_H +#define ZIG_CONFIG_H + +#define ZIG_VERSION_MAJOR @ZIG_VERSION_MAJOR@ +#define ZIG_VERSION_MINOR @ZIG_VERSION_MINOR@ +#define ZIG_VERSION_PATCH @ZIG_VERSION_PATCH@ +#define ZIG_VERSION_STRING "@ZIG_VERSION@" + +// Used for communicating build information to self hosted build. +#define ZIG_CMAKE_BINARY_DIR "@CMAKE_BINARY_DIR@" +#define ZIG_CXX_COMPILER "@CMAKE_CXX_COMPILER@" +#define ZIG_LLD_INCLUDE_PATH "@LLD_INCLUDE_DIRS@" +#define ZIG_LLD_LIBRARIES "@LLD_LIBRARIES@" +#define ZIG_CLANG_LIBRARIES "@CLANG_LIBRARIES@" +#define ZIG_LLVM_CONFIG_EXE "@LLVM_CONFIG_EXE@" +#define ZIG_DIA_GUIDS_LIB "@ZIG_DIA_GUIDS_LIB_ESCAPED@" + +#endif diff --git a/src/stage1/dump_analysis.cpp b/src/stage1/dump_analysis.cpp new file mode 100644 index 0000000000000000000000000000000000000000..df0d6f3ca273aa6f3511124d4b03ea83dfa41f62 --- /dev/null +++ b/src/stage1/dump_analysis.cpp @@ -0,0 +1,1370 @@ +/* + * Copyright (c) 2019 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#include "dump_analysis.hpp" +#include "analyze.hpp" +#include "config.h" +#include "ir.hpp" +#include "codegen.hpp" +#include "os.hpp" + +enum JsonWriterState { + JsonWriterStateInvalid, + JsonWriterStateValue, + JsonWriterStateArrayStart, + JsonWriterStateArray, + JsonWriterStateObjectStart, + JsonWriterStateObject, +}; + +#define JSON_MAX_DEPTH 10 + +struct JsonWriter { + size_t state_index; + FILE *f; + const char *one_indent; + const char *nl; + JsonWriterState state[JSON_MAX_DEPTH]; +}; + +static void jw_init(JsonWriter *jw, FILE *f, const char *one_indent, const char *nl) { + jw->state_index = 1; + jw->f = f; + jw->one_indent = one_indent; + jw->nl = nl; + jw->state[0] = JsonWriterStateInvalid; + jw->state[1] = JsonWriterStateValue; +} + +static void jw_nl_indent(JsonWriter *jw) { + assert(jw->state_index >= 1); + fprintf(jw->f, "%s", jw->nl); + for (size_t i = 0; i < jw->state_index - 1; i += 1) { + fprintf(jw->f, "%s", jw->one_indent); + } +} + +static void jw_push_state(JsonWriter *jw, JsonWriterState state) { + jw->state_index += 1; + assert(jw->state_index < JSON_MAX_DEPTH); + jw->state[jw->state_index] = state; +} + +static void jw_pop_state(JsonWriter *jw) { + assert(jw->state_index != 0); + jw->state_index -= 1; +} + +static void jw_begin_array(JsonWriter *jw) { + assert(jw->state[jw->state_index] == JsonWriterStateValue); + fprintf(jw->f, "["); + jw->state[jw->state_index] = JsonWriterStateArrayStart; +} + +static void jw_begin_object(JsonWriter *jw) { + assert(jw->state[jw->state_index] == JsonWriterStateValue); + fprintf(jw->f, "{"); + jw->state[jw->state_index] = JsonWriterStateObjectStart; +} + +static void jw_array_elem(JsonWriter *jw) { + switch (jw->state[jw->state_index]) { + case JsonWriterStateInvalid: + case JsonWriterStateValue: + case JsonWriterStateObjectStart: + case JsonWriterStateObject: + zig_unreachable(); + case JsonWriterStateArray: + fprintf(jw->f, ","); + ZIG_FALLTHROUGH; + case JsonWriterStateArrayStart: + jw->state[jw->state_index] = JsonWriterStateArray; + jw_push_state(jw, JsonWriterStateValue); + jw_nl_indent(jw); + return; + } + zig_unreachable(); +} + +static void jw_write_escaped_string(JsonWriter *jw, const char *s) { + fprintf(jw->f, "\""); + for (;; s += 1) { + switch (*s) { + case 0: + fprintf(jw->f, "\""); + return; + case '"': + fprintf(jw->f, "\\\""); + continue; + case '\t': + fprintf(jw->f, "\\t"); + continue; + case '\r': + fprintf(jw->f, "\\r"); + continue; + case '\n': + fprintf(jw->f, "\\n"); + continue; + case '\b': + fprintf(jw->f, "\\b"); + continue; + case '\f': + fprintf(jw->f, "\\f"); + continue; + case '\\': + fprintf(jw->f, "\\\\"); + continue; + default: + fprintf(jw->f, "%c", *s); + continue; + } + } +} + +static void jw_object_field(JsonWriter *jw, const char *name) { + switch (jw->state[jw->state_index]) { + case JsonWriterStateInvalid: + case JsonWriterStateValue: + case JsonWriterStateArray: + case JsonWriterStateArrayStart: + zig_unreachable(); + case JsonWriterStateObject: + fprintf(jw->f, ","); + ZIG_FALLTHROUGH; + case JsonWriterStateObjectStart: + jw->state[jw->state_index] = JsonWriterStateObject; + jw_push_state(jw, JsonWriterStateValue); + jw_nl_indent(jw); + jw_write_escaped_string(jw, name); + fprintf(jw->f, ": "); + return; + } + zig_unreachable(); +} + +static void jw_end_array(JsonWriter *jw) { + switch (jw->state[jw->state_index]) { + case JsonWriterStateInvalid: + case JsonWriterStateValue: + case JsonWriterStateObjectStart: + case JsonWriterStateObject: + zig_unreachable(); + case JsonWriterStateArrayStart: + fprintf(jw->f, "]"); + jw_pop_state(jw); + return; + case JsonWriterStateArray: + jw_nl_indent(jw); + jw_pop_state(jw); + fprintf(jw->f, "]"); + return; + } + zig_unreachable(); +} + + +static void jw_end_object(JsonWriter *jw) { + switch (jw->state[jw->state_index]) { + case JsonWriterStateInvalid: + zig_unreachable(); + case JsonWriterStateValue: + zig_unreachable(); + case JsonWriterStateArray: + zig_unreachable(); + case JsonWriterStateArrayStart: + zig_unreachable(); + case JsonWriterStateObjectStart: + fprintf(jw->f, "}"); + jw_pop_state(jw); + return; + case JsonWriterStateObject: + jw_nl_indent(jw); + jw_pop_state(jw); + fprintf(jw->f, "}"); + return; + } + zig_unreachable(); +} + +static void jw_null(JsonWriter *jw) { + assert(jw->state[jw->state_index] == JsonWriterStateValue); + fprintf(jw->f, "null"); + jw_pop_state(jw); +} + +static void jw_bool(JsonWriter *jw, bool x) { + assert(jw->state[jw->state_index] == JsonWriterStateValue); + if (x) { + fprintf(jw->f, "true"); + } else { + fprintf(jw->f, "false"); + } + jw_pop_state(jw); +} + +static void jw_int(JsonWriter *jw, int64_t x) { + assert(jw->state[jw->state_index] == JsonWriterStateValue); + if (x > 4503599627370496 || x < -4503599627370496) { + fprintf(jw->f, "\"%" ZIG_PRI_i64 "\"", x); + } else { + fprintf(jw->f, "%" ZIG_PRI_i64, x); + } + jw_pop_state(jw); +} + +static void jw_bigint(JsonWriter *jw, const BigInt *x) { + assert(jw->state[jw->state_index] == JsonWriterStateValue); + Buf *str = buf_alloc(); + bigint_append_buf(str, x, 10); + + if (bigint_fits_in_bits(x, 52, true)) { + fprintf(jw->f, "%s", buf_ptr(str)); + } else { + fprintf(jw->f, "\"%s\"", buf_ptr(str)); + } + jw_pop_state(jw); + + buf_destroy(str); +} + +static void jw_string(JsonWriter *jw, const char *s) { + assert(jw->state[jw->state_index] == JsonWriterStateValue); + jw_write_escaped_string(jw, s); + jw_pop_state(jw); +} + + +static void tree_print(FILE *f, ZigType *ty, size_t indent); + +static int compare_type_abi_sizes_desc(const void *a, const void *b) { + uint64_t size_a = (*(ZigType * const*)(a))->abi_size; + uint64_t size_b = (*(ZigType * const*)(b))->abi_size; + if (size_a > size_b) + return -1; + if (size_a < size_b) + return 1; + return 0; +} + +static void start_child(FILE *f, size_t indent) { + fprintf(f, "\n"); + for (size_t i = 0; i < indent; i += 1) { + fprintf(f, " "); + } +} + +static void start_peer(FILE *f, size_t indent) { + fprintf(f, ",\n"); + for (size_t i = 0; i < indent; i += 1) { + fprintf(f, " "); + } +} + +static void tree_print_struct(FILE *f, ZigType *struct_type, size_t indent) { + ZigList children = {}; + uint64_t sum_from_fields = 0; + for (size_t i = 0; i < struct_type->data.structure.src_field_count; i += 1) { + TypeStructField *field = struct_type->data.structure.fields[i]; + children.append(field->type_entry); + sum_from_fields += field->type_entry->abi_size; + } + qsort(children.items, children.length, sizeof(ZigType *), compare_type_abi_sizes_desc); + + start_peer(f, indent); + fprintf(f, "\"padding\": \"%" ZIG_PRI_u64 "\"", struct_type->abi_size - sum_from_fields); + + start_peer(f, indent); + fprintf(f, "\"fields\": ["); + + for (size_t i = 0; i < children.length; i += 1) { + if (i == 0) { + start_child(f, indent + 1); + } else { + start_peer(f, indent + 1); + } + fprintf(f, "{"); + + ZigType *child_type = children.at(i); + tree_print(f, child_type, indent + 2); + + start_child(f, indent + 1); + fprintf(f, "}"); + } + + start_child(f, indent); + fprintf(f, "]"); +} + +static void tree_print(FILE *f, ZigType *ty, size_t indent) { + start_child(f, indent); + fprintf(f, "\"type\": \"%s\"", buf_ptr(&ty->name)); + + start_peer(f, indent); + fprintf(f, "\"sizef\": \""); + zig_pretty_print_bytes(f, ty->abi_size); + fprintf(f, "\""); + + start_peer(f, indent); + fprintf(f, "\"size\": \"%" ZIG_PRI_usize "\"", ty->abi_size); + + switch (ty->id) { + case ZigTypeIdFnFrame: + return tree_print_struct(f, ty->data.frame.locals_struct, indent); + case ZigTypeIdStruct: + return tree_print_struct(f, ty, indent); + default: + start_child(f, indent); + return; + } +} + +void zig_print_stack_report(CodeGen *g, FILE *f) { + if (g->largest_frame_fn == nullptr) { + fprintf(f, "{\"error\": \"No async function frames in entire compilation.\"}\n"); + return; + } + fprintf(f, "{"); + tree_print(f, g->largest_frame_fn->frame_type, 1); + + start_child(f, 0); + fprintf(f, "}\n"); +} + +struct AnalDumpCtx { + CodeGen *g; + JsonWriter jw; + + ZigList type_list; + HashMap type_map; + + ZigList pkg_list; + HashMap pkg_map; + + ZigList file_list; + HashMap file_map; + + ZigList decl_list; + HashMap decl_map; + + ZigList fn_list; + HashMap fn_map; + + ZigList node_list; + HashMap node_map; + + ZigList err_list; + HashMap err_map; +}; + +static uint32_t anal_dump_get_type_id(AnalDumpCtx *ctx, ZigType *ty); +static void anal_dump_value(AnalDumpCtx *ctx, AstNode *source_node, ZigType *ty, ZigValue *value); + +static void anal_dump_poke_value(AnalDumpCtx *ctx, AstNode *source_node, ZigType *ty, ZigValue *value) { + Error err; + if (value->type != ty) { + return; + } + if ((err = ir_resolve_lazy(ctx->g, source_node, value))) { + codegen_report_errors_and_exit(ctx->g); + } + if (value->special == ConstValSpecialUndef) { + return; + } + if (value->special == ConstValSpecialRuntime) { + return; + } + switch (ty->id) { + case ZigTypeIdMetaType: { + ZigType *val_ty = value->data.x_type; + (void)anal_dump_get_type_id(ctx, val_ty); + return; + } + default: + return; + } + zig_unreachable(); +} + +static uint32_t anal_dump_get_type_id(AnalDumpCtx *ctx, ZigType *ty) { + uint32_t type_id = ctx->type_list.length; + auto existing_entry = ctx->type_map.put_unique(ty, type_id); + if (existing_entry == nullptr) { + ctx->type_list.append(ty); + } else { + type_id = existing_entry->value; + } + return type_id; +} + +static uint32_t anal_dump_get_pkg_id(AnalDumpCtx *ctx, ZigPackage *pkg) { + assert(pkg != nullptr); + uint32_t pkg_id = ctx->pkg_list.length; + auto existing_entry = ctx->pkg_map.put_unique(pkg, pkg_id); + if (existing_entry == nullptr) { + ctx->pkg_list.append(pkg); + } else { + pkg_id = existing_entry->value; + } + return pkg_id; +} + +static uint32_t anal_dump_get_file_id(AnalDumpCtx *ctx, Buf *file) { + uint32_t file_id = ctx->file_list.length; + auto existing_entry = ctx->file_map.put_unique(file, file_id); + if (existing_entry == nullptr) { + ctx->file_list.append(file); + } else { + file_id = existing_entry->value; + } + return file_id; +} + +static uint32_t anal_dump_get_node_id(AnalDumpCtx *ctx, AstNode *node) { + uint32_t node_id = ctx->node_list.length; + auto existing_entry = ctx->node_map.put_unique(node, node_id); + if (existing_entry == nullptr) { + ctx->node_list.append(node); + } else { + node_id = existing_entry->value; + } + return node_id; +} + +static uint32_t anal_dump_get_fn_id(AnalDumpCtx *ctx, ZigFn *fn) { + uint32_t fn_id = ctx->fn_list.length; + auto existing_entry = ctx->fn_map.put_unique(fn, fn_id); + if (existing_entry == nullptr) { + ctx->fn_list.append(fn); + + // poke the fn + (void)anal_dump_get_type_id(ctx, fn->type_entry); + (void)anal_dump_get_node_id(ctx, fn->proto_node); + } else { + fn_id = existing_entry->value; + } + return fn_id; +} + +static uint32_t anal_dump_get_err_id(AnalDumpCtx *ctx, ErrorTableEntry *err) { + uint32_t err_id = ctx->err_list.length; + auto existing_entry = ctx->err_map.put_unique(err, err_id); + if (existing_entry == nullptr) { + ctx->err_list.append(err); + } else { + err_id = existing_entry->value; + } + return err_id; +} + +static uint32_t anal_dump_get_decl_id(AnalDumpCtx *ctx, Tld *tld) { + uint32_t decl_id = ctx->decl_list.length; + auto existing_entry = ctx->decl_map.put_unique(tld, decl_id); + if (existing_entry == nullptr) { + ctx->decl_list.append(tld); + + if (tld->import != nullptr) { + (void)anal_dump_get_type_id(ctx, tld->import); + } + + // poke the types + switch (tld->id) { + case TldIdVar: { + TldVar *tld_var = reinterpret_cast(tld); + ZigVar *var = tld_var->var; + + if (var != nullptr) { + (void)anal_dump_get_type_id(ctx, var->var_type); + + if (var->const_value != nullptr) { + anal_dump_poke_value(ctx, var->decl_node, var->var_type, var->const_value); + } + } + break; + } + case TldIdFn: { + TldFn *tld_fn = reinterpret_cast(tld); + ZigFn *fn = tld_fn->fn_entry; + + if (fn != nullptr) { + (void)anal_dump_get_type_id(ctx, fn->type_entry); + } + break; + } + default: + break; + } + + } else { + decl_id = existing_entry->value; + } + return decl_id; +} + +static void anal_dump_type_ref(AnalDumpCtx *ctx, ZigType *ty) { + uint32_t type_id = anal_dump_get_type_id(ctx, ty); + jw_int(&ctx->jw, type_id); +} + +static void anal_dump_pkg_ref(AnalDumpCtx *ctx, ZigPackage *pkg) { + uint32_t pkg_id = anal_dump_get_pkg_id(ctx, pkg); + jw_int(&ctx->jw, pkg_id); +} + +static void anal_dump_file_ref(AnalDumpCtx *ctx, Buf *file) { + uint32_t file_id = anal_dump_get_file_id(ctx, file); + jw_int(&ctx->jw, file_id); +} + +static void anal_dump_node_ref(AnalDumpCtx *ctx, AstNode *node) { + uint32_t node_id = anal_dump_get_node_id(ctx, node); + jw_int(&ctx->jw, node_id); +} + +static void anal_dump_fn_ref(AnalDumpCtx *ctx, ZigFn *fn) { + uint32_t fn_id = anal_dump_get_fn_id(ctx, fn); + jw_int(&ctx->jw, fn_id); +} + +static void anal_dump_err_ref(AnalDumpCtx *ctx, ErrorTableEntry *err) { + uint32_t err_id = anal_dump_get_err_id(ctx, err); + jw_int(&ctx->jw, err_id); +} + +static void anal_dump_decl_ref(AnalDumpCtx *ctx, Tld *tld) { + uint32_t decl_id = anal_dump_get_decl_id(ctx, tld); + jw_int(&ctx->jw, decl_id); +} + +static void anal_dump_pkg(AnalDumpCtx *ctx, ZigPackage *pkg) { + JsonWriter *jw = &ctx->jw; + + Buf full_path_buf = BUF_INIT; + os_path_join(&pkg->root_src_dir, &pkg->root_src_path, &full_path_buf); + Buf *resolve_paths[] = { &full_path_buf, }; + Buf *resolved_path = buf_alloc(); + *resolved_path = os_path_resolve(resolve_paths, 1); + + auto import_entry = ctx->g->import_table.maybe_get(resolved_path); + if (!import_entry) { + return; + } + + jw_array_elem(jw); + jw_begin_object(jw); + + jw_object_field(jw, "name"); + jw_string(jw, buf_ptr(&pkg->pkg_path)); + + jw_object_field(jw, "file"); + anal_dump_file_ref(ctx, resolved_path); + + jw_object_field(jw, "main"); + anal_dump_type_ref(ctx, import_entry->value); + + jw_object_field(jw, "table"); + jw_begin_object(jw); + auto it = pkg->package_table.entry_iterator(); + for (;;) { + auto *entry = it.next(); + if (!entry) + break; + + ZigPackage *child_pkg = entry->value; + if (child_pkg != nullptr) { + jw_object_field(jw, buf_ptr(entry->key)); + anal_dump_pkg_ref(ctx, child_pkg); + } + } + jw_end_object(jw); + + jw_end_object(jw); +} + +static void anal_dump_decl(AnalDumpCtx *ctx, Tld *tld) { + JsonWriter *jw = &ctx->jw; + + bool make_obj = tld->id == TldIdVar || tld->id == TldIdFn; + if (make_obj) { + jw_array_elem(jw); + jw_begin_object(jw); + + jw_object_field(jw, "import"); + anal_dump_type_ref(ctx, tld->import); + + jw_object_field(jw, "src"); + anal_dump_node_ref(ctx, tld->source_node); + + jw_object_field(jw, "name"); + jw_string(jw, buf_ptr(tld->name)); + } + + switch (tld->id) { + case TldIdVar: { + TldVar *tld_var = reinterpret_cast(tld); + ZigVar *var = tld_var->var; + + if (var != nullptr) { + jw_object_field(jw, "kind"); + if (var->src_is_const) { + jw_string(jw, "const"); + } else { + jw_string(jw, "var"); + } + + if (var->is_thread_local) { + jw_object_field(jw, "threadlocal"); + jw_bool(jw, true); + } + + jw_object_field(jw, "type"); + anal_dump_type_ref(ctx, var->var_type); + + if (var->const_value != nullptr) { + jw_object_field(jw, "value"); + anal_dump_value(ctx, var->decl_node, var->var_type, var->const_value); + } + } + break; + } + case TldIdFn: { + TldFn *tld_fn = reinterpret_cast(tld); + ZigFn *fn = tld_fn->fn_entry; + + if (fn != nullptr) { + jw_object_field(jw, "kind"); + jw_string(jw, "const"); + + jw_object_field(jw, "type"); + anal_dump_type_ref(ctx, fn->type_entry); + + jw_object_field(jw, "value"); + anal_dump_fn_ref(ctx, fn); + } + break; + } + default: + break; + } + + if (make_obj) { + jw_end_object(jw); + } +} + +static void anal_dump_file(AnalDumpCtx *ctx, Buf *file) { + JsonWriter *jw = &ctx->jw; + jw_string(jw, buf_ptr(file)); +} + +static void anal_dump_value(AnalDumpCtx *ctx, AstNode *source_node, ZigType *ty, ZigValue *value) { + Error err; + + if (value->type != ty) { + jw_null(&ctx->jw); + return; + } + if ((err = ir_resolve_lazy(ctx->g, source_node, value))) { + codegen_report_errors_and_exit(ctx->g); + } + if (value->special == ConstValSpecialUndef) { + jw_string(&ctx->jw, "undefined"); + return; + } + if (value->special == ConstValSpecialRuntime) { + jw_null(&ctx->jw); + return; + } + switch (ty->id) { + case ZigTypeIdMetaType: { + ZigType *val_ty = value->data.x_type; + anal_dump_type_ref(ctx, val_ty); + return; + } + case ZigTypeIdFn: { + if (value->data.x_ptr.special == ConstPtrSpecialFunction) { + ZigFn *val_fn = value->data.x_ptr.data.fn.fn_entry; + anal_dump_fn_ref(ctx, val_fn); + } else { + jw_null(&ctx->jw); + } + return; + } + case ZigTypeIdOptional: { + if(optional_value_is_null(value)){ + jw_string(&ctx->jw, "null"); + } else { + jw_null(&ctx->jw); + } + return; + } + case ZigTypeIdInt: { + jw_bigint(&ctx->jw, &value->data.x_bigint); + return; + } + default: + jw_null(&ctx->jw); + return; + } + zig_unreachable(); +} + +static void anal_dump_pointer_attrs(AnalDumpCtx *ctx, ZigType *ty) { + JsonWriter *jw = &ctx->jw; + if (ty->data.pointer.explicit_alignment != 0) { + jw_object_field(jw, "align"); + jw_int(jw, ty->data.pointer.explicit_alignment); + } + if (ty->data.pointer.is_const) { + jw_object_field(jw, "const"); + jw_bool(jw, true); + } + if (ty->data.pointer.is_volatile) { + jw_object_field(jw, "volatile"); + jw_bool(jw, true); + } + if (ty->data.pointer.allow_zero) { + jw_object_field(jw, "allowZero"); + jw_bool(jw, true); + } + if (ty->data.pointer.host_int_bytes != 0) { + jw_object_field(jw, "hostIntBytes"); + jw_int(jw, ty->data.pointer.host_int_bytes); + + jw_object_field(jw, "bitOffsetInHost"); + jw_int(jw, ty->data.pointer.bit_offset_in_host); + } + + jw_object_field(jw, "elem"); + anal_dump_type_ref(ctx, ty->data.pointer.child_type); +} + +static void anal_dump_type(AnalDumpCtx *ctx, ZigType *ty) { + JsonWriter *jw = &ctx->jw; + jw_array_elem(jw); + jw_begin_object(jw); + + jw_object_field(jw, "kind"); + jw_int(jw, type_id_index(ty)); + + switch (ty->id) { + case ZigTypeIdMetaType: + case ZigTypeIdBool: + case ZigTypeIdEnumLiteral: + break; + case ZigTypeIdStruct: { + if (ty->data.structure.special == StructSpecialSlice) { + jw_object_field(jw, "len"); + jw_int(jw, 2); + anal_dump_pointer_attrs(ctx, ty->data.structure.fields[slice_ptr_index]->type_entry); + break; + } + + jw_object_field(jw, "name"); + jw_string(jw, buf_ptr(&ty->name)); + + jw_object_field(jw, "src"); + anal_dump_node_ref(ctx, ty->data.structure.decl_node); + + { + jw_object_field(jw, "pubDecls"); + jw_begin_array(jw); + + ScopeDecls *decls_scope = ty->data.structure.decls_scope; + auto it = decls_scope->decl_table.entry_iterator(); + for (;;) { + auto *entry = it.next(); + if (!entry) + break; + + Tld *tld = entry->value; + if (tld->visib_mod == VisibModPub) { + jw_array_elem(jw); + anal_dump_decl_ref(ctx, tld); + } + } + jw_end_array(jw); + } + + { + jw_object_field(jw, "privDecls"); + jw_begin_array(jw); + + ScopeDecls *decls_scope = ty->data.structure.decls_scope; + auto it = decls_scope->decl_table.entry_iterator(); + for (;;) { + auto *entry = it.next(); + if (!entry) + break; + + Tld *tld = entry->value; + if (tld->visib_mod == VisibModPrivate) { + jw_array_elem(jw); + anal_dump_decl_ref(ctx, tld); + } + } + jw_end_array(jw); + } + + if (ty->data.structure.src_field_count != 0) { + jw_object_field(jw, "fields"); + jw_begin_array(jw); + + for(size_t i = 0; i < ty->data.structure.src_field_count; i += 1) { + jw_array_elem(jw); + anal_dump_type_ref(ctx, ty->data.structure.fields[i]->type_entry); + } + jw_end_array(jw); + } + + if (ty->data.structure.root_struct != nullptr) { + Buf *path_buf = ty->data.structure.root_struct->path; + + jw_object_field(jw, "file"); + anal_dump_file_ref(ctx, path_buf); + } + break; + } + case ZigTypeIdUnion: { + jw_object_field(jw, "name"); + jw_string(jw, buf_ptr(&ty->name)); + + jw_object_field(jw, "src"); + anal_dump_node_ref(ctx, ty->data.unionation.decl_node); + + { + jw_object_field(jw, "pubDecls"); + jw_begin_array(jw); + + ScopeDecls *decls_scope = ty->data.unionation.decls_scope; + auto it = decls_scope->decl_table.entry_iterator(); + for (;;) { + auto *entry = it.next(); + if (!entry) + break; + + Tld *tld = entry->value; + if (tld->visib_mod == VisibModPub) { + jw_array_elem(jw); + anal_dump_decl_ref(ctx, tld); + } + } + jw_end_array(jw); + } + + { + jw_object_field(jw, "privDecls"); + jw_begin_array(jw); + + ScopeDecls *decls_scope = ty->data.unionation.decls_scope; + auto it = decls_scope->decl_table.entry_iterator(); + for (;;) { + auto *entry = it.next(); + if (!entry) + break; + + Tld *tld = entry->value; + if (tld->visib_mod == VisibModPrivate) { + jw_array_elem(jw); + anal_dump_decl_ref(ctx, tld); + } + } + jw_end_array(jw); + } + + if (ty->data.unionation.src_field_count != 0) { + jw_object_field(jw, "fields"); + jw_begin_array(jw); + + for(size_t i = 0; i < ty->data.unionation.src_field_count; i += 1) { + jw_array_elem(jw); + anal_dump_type_ref(ctx, ty->data.unionation.fields[i].type_entry); + } + jw_end_array(jw); + } + break; + } + case ZigTypeIdEnum: { + jw_object_field(jw, "name"); + jw_string(jw, buf_ptr(&ty->name)); + + jw_object_field(jw, "src"); + anal_dump_node_ref(ctx, ty->data.enumeration.decl_node); + + { + jw_object_field(jw, "pubDecls"); + jw_begin_array(jw); + + ScopeDecls *decls_scope = ty->data.enumeration.decls_scope; + auto it = decls_scope->decl_table.entry_iterator(); + for (;;) { + auto *entry = it.next(); + if (!entry) + break; + + Tld *tld = entry->value; + if (tld->visib_mod == VisibModPub) { + jw_array_elem(jw); + anal_dump_decl_ref(ctx, tld); + } + } + jw_end_array(jw); + } + + { + jw_object_field(jw, "privDecls"); + jw_begin_array(jw); + + ScopeDecls *decls_scope = ty->data.enumeration.decls_scope; + auto it = decls_scope->decl_table.entry_iterator(); + for (;;) { + auto *entry = it.next(); + if (!entry) + break; + + Tld *tld = entry->value; + if (tld->visib_mod == VisibModPrivate) { + jw_array_elem(jw); + anal_dump_decl_ref(ctx, tld); + } + } + jw_end_array(jw); + } + + if (ty->data.enumeration.src_field_count != 0) { + jw_object_field(jw, "fields"); + jw_begin_array(jw); + + for(size_t i = 0; i < ty->data.enumeration.src_field_count; i += 1) { + jw_array_elem(jw); + jw_bigint(jw, &ty->data.enumeration.fields[i].value); + } + jw_end_array(jw); + } + break; + } + case ZigTypeIdFloat: { + jw_object_field(jw, "bits"); + jw_int(jw, ty->data.floating.bit_count); + break; + } + case ZigTypeIdInt: { + if (ty->data.integral.is_signed) { + jw_object_field(jw, "i"); + } else { + jw_object_field(jw, "u"); + } + jw_int(jw, ty->data.integral.bit_count); + break; + } + case ZigTypeIdFn: { + jw_object_field(jw, "name"); + jw_string(jw, buf_ptr(&ty->name)); + + jw_object_field(jw, "generic"); + jw_bool(jw, ty->data.fn.is_generic); + + if (ty->data.fn.fn_type_id.return_type != nullptr) { + jw_object_field(jw, "ret"); + anal_dump_type_ref(ctx, ty->data.fn.fn_type_id.return_type); + } + + if (ty->data.fn.fn_type_id.param_count != 0) { + jw_object_field(jw, "args"); + jw_begin_array(jw); + for (size_t i = 0; i < ty->data.fn.fn_type_id.param_count; i += 1) { + jw_array_elem(jw); + if (ty->data.fn.fn_type_id.param_info[i].type != nullptr) { + anal_dump_type_ref(ctx, ty->data.fn.fn_type_id.param_info[i].type); + } else { + jw_null(jw); + } + } + jw_end_array(jw); + } + break; + } + case ZigTypeIdOptional: { + jw_object_field(jw, "child"); + anal_dump_type_ref(ctx, ty->data.maybe.child_type); + break; + } + case ZigTypeIdPointer: { + switch (ty->data.pointer.ptr_len) { + case PtrLenSingle: + break; + case PtrLenUnknown: + jw_object_field(jw, "len"); + jw_int(jw, 1); + break; + case PtrLenC: + jw_object_field(jw, "len"); + jw_int(jw, 3); + break; + } + anal_dump_pointer_attrs(ctx, ty); + break; + } + case ZigTypeIdErrorSet: { + if (type_is_global_error_set(ty)) { + break; + } + jw_object_field(jw, "name"); + jw_string(jw, buf_ptr(&ty->name)); + + if (ty->data.error_set.infer_fn != nullptr) { + jw_object_field(jw, "fn"); + anal_dump_fn_ref(ctx, ty->data.error_set.infer_fn); + } + jw_object_field(jw, "errors"); + jw_begin_array(jw); + for (uint32_t i = 0; i < ty->data.error_set.err_count; i += 1) { + jw_array_elem(jw); + ErrorTableEntry *err = ty->data.error_set.errors[i]; + anal_dump_err_ref(ctx, err); + } + jw_end_array(jw); + break; + } + case ZigTypeIdErrorUnion: { + jw_object_field(jw, "err"); + anal_dump_type_ref(ctx, ty->data.error_union.err_set_type); + + jw_object_field(jw, "payload"); + anal_dump_type_ref(ctx, ty->data.error_union.payload_type); + + break; + } + case ZigTypeIdArray: { + jw_object_field(jw, "len"); + jw_int(jw, ty->data.array.len); + + jw_object_field(jw, "elem"); + anal_dump_type_ref(ctx, ty->data.array.child_type); + break; + } + default: + jw_object_field(jw, "name"); + jw_string(jw, buf_ptr(&ty->name)); + break; + } + jw_end_object(jw); +} + +static void anal_dump_node(AnalDumpCtx *ctx, const AstNode *node) { + JsonWriter *jw = &ctx->jw; + + jw_begin_object(jw); + + jw_object_field(jw, "file"); + anal_dump_file_ref(ctx, node->owner->data.structure.root_struct->path); + + jw_object_field(jw, "line"); + jw_int(jw, node->line); + + jw_object_field(jw, "col"); + jw_int(jw, node->column); + + const Buf *doc_comments_buf = nullptr; + const Buf *name_buf = nullptr; + const ZigList *field_nodes = nullptr; + bool is_var_args = false; + bool is_noalias = false; + bool is_comptime = false; + + switch (node->type) { + case NodeTypeParamDecl: + doc_comments_buf = &node->data.param_decl.doc_comments; + name_buf = node->data.param_decl.name; + is_var_args = node->data.param_decl.is_var_args; + is_noalias = node->data.param_decl.is_noalias; + is_comptime = node->data.param_decl.is_comptime; + break; + case NodeTypeFnProto: + doc_comments_buf = &node->data.fn_proto.doc_comments; + field_nodes = &node->data.fn_proto.params; + is_var_args = node->data.fn_proto.is_var_args; + break; + case NodeTypeVariableDeclaration: + doc_comments_buf = &node->data.variable_declaration.doc_comments; + break; + case NodeTypeErrorSetField: + doc_comments_buf = &node->data.err_set_field.doc_comments; + break; + case NodeTypeStructField: + doc_comments_buf = &node->data.struct_field.doc_comments; + name_buf = node->data.struct_field.name; + break; + case NodeTypeContainerDecl: + field_nodes = &node->data.container_decl.fields; + doc_comments_buf = &node->data.container_decl.doc_comments; + break; + default: + break; + } + + if (doc_comments_buf != nullptr && doc_comments_buf->list.length != 0) { + jw_object_field(jw, "docs"); + jw_string(jw, buf_ptr(doc_comments_buf)); + } + + if (name_buf != nullptr) { + jw_object_field(jw, "name"); + jw_string(jw, buf_ptr(name_buf)); + } + + if (field_nodes != nullptr) { + jw_object_field(jw, "fields"); + jw_begin_array(jw); + for (size_t i = 0; i < field_nodes->length; i += 1) { + jw_array_elem(jw); + anal_dump_node_ref(ctx, field_nodes->at(i)); + } + jw_end_array(jw); + } + + if (is_var_args) { + jw_object_field(jw, "varArgs"); + jw_bool(jw, true); + } + + if (is_comptime) { + jw_object_field(jw, "comptime"); + jw_bool(jw, true); + } + + if (is_noalias) { + jw_object_field(jw, "noalias"); + jw_bool(jw, true); + } + + jw_end_object(jw); +} + +static void anal_dump_err(AnalDumpCtx *ctx, const ErrorTableEntry *err) { + JsonWriter *jw = &ctx->jw; + + jw_begin_object(jw); + + jw_object_field(jw, "src"); + anal_dump_node_ref(ctx, err->decl_node); + + jw_object_field(jw, "name"); + jw_string(jw, buf_ptr(&err->name)); + + jw_end_object(jw); +} + +static void anal_dump_fn(AnalDumpCtx *ctx, ZigFn *fn) { + JsonWriter *jw = &ctx->jw; + + jw_begin_object(jw); + + jw_object_field(jw, "src"); + anal_dump_node_ref(ctx, fn->proto_node); + + jw_object_field(jw, "type"); + anal_dump_type_ref(ctx, fn->type_entry); + + jw_end_object(jw); +} + +void zig_print_analysis_dump(CodeGen *g, FILE *f, const char *one_indent, const char *nl) { + AnalDumpCtx ctx = {}; + ctx.g = g; + JsonWriter *jw = &ctx.jw; + jw_init(jw, f, one_indent, nl); + ctx.type_map.init(16); + ctx.pkg_map.init(16); + ctx.file_map.init(16); + ctx.decl_map.init(16); + ctx.node_map.init(16); + ctx.fn_map.init(16); + ctx.err_map.init(16); + + jw_begin_object(jw); + + jw_object_field(jw, "typeKinds"); + jw_begin_array(jw); + for (size_t i = 0; i < type_id_len(); i += 1) { + jw_array_elem(jw); + jw_string(jw, type_id_name(type_id_at_index(i))); + } + jw_end_array(jw); + + jw_object_field(jw, "params"); + jw_begin_object(jw); + { + jw_object_field(jw, "zigVersion"); + jw_string(jw, ZIG_VERSION_STRING); + + jw_object_field(jw, "builds"); + jw_begin_array(jw); + jw_array_elem(jw); + jw_begin_object(jw); + jw_object_field(jw, "target"); + Buf triple_buf = BUF_INIT; + target_triple_zig(&triple_buf, g->zig_target); + jw_string(jw, buf_ptr(&triple_buf)); + jw_end_object(jw); + jw_end_array(jw); + + jw_object_field(jw, "rootName"); + jw_string(jw, buf_ptr(g->root_out_name)); + } + jw_end_object(jw); + + jw_object_field(jw, "rootPkg"); + anal_dump_pkg_ref(&ctx, g->main_pkg); + + // FIXME: Remove this ugly workaround. + // Right now the code in docs/main.js relies on the root of the main package being itself. + g->main_pkg->package_table.put(buf_create_from_str("root"), g->main_pkg); + + // Poke the functions + for (size_t i = 0; i < g->fn_defs.length; i += 1) { + ZigFn *fn = g->fn_defs.at(i); + (void)anal_dump_get_fn_id(&ctx, fn); + } + + jw_object_field(jw, "calls"); + jw_begin_array(jw); + { + ZigList var_stack = {}; + + auto it = g->memoized_fn_eval_table.entry_iterator(); + for (;;) { + auto *entry = it.next(); + if (!entry) + break; + + var_stack.resize(0); + ZigFn *fn = nullptr; + + Scope *scope = entry->key; + while (scope != nullptr) { + if (scope->id == ScopeIdVarDecl) { + ZigVar *var = reinterpret_cast(scope)->var; + var_stack.append(var); + } else if (scope->id == ScopeIdFnDef) { + fn = reinterpret_cast(scope)->fn_entry; + break; + } + scope = scope->parent; + } + ZigValue *result = entry->value; + + assert(fn != nullptr); + + jw_array_elem(jw); + jw_begin_object(jw); + + jw_object_field(jw, "fn"); + anal_dump_fn_ref(&ctx, fn); + + jw_object_field(jw, "result"); + { + jw_begin_object(jw); + + jw_object_field(jw, "type"); + anal_dump_type_ref(&ctx, result->type); + + jw_object_field(jw, "value"); + anal_dump_value(&ctx, scope->source_node, result->type, result); + + jw_end_object(jw); + } + + if (var_stack.length != 0) { + jw_object_field(jw, "args"); + jw_begin_array(jw); + + while (var_stack.length != 0) { + ZigVar *var = var_stack.pop(); + + jw_array_elem(jw); + jw_begin_object(jw); + + jw_object_field(jw, "type"); + anal_dump_type_ref(&ctx, var->var_type); + + jw_object_field(jw, "value"); + anal_dump_value(&ctx, scope->source_node, var->var_type, var->const_value); + + jw_end_object(jw); + } + jw_end_array(jw); + } + + jw_end_object(jw); + } + + var_stack.deinit(); + } + jw_end_array(jw); + + jw_object_field(jw, "packages"); + jw_begin_array(jw); + for (uint32_t i = 0; i < ctx.pkg_list.length; i += 1) { + anal_dump_pkg(&ctx, ctx.pkg_list.at(i)); + } + jw_end_array(jw); + + jw_object_field(jw, "types"); + jw_begin_array(jw); + + for (uint32_t i = 0; i < ctx.type_list.length; i += 1) { + ZigType *ty = ctx.type_list.at(i); + anal_dump_type(&ctx, ty); + } + jw_end_array(jw); + + jw_object_field(jw, "decls"); + jw_begin_array(jw); + for (uint32_t i = 0; i < ctx.decl_list.length; i += 1) { + Tld *decl = ctx.decl_list.at(i); + anal_dump_decl(&ctx, decl); + } + jw_end_array(jw); + + jw_object_field(jw, "fns"); + jw_begin_array(jw); + for (uint32_t i = 0; i < ctx.fn_list.length; i += 1) { + ZigFn *fn = ctx.fn_list.at(i); + jw_array_elem(jw); + anal_dump_fn(&ctx, fn); + } + jw_end_array(jw); + + jw_object_field(jw, "errors"); + jw_begin_array(jw); + for (uint32_t i = 0; i < ctx.err_list.length; i += 1) { + const ErrorTableEntry *err = ctx.err_list.at(i); + jw_array_elem(jw); + anal_dump_err(&ctx, err); + } + jw_end_array(jw); + + jw_object_field(jw, "astNodes"); + jw_begin_array(jw); + for (uint32_t i = 0; i < ctx.node_list.length; i += 1) { + const AstNode *node = ctx.node_list.at(i); + jw_array_elem(jw); + anal_dump_node(&ctx, node); + } + jw_end_array(jw); + + jw_object_field(jw, "files"); + jw_begin_array(jw); + for (uint32_t i = 0; i < ctx.file_list.length; i += 1) { + Buf *file = ctx.file_list.at(i); + jw_array_elem(jw); + anal_dump_file(&ctx, file); + } + jw_end_array(jw); + + jw_end_object(jw); +} diff --git a/src/stage1/dump_analysis.hpp b/src/stage1/dump_analysis.hpp new file mode 100644 index 0000000000000000000000000000000000000000..6d1c644ea208b5ccf6b51dbf92d3943a09d5b836 --- /dev/null +++ b/src/stage1/dump_analysis.hpp @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2019 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef ZIG_DUMP_ANALYSIS_HPP +#define ZIG_DUMP_ANALYSIS_HPP + +#include "all_types.hpp" +#include + +void zig_print_stack_report(CodeGen *g, FILE *f); +void zig_print_analysis_dump(CodeGen *g, FILE *f, const char *one_indent, const char *nl); + +#endif diff --git a/src/stage1/empty.cpp b/src/stage1/empty.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/src/stage1/errmsg.cpp b/src/stage1/errmsg.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7bf096547fdb00805179d95c295080a95624eda8 --- /dev/null +++ b/src/stage1/errmsg.cpp @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2016 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#include "errmsg.hpp" +#include "os.hpp" + +#include + +enum ErrType { + ErrTypeError, + ErrTypeNote, +}; + +static void print_err_msg_type(ErrorMsg *err, ErrColor color, ErrType err_type) { + bool is_tty = os_stderr_tty(); + bool use_colors = color == ErrColorOn || (color == ErrColorAuto && is_tty); + + // Show the error location, if available + if (err->path != nullptr) { + const char *path = buf_ptr(err->path); + Slice pathslice{path, strlen(path)}; + + // Cache cwd + static Buf *cwdbuf{nullptr}; + static Slice cwd; + + if (cwdbuf == nullptr) { + cwdbuf = buf_alloc(); + Error err = os_get_cwd(cwdbuf); + if (err != ErrorNone) + zig_panic("get cwd failed"); + buf_append_char(cwdbuf, ZIG_OS_SEP_CHAR); + cwd.ptr = buf_ptr(cwdbuf); + cwd.len = strlen(cwd.ptr); + } + + const size_t line = err->line_start + 1; + const size_t col = err->column_start + 1; + if (use_colors) os_stderr_set_color(TermColorBold); + + // Strip cwd from path + if (memStartsWith(pathslice, cwd)) + fprintf(stderr, ".%c%s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize ": ", ZIG_OS_SEP_CHAR, path+cwd.len, line, col); + else + fprintf(stderr, "%s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize ": ", path, line, col); + } + + // Write out the error type + switch (err_type) { + case ErrTypeError: + if (use_colors) os_stderr_set_color(TermColorRed); + fprintf(stderr, "error: "); + break; + case ErrTypeNote: + if (use_colors) os_stderr_set_color(TermColorCyan); + fprintf(stderr, "note: "); + break; + default: + zig_unreachable(); + } + + // Write out the error message + if (use_colors) os_stderr_set_color(TermColorBold); + fputs(buf_ptr(err->msg), stderr); + if (use_colors) os_stderr_set_color(TermColorReset); + fputc('\n', stderr); + + if (buf_len(&err->line_buf) != 0){ + // Show the referenced line + fprintf(stderr, "%s\n", buf_ptr(&err->line_buf)); + for (size_t i = 0; i < err->column_start; i += 1) { + fprintf(stderr, " "); + } + // Draw the caret + if (use_colors) os_stderr_set_color(TermColorGreen); + fprintf(stderr, "^"); + if (use_colors) os_stderr_set_color(TermColorReset); + fprintf(stderr, "\n"); + } + + for (size_t i = 0; i < err->notes.length; i += 1) { + ErrorMsg *note = err->notes.at(i); + print_err_msg_type(note, color, ErrTypeNote); + } +} + +void print_err_msg(ErrorMsg *err, ErrColor color) { + print_err_msg_type(err, color, ErrTypeError); +} + +void err_msg_add_note(ErrorMsg *parent, ErrorMsg *note) { + parent->notes.append(note); +} + +ErrorMsg *err_msg_create_with_offset(Buf *path, size_t line, size_t column, size_t offset, + const char *source, Buf *msg) +{ + ErrorMsg *err_msg = heap::c_allocator.create(); + err_msg->path = path; + err_msg->line_start = line; + err_msg->column_start = column; + err_msg->msg = msg; + + if (source == nullptr) { + // Must initialize the buffer anyway + buf_init_from_str(&err_msg->line_buf, ""); + return err_msg; + } + + size_t line_start_offset = offset; + for (;;) { + if (line_start_offset == 0) { + break; + } + + line_start_offset -= 1; + + if (source[line_start_offset] == '\n') { + line_start_offset += 1; + break; + } + } + + size_t line_end_offset = offset; + while (source[line_end_offset] && source[line_end_offset] != '\n') { + line_end_offset += 1; + } + + buf_init_from_mem(&err_msg->line_buf, source + line_start_offset, line_end_offset - line_start_offset); + + return err_msg; +} + +ErrorMsg *err_msg_create_with_line(Buf *path, size_t line, size_t column, + Buf *source, ZigList *line_offsets, Buf *msg) +{ + ErrorMsg *err_msg = heap::c_allocator.create(); + err_msg->path = path; + err_msg->line_start = line; + err_msg->column_start = column; + err_msg->msg = msg; + + size_t line_start_offset = line_offsets->at(line); + size_t end_line = line + 1; + size_t line_end_offset = (end_line >= line_offsets->length) ? buf_len(source) : line_offsets->at(line + 1); + size_t len = (line_end_offset + 1 > line_start_offset) ? (line_end_offset - line_start_offset - 1) : 0; + if (len == SIZE_MAX) len = 0; + + buf_init_from_mem(&err_msg->line_buf, buf_ptr(source) + line_start_offset, len); + + return err_msg; +} diff --git a/src/stage1/errmsg.hpp b/src/stage1/errmsg.hpp new file mode 100644 index 0000000000000000000000000000000000000000..73cbd4e0d991568a12977d8c2d689c73051eb151 --- /dev/null +++ b/src/stage1/errmsg.hpp @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2015 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef ZIG_ERRMSG_HPP +#define ZIG_ERRMSG_HPP + +#include "buffer.hpp" +#include "list.hpp" +#include "stage1.h" + +struct ErrorMsg { + size_t line_start; + size_t column_start; + Buf *msg; + Buf *path; + Buf line_buf; + + ZigList notes; +}; + +void print_err_msg(ErrorMsg *msg, ErrColor color); + +void err_msg_add_note(ErrorMsg *parent, ErrorMsg *note); +ErrorMsg *err_msg_create_with_offset(Buf *path, size_t line, size_t column, size_t offset, + const char *source, Buf *msg); + +ErrorMsg *err_msg_create_with_line(Buf *path, size_t line, size_t column, + Buf *source, ZigList *line_offsets, Buf *msg); + +#endif diff --git a/src/stage1/error.cpp b/src/stage1/error.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d8bb4ac8a2b1a163f7590c3d14c89a923029255f --- /dev/null +++ b/src/stage1/error.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2016 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#include "error.hpp" + +const char *err_str(Error err) { + switch (err) { + case ErrorNone: return "(no error)"; + case ErrorNoMem: return "out of memory"; + case ErrorInvalidFormat: return "invalid format"; + case ErrorSemanticAnalyzeFail: return "semantic analyze failed"; + case ErrorAccess: return "access denied"; + case ErrorInterrupted: return "interrupted"; + case ErrorSystemResources: return "lack of system resources"; + case ErrorFileNotFound: return "file not found"; + case ErrorFileSystem: return "file system error"; + case ErrorFileTooBig: return "file too big"; + case ErrorDivByZero: return "division by zero"; + case ErrorOverflow: return "overflow"; + case ErrorPathAlreadyExists: return "path already exists"; + case ErrorUnexpected: return "unexpected error"; + case ErrorExactDivRemainder: return "exact division had a remainder"; + case ErrorNegativeDenominator: return "negative denominator"; + case ErrorShiftedOutOneBits: return "exact shift shifted out one bits"; + case ErrorCCompileErrors: return "C compile errors"; + case ErrorEndOfFile: return "end of file"; + case ErrorIsDir: return "is directory"; + case ErrorNotDir: return "not a directory"; + case ErrorUnsupportedOperatingSystem: return "unsupported operating system"; + case ErrorSharingViolation: return "sharing violation"; + case ErrorPipeBusy: return "pipe busy"; + case ErrorPrimitiveTypeNotFound: return "primitive type not found"; + case ErrorCacheUnavailable: return "cache unavailable"; + case ErrorPathTooLong: return "path too long"; + case ErrorCCompilerCannotFindFile: return "C compiler cannot find file"; + case ErrorReadingDepFile: return "failed to read .d file"; + case ErrorInvalidDepFile: return "invalid .d file"; + case ErrorMissingArchitecture: return "missing architecture"; + case ErrorMissingOperatingSystem: return "missing operating system"; + case ErrorUnknownArchitecture: return "unrecognized architecture"; + case ErrorUnknownOperatingSystem: return "unrecognized operating system"; + case ErrorUnknownABI: return "unrecognized C ABI"; + case ErrorInvalidFilename: return "invalid filename"; + case ErrorDiskQuota: return "disk space quota exceeded"; + case ErrorDiskSpace: return "out of disk space"; + case ErrorUnexpectedWriteFailure: return "unexpected write failure"; + case ErrorUnexpectedSeekFailure: return "unexpected seek failure"; + case ErrorUnexpectedFileTruncationFailure: return "unexpected file truncation failure"; + case ErrorUnimplemented: return "unimplemented"; + case ErrorOperationAborted: return "operation aborted"; + case ErrorBrokenPipe: return "broken pipe"; + case ErrorNoSpaceLeft: return "no space left"; + case ErrorNoCCompilerInstalled: return "no C compiler installed"; + case ErrorNotLazy: return "not lazy"; + case ErrorIsAsync: return "is async"; + case ErrorImportOutsidePkgPath: return "import of file outside package path"; + case ErrorUnknownCpu: return "unknown CPU"; + case ErrorUnknownCpuFeature: return "unknown CPU feature"; + case ErrorInvalidCpuFeatures: return "invalid CPU features"; + case ErrorInvalidLlvmCpuFeaturesFormat: return "invalid LLVM CPU features format"; + case ErrorUnknownApplicationBinaryInterface: return "unknown application binary interface"; + case ErrorASTUnitFailure: return "compiler bug: clang encountered a compile error, but the libclang API does not expose the error. See https://github.com/ziglang/zig/issues/4455 for more details"; + case ErrorBadPathName: return "bad path name"; + case ErrorSymLinkLoop: return "sym link loop"; + case ErrorProcessFdQuotaExceeded: return "process fd quota exceeded"; + case ErrorSystemFdQuotaExceeded: return "system fd quota exceeded"; + case ErrorNoDevice: return "no device"; + case ErrorDeviceBusy: return "device busy"; + case ErrorUnableToSpawnCCompiler: return "unable to spawn system C compiler"; + case ErrorCCompilerExitCode: return "system C compiler exited with failure code"; + case ErrorCCompilerCrashed: return "system C compiler crashed"; + case ErrorCCompilerCannotFindHeaders: return "system C compiler cannot find libc headers"; + case ErrorLibCRuntimeNotFound: return "libc runtime not found"; + case ErrorLibCStdLibHeaderNotFound: return "libc std lib headers not found"; + case ErrorLibCKernel32LibNotFound: return "kernel32 library not found"; + case ErrorUnsupportedArchitecture: return "unsupported architecture"; + case ErrorWindowsSdkNotFound: return "Windows SDK not found"; + case ErrorUnknownDynamicLinkerPath: return "unknown dynamic linker path"; + case ErrorTargetHasNoDynamicLinker: return "target has no dynamic linker"; + case ErrorInvalidAbiVersion: return "invalid C ABI version"; + case ErrorInvalidOperatingSystemVersion: return "invalid operating system version"; + case ErrorUnknownClangOption: return "unknown Clang option"; + case ErrorNestedResponseFile: return "nested response file"; + case ErrorZigIsTheCCompiler: return "Zig was not provided with libc installation information, and so it does not know where the libc paths are on the system. Zig attempted to use the system C compiler to find out where the libc paths are, but discovered that Zig is being used as the system C compiler."; + case ErrorFileBusy: return "file is busy"; + case ErrorLocked: return "file is locked by another process"; + } + return "(invalid error)"; +} diff --git a/src/stage1/error.hpp b/src/stage1/error.hpp new file mode 100644 index 0000000000000000000000000000000000000000..90772df10814b6f8ce452675803e9847658eb5cf --- /dev/null +++ b/src/stage1/error.hpp @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2015 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef ERROR_HPP +#define ERROR_HPP + +#include "stage2.h" + +const char *err_str(Error err); + +#define assertNoError(err) assert((err) == ErrorNone); + +#endif diff --git a/src/stage1/hash_map.hpp b/src/stage1/hash_map.hpp new file mode 100644 index 0000000000000000000000000000000000000000..8681e5b7613453b1d1b3446e363d257581e8dc4b --- /dev/null +++ b/src/stage1/hash_map.hpp @@ -0,0 +1,420 @@ +/* + * Copyright (c) 2015 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef ZIG_HASH_MAP_HPP +#define ZIG_HASH_MAP_HPP + +#include "util.hpp" + +#include + +template +class HashMap { +public: + void init(int capacity) { + init_capacity(capacity); + } + void deinit(void) { + _entries.deinit(); + heap::c_allocator.deallocate(_index_bytes, + _indexes_len * capacity_index_size(_indexes_len)); + } + + struct Entry { + uint32_t hash; + uint32_t distance_from_start_index; + K key; + V value; + }; + + void clear() { + _entries.clear(); + memset(_index_bytes, 0, _indexes_len * capacity_index_size(_indexes_len)); + _max_distance_from_start_index = 0; + _modification_count += 1; + } + + size_t size() const { + return _entries.length; + } + + void put(const K &key, const V &value) { + _modification_count += 1; + + // This allows us to take a pointer to an entry in `internal_put` which + // will not become a dead pointer when the array list is appended. + _entries.ensure_capacity(_entries.length + 1); + + if (_index_bytes == nullptr) { + if (_entries.length < 16) { + _entries.append({HashFunction(key), 0, key, value}); + return; + } else { + _indexes_len = 32; + _index_bytes = heap::c_allocator.allocate(_indexes_len); + _max_distance_from_start_index = 0; + for (size_t i = 0; i < _entries.length; i += 1) { + Entry *entry = &_entries.items[i]; + put_index(entry, i, _index_bytes); + } + return internal_put(key, value, _index_bytes); + } + } + + // if we would get too full (60%), double the indexes size + if ((_entries.length + 1) * 5 >= _indexes_len * 3) { + heap::c_allocator.deallocate(_index_bytes, + _indexes_len * capacity_index_size(_indexes_len)); + _indexes_len *= 2; + size_t sz = capacity_index_size(_indexes_len); + // This zero initializes the bytes, setting them all empty. + _index_bytes = heap::c_allocator.allocate(_indexes_len * sz); + _max_distance_from_start_index = 0; + for (size_t i = 0; i < _entries.length; i += 1) { + Entry *entry = &_entries.items[i]; + switch (sz) { + case 1: + put_index(entry, i, (uint8_t*)_index_bytes); + continue; + case 2: + put_index(entry, i, (uint16_t*)_index_bytes); + continue; + case 4: + put_index(entry, i, (uint32_t*)_index_bytes); + continue; + default: + put_index(entry, i, (size_t*)_index_bytes); + continue; + } + } + } + + switch (capacity_index_size(_indexes_len)) { + case 1: return internal_put(key, value, (uint8_t*)_index_bytes); + case 2: return internal_put(key, value, (uint16_t*)_index_bytes); + case 4: return internal_put(key, value, (uint32_t*)_index_bytes); + default: return internal_put(key, value, (size_t*)_index_bytes); + } + } + + Entry *put_unique(const K &key, const V &value) { + // TODO make this more efficient + Entry *entry = internal_get(key); + if (entry) + return entry; + put(key, value); + return nullptr; + } + + const V &get(const K &key) const { + Entry *entry = internal_get(key); + if (!entry) + zig_panic("key not found"); + return entry->value; + } + + Entry *maybe_get(const K &key) const { + return internal_get(key); + } + + bool remove(const K &key) { + bool deleted_something = maybe_remove(key); + if (!deleted_something) + zig_panic("key not found"); + return deleted_something; + } + + bool maybe_remove(const K &key) { + _modification_count += 1; + if (_index_bytes == nullptr) { + uint32_t hash = HashFunction(key); + for (size_t i = 0; i < _entries.length; i += 1) { + if (_entries.items[i].hash == hash && EqualFn(_entries.items[i].key, key)) { + _entries.swap_remove(i); + return true; + } + } + return false; + } + switch (capacity_index_size(_indexes_len)) { + case 1: return internal_remove(key, (uint8_t*)_index_bytes); + case 2: return internal_remove(key, (uint16_t*)_index_bytes); + case 4: return internal_remove(key, (uint32_t*)_index_bytes); + default: return internal_remove(key, (size_t*)_index_bytes); + } + } + + class Iterator { + public: + Entry *next() { + if (_inital_modification_count != _table->_modification_count) + zig_panic("concurrent modification"); + if (_index >= _table->_entries.length) + return nullptr; + Entry *entry = &_table->_entries.items[_index]; + _index += 1; + return entry; + } + private: + const HashMap * _table; + // iterator through the entry array + size_t _index = 0; + // used to detect concurrent modification + uint32_t _inital_modification_count; + Iterator(const HashMap * table) : + _table(table), _inital_modification_count(table->_modification_count) { + } + friend HashMap; + }; + + // you must not modify the underlying HashMap while this iterator is still in use + Iterator entry_iterator() const { + return Iterator(this); + } + +private: + // Maintains insertion order. + ZigList _entries; + // If _indexes_len is less than 2**8, this is an array of uint8_t. + // If _indexes_len is less than 2**16, it is an array of uint16_t. + // If _indexes_len is less than 2**32, it is an array of uint32_t. + // Otherwise it is size_t. + // It's off by 1. 0 means empty slot, 1 means index 0, etc. + uint8_t *_index_bytes; + // This is the number of indexes. When indexes are bytes, it equals number of bytes. + // When indexes are uint16_t, _indexes_len is half the number of bytes. + size_t _indexes_len; + + size_t _max_distance_from_start_index; + // This is used to detect bugs where a hashtable is edited while an iterator is running. + uint32_t _modification_count; + + void init_capacity(size_t capacity) { + _entries = {}; + _entries.ensure_capacity(capacity); + _indexes_len = 0; + if (capacity >= 16) { + // So that at capacity it will only be 60% full. + _indexes_len = capacity * 5 / 3; + size_t sz = capacity_index_size(_indexes_len); + // This zero initializes _index_bytes which sets them all to empty. + _index_bytes = heap::c_allocator.allocate(_indexes_len * sz); + } else { + _index_bytes = nullptr; + } + + _max_distance_from_start_index = 0; + _modification_count = 0; + } + + static size_t capacity_index_size(size_t len) { + if (len < UINT8_MAX) + return 1; + if (len < UINT16_MAX) + return 2; + if (len < UINT32_MAX) + return 4; + return sizeof(size_t); + } + + template + void internal_put(const K &key, const V &value, I *indexes) { + uint32_t hash = HashFunction(key); + uint32_t distance_from_start_index = 0; + size_t start_index = hash_to_index(hash); + for (size_t roll_over = 0; roll_over < _indexes_len; + roll_over += 1, distance_from_start_index += 1) + { + size_t index_index = (start_index + roll_over) % _indexes_len; + I index_data = indexes[index_index]; + if (index_data == 0) { + _entries.append_assuming_capacity({ hash, distance_from_start_index, key, value }); + indexes[index_index] = _entries.length; + if (distance_from_start_index > _max_distance_from_start_index) + _max_distance_from_start_index = distance_from_start_index; + return; + } + // This pointer survives the following append because we call + // _entries.ensure_capacity before internal_put. + Entry *entry = &_entries.items[index_data - 1]; + if (entry->hash == hash && EqualFn(entry->key, key)) { + *entry = {hash, distance_from_start_index, key, value}; + if (distance_from_start_index > _max_distance_from_start_index) + _max_distance_from_start_index = distance_from_start_index; + return; + } + if (entry->distance_from_start_index < distance_from_start_index) { + // In this case, we did not find the item. We will put a new entry. + // However, we will use this index for the new entry, and move + // the previous index down the line, to keep the _max_distance_from_start_index + // as small as possible. + _entries.append_assuming_capacity({ hash, distance_from_start_index, key, value }); + indexes[index_index] = _entries.length; + if (distance_from_start_index > _max_distance_from_start_index) + _max_distance_from_start_index = distance_from_start_index; + + distance_from_start_index = entry->distance_from_start_index; + + // Find somewhere to put the index we replaced by shifting + // following indexes backwards. + roll_over += 1; + distance_from_start_index += 1; + for (; roll_over < _indexes_len; roll_over += 1, distance_from_start_index += 1) { + size_t index_index = (start_index + roll_over) % _indexes_len; + I next_index_data = indexes[index_index]; + if (next_index_data == 0) { + if (distance_from_start_index > _max_distance_from_start_index) + _max_distance_from_start_index = distance_from_start_index; + entry->distance_from_start_index = distance_from_start_index; + indexes[index_index] = index_data; + return; + } + Entry *next_entry = &_entries.items[next_index_data - 1]; + if (next_entry->distance_from_start_index < distance_from_start_index) { + if (distance_from_start_index > _max_distance_from_start_index) + _max_distance_from_start_index = distance_from_start_index; + entry->distance_from_start_index = distance_from_start_index; + indexes[index_index] = index_data; + distance_from_start_index = next_entry->distance_from_start_index; + entry = next_entry; + index_data = next_index_data; + } + } + zig_unreachable(); + } + } + zig_unreachable(); + } + + template + void put_index(Entry *entry, size_t entry_index, I *indexes) { + size_t start_index = hash_to_index(entry->hash); + size_t index_data = entry_index + 1; + for (size_t roll_over = 0, distance_from_start_index = 0; + roll_over < _indexes_len; roll_over += 1, distance_from_start_index += 1) + { + size_t index_index = (start_index + roll_over) % _indexes_len; + size_t next_index_data = indexes[index_index]; + if (next_index_data == 0) { + if (distance_from_start_index > _max_distance_from_start_index) + _max_distance_from_start_index = distance_from_start_index; + entry->distance_from_start_index = distance_from_start_index; + indexes[index_index] = index_data; + return; + } + Entry *next_entry = &_entries.items[next_index_data - 1]; + if (next_entry->distance_from_start_index < distance_from_start_index) { + if (distance_from_start_index > _max_distance_from_start_index) + _max_distance_from_start_index = distance_from_start_index; + entry->distance_from_start_index = distance_from_start_index; + indexes[index_index] = index_data; + distance_from_start_index = next_entry->distance_from_start_index; + entry = next_entry; + index_data = next_index_data; + } + } + zig_unreachable(); + } + + Entry *internal_get(const K &key) const { + if (_index_bytes == nullptr) { + uint32_t hash = HashFunction(key); + for (size_t i = 0; i < _entries.length; i += 1) { + if (_entries.items[i].hash == hash && EqualFn(_entries.items[i].key, key)) { + return &_entries.items[i]; + } + } + return nullptr; + } + switch (capacity_index_size(_indexes_len)) { + case 1: return internal_get2(key, (uint8_t*)_index_bytes); + case 2: return internal_get2(key, (uint16_t*)_index_bytes); + case 4: return internal_get2(key, (uint32_t*)_index_bytes); + default: return internal_get2(key, (size_t*)_index_bytes); + } + } + + template + Entry *internal_get2(const K &key, I *indexes) const { + uint32_t hash = HashFunction(key); + size_t start_index = hash_to_index(hash); + for (size_t roll_over = 0; roll_over <= _max_distance_from_start_index; roll_over += 1) { + size_t index_index = (start_index + roll_over) % _indexes_len; + size_t index_data = indexes[index_index]; + if (index_data == 0) + return nullptr; + + Entry *entry = &_entries.items[index_data - 1]; + if (entry->hash == hash && EqualFn(entry->key, key)) + return entry; + } + return nullptr; + } + + size_t hash_to_index(uint32_t hash) const { + return ((size_t)hash) % _indexes_len; + } + + template + bool internal_remove(const K &key, I *indexes) { + uint32_t hash = HashFunction(key); + size_t start_index = hash_to_index(hash); + for (size_t roll_over = 0; roll_over <= _max_distance_from_start_index; roll_over += 1) { + size_t index_index = (start_index + roll_over) % _indexes_len; + size_t index_data = indexes[index_index]; + if (index_data == 0) + return false; + + size_t index = index_data - 1; + Entry *entry = &_entries.items[index]; + if (entry->hash != hash || !EqualFn(entry->key, key)) + continue; + + size_t prev_index = index_index; + _entries.swap_remove(index); + if (_entries.length > 0 && _entries.length != index) { + // Because of the swap remove, now we need to update the index that was + // pointing to the last entry and is now pointing to this removed item slot. + update_entry_index(_entries.length, index, indexes); + } + + // Now we have to shift over the following indexes. + roll_over += 1; + for (; roll_over < _indexes_len; roll_over += 1) { + size_t next_index = (start_index + roll_over) % _indexes_len; + if (indexes[next_index] == 0) { + indexes[prev_index] = 0; + return true; + } + Entry *next_entry = &_entries.items[indexes[next_index] - 1]; + if (next_entry->distance_from_start_index == 0) { + indexes[prev_index] = 0; + return true; + } + indexes[prev_index] = indexes[next_index]; + prev_index = next_index; + next_entry->distance_from_start_index -= 1; + } + zig_unreachable(); + } + return false; + } + + template + void update_entry_index(size_t old_entry_index, size_t new_entry_index, I *indexes) { + size_t start_index = hash_to_index(_entries.items[new_entry_index].hash); + for (size_t roll_over = 0; roll_over <= _max_distance_from_start_index; roll_over += 1) { + size_t index_index = (start_index + roll_over) % _indexes_len; + if (indexes[index_index] == old_entry_index + 1) { + indexes[index_index] = new_entry_index + 1; + return; + } + } + zig_unreachable(); + } +}; +#endif diff --git a/src/stage1/heap.cpp b/src/stage1/heap.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7e7a171bde25f95224d0479064d1f632a29b5bf9 --- /dev/null +++ b/src/stage1/heap.cpp @@ -0,0 +1,315 @@ +/* + * Copyright (c) 2020 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#include +#include + +#include "config.h" +#include "heap.hpp" + +namespace heap { + +extern mem::Allocator &bootstrap_allocator; + +// +// BootstrapAllocator implementation is identical to CAllocator minus +// profile profile functionality. Splitting off to a base interface doesn't +// seem worthwhile. +// + +void BootstrapAllocator::init(const char *name) {} +void BootstrapAllocator::deinit() {} + +void *BootstrapAllocator::internal_allocate(const mem::TypeInfo &info, size_t count) { + return mem::os::calloc(count, info.size); +} + +void *BootstrapAllocator::internal_allocate_nonzero(const mem::TypeInfo &info, size_t count) { + return mem::os::malloc(count * info.size); +} + +void *BootstrapAllocator::internal_reallocate(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) { + auto new_ptr = this->internal_reallocate_nonzero(info, old_ptr, old_count, new_count); + if (new_count > old_count) + memset(reinterpret_cast(new_ptr) + (old_count * info.size), 0, (new_count - old_count) * info.size); + return new_ptr; +} + +void *BootstrapAllocator::internal_reallocate_nonzero(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) { + return mem::os::realloc(old_ptr, new_count * info.size); +} + +void BootstrapAllocator::internal_deallocate(const mem::TypeInfo &info, void *ptr, size_t count) { + mem::os::free(ptr); +} + +void CAllocator::init(const char *name) { } + +void CAllocator::deinit() { } + +CAllocator *CAllocator::construct(mem::Allocator *allocator, const char *name) { + auto p = new(allocator->create()) CAllocator(); + p->init(name); + return p; +} + +void CAllocator::destruct(mem::Allocator *allocator) { + this->deinit(); + allocator->destroy(this); +} + +void *CAllocator::internal_allocate(const mem::TypeInfo &info, size_t count) { + return mem::os::calloc(count, info.size); +} + +void *CAllocator::internal_allocate_nonzero(const mem::TypeInfo &info, size_t count) { + return mem::os::malloc(count * info.size); +} + +void *CAllocator::internal_reallocate(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) { + auto new_ptr = this->internal_reallocate_nonzero(info, old_ptr, old_count, new_count); + if (new_count > old_count) + memset(reinterpret_cast(new_ptr) + (old_count * info.size), 0, (new_count - old_count) * info.size); + return new_ptr; +} + +void *CAllocator::internal_reallocate_nonzero(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) { + return mem::os::realloc(old_ptr, new_count * info.size); +} + +void CAllocator::internal_deallocate(const mem::TypeInfo &info, void *ptr, size_t count) { + mem::os::free(ptr); +} + +struct ArenaAllocator::Impl { + Allocator *backing; + + // regular allocations bump through a segment of static size + struct Segment { + static constexpr size_t size = 65536; + static constexpr size_t object_threshold = 4096; + + uint8_t data[size]; + }; + + // active segment + Segment *segment; + size_t segment_offset; + + // keep track of segments + struct SegmentTrack { + static constexpr size_t size = (4096 - sizeof(SegmentTrack *)) / sizeof(Segment *); + + // null if first + SegmentTrack *prev; + Segment *segments[size]; + }; + static_assert(sizeof(SegmentTrack) <= 4096, "unwanted struct padding"); + + // active segment track + SegmentTrack *segment_track; + size_t segment_track_remain; + + // individual allocations punted to backing allocator + struct Object { + uint8_t *ptr; + size_t len; + }; + + // keep track of objects + struct ObjectTrack { + static constexpr size_t size = (4096 - sizeof(ObjectTrack *)) / sizeof(Object); + + // null if first + ObjectTrack *prev; + Object objects[size]; + }; + static_assert(sizeof(ObjectTrack) <= 4096, "unwanted struct padding"); + + // active object track + ObjectTrack *object_track; + size_t object_track_remain; + + ATTRIBUTE_RETURNS_NOALIAS inline void *allocate(const mem::TypeInfo& info, size_t count); + inline void *reallocate(const mem::TypeInfo& info, void *old_ptr, size_t old_count, size_t new_count); + + inline void new_segment(); + inline void track_segment(); + inline void track_object(Object object); +}; + +void *ArenaAllocator::Impl::allocate(const mem::TypeInfo& info, size_t count) { +#ifndef NDEBUG + // make behavior when size == 0 portable + if (info.size == 0 || count == 0) + return nullptr; +#endif + const size_t nbytes = info.size * count; + this->segment_offset = (this->segment_offset + (info.alignment - 1)) & ~(info.alignment - 1); + if (nbytes >= Segment::object_threshold) { + auto ptr = this->backing->allocate(nbytes); + this->track_object({ptr, nbytes}); + return ptr; + } + if (this->segment_offset + nbytes > Segment::size) + this->new_segment(); + auto ptr = &this->segment->data[this->segment_offset]; + this->segment_offset += nbytes; + return ptr; +} + +void *ArenaAllocator::Impl::reallocate(const mem::TypeInfo& info, void *old_ptr, size_t old_count, size_t new_count) { +#ifndef NDEBUG + // make behavior when size == 0 portable + if (info.size == 0 && old_ptr == nullptr) + return nullptr; +#endif + const size_t new_nbytes = info.size * new_count; + if (new_nbytes <= info.size * old_count) + return old_ptr; + const size_t old_nbytes = info.size * old_count; + this->segment_offset = (this->segment_offset + (info.alignment - 1)) & ~(info.alignment - 1); + if (new_nbytes >= Segment::object_threshold) { + auto new_ptr = this->backing->allocate(new_nbytes); + this->track_object({new_ptr, new_nbytes}); + memcpy(new_ptr, old_ptr, old_nbytes); + return new_ptr; + } + if (this->segment_offset + new_nbytes > Segment::size) + this->new_segment(); + auto new_ptr = &this->segment->data[this->segment_offset]; + this->segment_offset += new_nbytes; + memcpy(new_ptr, old_ptr, old_nbytes); + return new_ptr; +} + +void ArenaAllocator::Impl::new_segment() { + this->segment = this->backing->create(); + this->segment_offset = 0; + this->track_segment(); +} + +void ArenaAllocator::Impl::track_segment() { + assert(this->segment != nullptr); + if (this->segment_track_remain < 1) { + auto prev = this->segment_track; + this->segment_track = this->backing->create(); + this->segment_track->prev = prev; + this->segment_track_remain = SegmentTrack::size; + } + this->segment_track_remain -= 1; + this->segment_track->segments[this->segment_track_remain] = this->segment; +} + +void ArenaAllocator::Impl::track_object(Object object) { + if (this->object_track_remain < 1) { + auto prev = this->object_track; + this->object_track = this->backing->create(); + this->object_track->prev = prev; + this->object_track_remain = ObjectTrack::size; + } + this->object_track_remain -= 1; + this->object_track->objects[this->object_track_remain] = object; +} + +void ArenaAllocator::init(Allocator *backing, const char *name) { + this->impl = bootstrap_allocator.create(); + { + auto &r = *this->impl; + r.backing = backing; + r.segment_offset = Impl::Segment::size; + } +} + +void ArenaAllocator::deinit() { + auto &backing = *this->impl->backing; + + // segments + if (this->impl->segment_track) { + // active track is not full and bounded by track_remain + auto prev = this->impl->segment_track->prev; + { + auto t = this->impl->segment_track; + for (size_t i = this->impl->segment_track_remain; i < Impl::SegmentTrack::size; ++i) + backing.destroy(t->segments[i]); + backing.destroy(t); + } + + // previous tracks are full + for (auto t = prev; t != nullptr;) { + for (size_t i = 0; i < Impl::SegmentTrack::size; ++i) + backing.destroy(t->segments[i]); + prev = t->prev; + backing.destroy(t); + t = prev; + } + } + + // objects + if (this->impl->object_track) { + // active track is not full and bounded by track_remain + auto prev = this->impl->object_track->prev; + { + auto t = this->impl->object_track; + for (size_t i = this->impl->object_track_remain; i < Impl::ObjectTrack::size; ++i) { + auto &obj = t->objects[i]; + backing.deallocate(obj.ptr, obj.len); + } + backing.destroy(t); + } + + // previous tracks are full + for (auto t = prev; t != nullptr;) { + for (size_t i = 0; i < Impl::ObjectTrack::size; ++i) { + auto &obj = t->objects[i]; + backing.deallocate(obj.ptr, obj.len); + } + prev = t->prev; + backing.destroy(t); + t = prev; + } + } +} + +ArenaAllocator *ArenaAllocator::construct(mem::Allocator *allocator, mem::Allocator *backing, const char *name) { + auto p = new(allocator->create()) ArenaAllocator; + p->init(backing, name); + return p; +} + +void ArenaAllocator::destruct(mem::Allocator *allocator) { + this->deinit(); + allocator->destroy(this); +} + +void *ArenaAllocator::internal_allocate(const mem::TypeInfo &info, size_t count) { + return this->impl->allocate(info, count); +} + +void *ArenaAllocator::internal_allocate_nonzero(const mem::TypeInfo &info, size_t count) { + return this->impl->allocate(info, count); +} + +void *ArenaAllocator::internal_reallocate(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) { + return this->internal_reallocate_nonzero(info, old_ptr, old_count, new_count); +} + +void *ArenaAllocator::internal_reallocate_nonzero(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) { + return this->impl->reallocate(info, old_ptr, old_count, new_count); +} + +void ArenaAllocator::internal_deallocate(const mem::TypeInfo &info, void *ptr, size_t count) { + // noop +} + +BootstrapAllocator bootstrap_allocator_state; +mem::Allocator &bootstrap_allocator = bootstrap_allocator_state; + +CAllocator c_allocator_state; +mem::Allocator &c_allocator = c_allocator_state; + +} // namespace heap diff --git a/src/stage1/heap.hpp b/src/stage1/heap.hpp new file mode 100644 index 0000000000000000000000000000000000000000..ec5c81026d167d31e71bbb3750f8b90bb1438a42 --- /dev/null +++ b/src/stage1/heap.hpp @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2020 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef ZIG_HEAP_HPP +#define ZIG_HEAP_HPP + +#include "config.h" +#include "util_base.hpp" +#include "mem.hpp" + +namespace heap { + +struct BootstrapAllocator final : mem::Allocator { + void init(const char *name); + void deinit(); + void destruct(Allocator *allocator) {} + +private: + ATTRIBUTE_RETURNS_NOALIAS void *internal_allocate(const mem::TypeInfo &info, size_t count) final; + ATTRIBUTE_RETURNS_NOALIAS void *internal_allocate_nonzero(const mem::TypeInfo &info, size_t count) final; + void *internal_reallocate(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) final; + void *internal_reallocate_nonzero(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) final; + void internal_deallocate(const mem::TypeInfo &info, void *ptr, size_t count) final; +}; + +struct CAllocator final : mem::Allocator { + void init(const char *name); + void deinit(); + + static CAllocator *construct(mem::Allocator *allocator, const char *name); + void destruct(mem::Allocator *allocator) final; + + +private: + ATTRIBUTE_RETURNS_NOALIAS void *internal_allocate(const mem::TypeInfo &info, size_t count) final; + ATTRIBUTE_RETURNS_NOALIAS void *internal_allocate_nonzero(const mem::TypeInfo &info, size_t count) final; + void *internal_reallocate(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) final; + void *internal_reallocate_nonzero(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) final; + void internal_deallocate(const mem::TypeInfo &info, void *ptr, size_t count) final; + +}; + +// +// arena allocator +// +// - allocations are backed by the underlying allocator's memory +// - allocations are N:1 relationship to underlying allocations +// - dellocations are noops +// - deinit() releases all underlying memory +// +struct ArenaAllocator final : mem::Allocator { + void init(Allocator *backing, const char *name); + void deinit(); + + static ArenaAllocator *construct(mem::Allocator *allocator, mem::Allocator *backing, const char *name); + void destruct(mem::Allocator *allocator) final; + + +private: + ATTRIBUTE_RETURNS_NOALIAS void *internal_allocate(const mem::TypeInfo &info, size_t count) final; + ATTRIBUTE_RETURNS_NOALIAS void *internal_allocate_nonzero(const mem::TypeInfo &info, size_t count) final; + void *internal_reallocate(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) final; + void *internal_reallocate_nonzero(const mem::TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) final; + void internal_deallocate(const mem::TypeInfo &info, void *ptr, size_t count) final; + + struct Impl; + Impl *impl; +}; + +extern BootstrapAllocator bootstrap_allocator_state; +extern mem::Allocator &bootstrap_allocator; + +extern CAllocator c_allocator_state; +extern mem::Allocator &c_allocator; + +} // namespace heap + +#endif diff --git a/src/stage1/ir.cpp b/src/stage1/ir.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bb4ca8dbf3a5a5f383ab50b3c439ee86742d8d0d --- /dev/null +++ b/src/stage1/ir.cpp @@ -0,0 +1,32590 @@ +/* + * 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 "error.hpp" +#include "ir.hpp" +#include "ir_print.hpp" +#include "os.hpp" +#include "range_set.hpp" +#include "softfloat.hpp" +#include "softfloat_ext.hpp" +#include "util.hpp" +#include "mem_list.hpp" +#include "all_types.hpp" + +#include + +struct IrBuilderSrc { + CodeGen *codegen; + IrExecutableSrc *exec; + IrBasicBlockSrc *current_basic_block; + AstNode *main_block_node; +}; + +struct IrBuilderGen { + CodeGen *codegen; + IrExecutableGen *exec; + IrBasicBlockGen *current_basic_block; + + // track for immediate post-analysis destruction + mem::List constants; +}; + +struct IrAnalyze { + CodeGen *codegen; + IrBuilderSrc old_irb; + IrBuilderGen new_irb; + size_t old_bb_index; + size_t instruction_index; + ZigType *explicit_return_type; + AstNode *explicit_return_type_source_node; + ZigList src_implicit_return_type_list; + ZigList resume_stack; + IrBasicBlockSrc *const_predecessor_bb; + size_t ref_count; + size_t break_debug_id; // for debugging purposes + IrInstGen *return_ptr; + + // For the purpose of using in a debugger + void dump(); +}; + +enum ConstCastResultId { + ConstCastResultIdOk, + ConstCastResultIdInvalid, + ConstCastResultIdErrSet, + ConstCastResultIdErrSetGlobal, + ConstCastResultIdPointerChild, + ConstCastResultIdSliceChild, + ConstCastResultIdOptionalChild, + ConstCastResultIdOptionalShape, + ConstCastResultIdErrorUnionPayload, + ConstCastResultIdErrorUnionErrorSet, + ConstCastResultIdFnAlign, + ConstCastResultIdFnCC, + ConstCastResultIdFnVarArgs, + ConstCastResultIdFnIsGeneric, + ConstCastResultIdFnReturnType, + ConstCastResultIdFnArgCount, + ConstCastResultIdFnGenericArgCount, + ConstCastResultIdFnArg, + ConstCastResultIdFnArgNoAlias, + ConstCastResultIdType, + ConstCastResultIdUnresolvedInferredErrSet, + ConstCastResultIdAsyncAllocatorType, + ConstCastResultIdBadAllowsZero, + ConstCastResultIdArrayChild, + ConstCastResultIdSentinelArrays, + ConstCastResultIdPtrLens, + ConstCastResultIdCV, + ConstCastResultIdPtrSentinel, + ConstCastResultIdIntShorten, +}; + +struct ConstCastOnly; +struct ConstCastArg { + size_t arg_index; + ZigType *actual_param_type; + ZigType *expected_param_type; + ConstCastOnly *child; +}; + +struct ConstCastArgNoAlias { + size_t arg_index; +}; + +struct ConstCastOptionalMismatch; +struct ConstCastPointerMismatch; +struct ConstCastSliceMismatch; +struct ConstCastErrUnionErrSetMismatch; +struct ConstCastErrUnionPayloadMismatch; +struct ConstCastErrSetMismatch; +struct ConstCastTypeMismatch; +struct ConstCastArrayMismatch; +struct ConstCastBadAllowsZero; +struct ConstCastBadNullTermArrays; +struct ConstCastBadCV; +struct ConstCastPtrSentinel; +struct ConstCastIntShorten; + +struct ConstCastOnly { + ConstCastResultId id; + union { + ConstCastErrSetMismatch *error_set_mismatch; + ConstCastPointerMismatch *pointer_mismatch; + ConstCastSliceMismatch *slice_mismatch; + ConstCastOptionalMismatch *optional; + ConstCastErrUnionPayloadMismatch *error_union_payload; + ConstCastErrUnionErrSetMismatch *error_union_error_set; + ConstCastTypeMismatch *type_mismatch; + ConstCastArrayMismatch *array_mismatch; + ConstCastOnly *return_type; + ConstCastOnly *null_wrap_ptr_child; + ConstCastArg fn_arg; + ConstCastArgNoAlias arg_no_alias; + ConstCastBadAllowsZero *bad_allows_zero; + ConstCastBadNullTermArrays *sentinel_arrays; + ConstCastBadCV *bad_cv; + ConstCastPtrSentinel *bad_ptr_sentinel; + ConstCastIntShorten *int_shorten; + } data; +}; + +struct ConstCastTypeMismatch { + ZigType *wanted_type; + ZigType *actual_type; +}; + +struct ConstCastOptionalMismatch { + ConstCastOnly child; + ZigType *wanted_child; + ZigType *actual_child; +}; + +struct ConstCastPointerMismatch { + ConstCastOnly child; + ZigType *wanted_child; + ZigType *actual_child; +}; + +struct ConstCastSliceMismatch { + ConstCastOnly child; + ZigType *wanted_child; + ZigType *actual_child; +}; + +struct ConstCastArrayMismatch { + ConstCastOnly child; + ZigType *wanted_child; + ZigType *actual_child; +}; + +struct ConstCastErrUnionErrSetMismatch { + ConstCastOnly child; + ZigType *wanted_err_set; + ZigType *actual_err_set; +}; + +struct ConstCastErrUnionPayloadMismatch { + ConstCastOnly child; + ZigType *wanted_payload; + ZigType *actual_payload; +}; + +struct ConstCastErrSetMismatch { + ZigList missing_errors; +}; + +struct ConstCastBadAllowsZero { + ZigType *wanted_type; + ZigType *actual_type; +}; + +struct ConstCastBadNullTermArrays { + ConstCastOnly child; + ZigType *wanted_type; + ZigType *actual_type; +}; + +struct ConstCastBadCV { + ZigType *wanted_type; + ZigType *actual_type; +}; + +struct ConstCastPtrSentinel { + ZigType *wanted_type; + ZigType *actual_type; +}; + +struct ConstCastIntShorten { + ZigType *wanted_type; + ZigType *actual_type; +}; + +// for debugging purposes +struct DbgIrBreakPoint { + const char *src_file; + uint32_t line; +}; +DbgIrBreakPoint dbg_ir_breakpoints_buf[20]; +size_t dbg_ir_breakpoints_count = 0; + +static IrInstSrc *ir_gen_node(IrBuilderSrc *irb, AstNode *node, Scope *scope); +static IrInstSrc *ir_gen_node_extra(IrBuilderSrc *irb, AstNode *node, Scope *scope, LVal lval, + ResultLoc *result_loc); +static IrInstGen *ir_implicit_cast(IrAnalyze *ira, IrInstGen *value, ZigType *expected_type); +static IrInstGen *ir_implicit_cast2(IrAnalyze *ira, IrInst *value_source_instr, + IrInstGen *value, ZigType *expected_type); +static IrInstGen *ir_get_deref(IrAnalyze *ira, IrInst *source_instr, IrInstGen *ptr, + ResultLoc *result_loc); +static ErrorMsg *exec_add_error_node(CodeGen *codegen, IrExecutableSrc *exec, AstNode *source_node, Buf *msg); +static IrInstGen *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_name, + IrInst* source_instr, IrInstGen *container_ptr, IrInst *container_ptr_src, + ZigType *container_type, bool initializing); +static void ir_assert_impl(bool ok, IrInst* source_instruction, const char *file, unsigned int line); +static void ir_assert_gen_impl(bool ok, IrInstGen *source_instruction, const char *file, unsigned int line); +static IrInstGen *ir_get_var_ptr(IrAnalyze *ira, IrInst *source_instr, ZigVar *var); +static ZigType *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstGen *op); +static IrInstSrc *ir_lval_wrap(IrBuilderSrc *irb, Scope *scope, IrInstSrc *value, LVal lval, ResultLoc *result_loc); +static IrInstSrc *ir_expr_wrap(IrBuilderSrc *irb, Scope *scope, IrInstSrc *inst, ResultLoc *result_loc); +static ZigType *adjust_ptr_align(CodeGen *g, ZigType *ptr_type, uint32_t new_align); +static ZigType *adjust_ptr_const(CodeGen *g, ZigType *ptr_type, bool is_const); +static ZigType *adjust_slice_align(CodeGen *g, ZigType *slice_type, uint32_t new_align); +static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, uint8_t *buf, ZigValue *val); +static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ZigValue *val); +static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, + ZigValue *out_val, ZigValue *ptr_val); +static IrInstGen *ir_analyze_ptr_cast(IrAnalyze *ira, IrInst* source_instr, IrInstGen *ptr, + IrInst *ptr_src, ZigType *dest_type, IrInst *dest_type_src, bool safety_check_on, + bool keep_bigger_alignment); +static ZigValue *ir_resolve_const(IrAnalyze *ira, IrInstGen *value, UndefAllowed undef_allowed); +static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align); +static IrInstGen *ir_analyze_int_to_ptr(IrAnalyze *ira, IrInst* source_instr, IrInstGen *target, + ZigType *ptr_type); +static IrInstGen *ir_analyze_bit_cast(IrAnalyze *ira, IrInst* source_instr, IrInstGen *value, + ZigType *dest_type); +static IrInstGen *ir_resolve_result_raw(IrAnalyze *ira, IrInst *suspend_source_instr, + ResultLoc *result_loc, ZigType *value_type, IrInstGen *value, bool force_runtime, bool allow_discard); +static IrInstGen *ir_resolve_result(IrAnalyze *ira, IrInst *suspend_source_instr, + ResultLoc *result_loc, ZigType *value_type, IrInstGen *value, bool force_runtime, bool allow_discard); +static IrInstGen *ir_analyze_unwrap_optional_payload(IrAnalyze *ira, IrInst* source_instr, + IrInstGen *base_ptr, bool safety_check_on, bool initializing); +static IrInstGen *ir_analyze_unwrap_error_payload(IrAnalyze *ira, IrInst* source_instr, + IrInstGen *base_ptr, bool safety_check_on, bool initializing); +static IrInstGen *ir_analyze_unwrap_err_code(IrAnalyze *ira, IrInst* source_instr, + IrInstGen *base_ptr, bool initializing); +static IrInstGen *ir_analyze_store_ptr(IrAnalyze *ira, IrInst* source_instr, + IrInstGen *ptr, IrInstGen *uncasted_value, bool allow_write_through_const); +static IrInstSrc *ir_gen_union_init_expr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *union_type, IrInstSrc *field_name, AstNode *expr_node, + LVal lval, ResultLoc *parent_result_loc); +static void ir_reset_result(ResultLoc *result_loc); +static Buf *get_anon_type_name(CodeGen *codegen, IrExecutableSrc *exec, const char *kind_name, + Scope *scope, AstNode *source_node, Buf *out_bare_name); +static ResultLocCast *ir_build_cast_result_loc(IrBuilderSrc *irb, IrInstSrc *dest_type, + ResultLoc *parent_result_loc); +static IrInstGen *ir_analyze_struct_field_ptr(IrAnalyze *ira, IrInst* source_instr, + TypeStructField *field, IrInstGen *struct_ptr, ZigType *struct_type, bool initializing); +static IrInstGen *ir_analyze_inferred_field_ptr(IrAnalyze *ira, Buf *field_name, + IrInst* source_instr, IrInstGen *container_ptr, ZigType *container_type); +static ResultLoc *no_result_loc(void); +static IrInstGen *ir_analyze_test_non_null(IrAnalyze *ira, IrInst *source_inst, IrInstGen *value); +static IrInstGen *ir_error_dependency_loop(IrAnalyze *ira, IrInst *source_instr); +static IrInstGen *ir_const_undef(IrAnalyze *ira, IrInst *source_instruction, ZigType *ty); +static ZigVar *ir_create_var(IrBuilderSrc *irb, AstNode *node, Scope *scope, Buf *name, + bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstSrc *is_comptime); +static void build_decl_var_and_init(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, ZigVar *var, + IrInstSrc *init, const char *name_hint, IrInstSrc *is_comptime); +static IrInstGen *ir_analyze_union_init(IrAnalyze *ira, IrInst* source_instruction, + AstNode *field_source_node, ZigType *union_type, Buf *field_name, IrInstGen *field_result_loc, + IrInstGen *result_loc); +static IrInstGen *ir_analyze_struct_value_field_value(IrAnalyze *ira, IrInst* source_instr, + IrInstGen *struct_operand, TypeStructField *field); +static bool value_cmp_numeric_val_any(ZigValue *left, Cmp predicate, ZigValue *right); +static bool value_cmp_numeric_val_all(ZigValue *left, Cmp predicate, ZigValue *right); +static void memoize_field_init_val(CodeGen *codegen, ZigType *container_type, TypeStructField *field); +static void value_to_bigfloat(BigFloat *out, ZigValue *val); + +#define ir_assert(OK, SOURCE_INSTRUCTION) ir_assert_impl((OK), (SOURCE_INSTRUCTION), __FILE__, __LINE__) +#define ir_assert_gen(OK, SOURCE_INSTRUCTION) ir_assert_gen_impl((OK), (SOURCE_INSTRUCTION), __FILE__, __LINE__) + +static void destroy_instruction_src(IrInstSrc *inst) { + switch (inst->id) { + case IrInstSrcIdInvalid: + zig_unreachable(); + case IrInstSrcIdReturn: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdConst: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdBinOp: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdMergeErrSets: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdDeclVar: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdCall: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdCallExtra: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdAsyncCallExtra: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdUnOp: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdCondBr: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdBr: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdPhi: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdContainerInitList: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdContainerInitFields: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdUnreachable: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdElemPtr: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdVarPtr: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdLoadPtr: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdStorePtr: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdTypeOf: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdFieldPtr: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdSetCold: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdSetRuntimeSafety: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdSetFloatMode: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdArrayType: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdSliceType: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdAnyFrameType: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdAsm: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdSizeOf: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdTestNonNull: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdOptionalUnwrapPtr: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdPopCount: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdClz: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdCtz: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdBswap: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdBitReverse: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdSwitchBr: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdSwitchVar: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdSwitchElseVar: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdSwitchTarget: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdImport: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdRef: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdCompileErr: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdCompileLog: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdErrName: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdCImport: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdCInclude: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdCDefine: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdCUndef: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdEmbedFile: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdCmpxchg: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdFence: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdTruncate: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdIntCast: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdFloatCast: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdErrSetCast: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdIntToFloat: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdFloatToInt: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdBoolToInt: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdVectorType: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdShuffleVector: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdSplat: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdBoolNot: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdMemset: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdMemcpy: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdSlice: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdBreakpoint: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdReturnAddress: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdFrameAddress: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdFrameHandle: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdFrameType: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdFrameSize: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdAlignOf: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdOverflowOp: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdTestErr: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdUnwrapErrCode: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdUnwrapErrPayload: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdFnProto: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdTestComptime: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdPtrCast: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdBitCast: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdPtrToInt: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdIntToPtr: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdIntToEnum: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdIntToErr: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdErrToInt: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdCheckSwitchProngs: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdCheckStatementIsVoid: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdTypeName: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdTagName: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdPtrType: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdDeclRef: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdPanic: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdFieldParentPtr: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdByteOffsetOf: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdBitOffsetOf: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdTypeInfo: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdType: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdHasField: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdSetEvalBranchQuota: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdAlignCast: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdImplicitCast: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdResolveResult: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdResetResult: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdSetAlignStack: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdArgType: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdTagType: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdExport: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdErrorReturnTrace: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdErrorUnion: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdAtomicRmw: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdSaveErrRetAddr: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdAddImplicitReturnType: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdFloatOp: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdMulAdd: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdAtomicLoad: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdAtomicStore: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdEnumToInt: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdCheckRuntimeScope: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdHasDecl: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdUndeclaredIdent: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdAlloca: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdEndExpr: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdUnionInitNamedField: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdSuspendBegin: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdSuspendFinish: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdResume: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdAwait: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdSpillBegin: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdSpillEnd: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdCallArgs: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdWasmMemorySize: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdWasmMemoryGrow: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstSrcIdSrc: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + } + zig_unreachable(); +} + +void destroy_instruction_gen(IrInstGen *inst) { + switch (inst->id) { + case IrInstGenIdInvalid: + zig_unreachable(); + case IrInstGenIdReturn: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdConst: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdBinOp: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdCast: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdCall: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdCondBr: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdBr: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdPhi: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdUnreachable: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdElemPtr: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdVarPtr: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdReturnPtr: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdLoadPtr: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdStorePtr: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdVectorStoreElem: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdStructFieldPtr: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdUnionFieldPtr: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdAsm: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdTestNonNull: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdOptionalUnwrapPtr: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdPopCount: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdClz: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdCtz: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdBswap: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdBitReverse: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdSwitchBr: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdUnionTag: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdRef: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdErrName: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdCmpxchg: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdFence: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdTruncate: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdShuffleVector: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdSplat: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdBoolNot: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdMemset: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdMemcpy: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdSlice: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdBreakpoint: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdReturnAddress: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdFrameAddress: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdFrameHandle: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdFrameSize: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdOverflowOp: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdTestErr: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdUnwrapErrCode: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdUnwrapErrPayload: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdOptionalWrap: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdErrWrapCode: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdErrWrapPayload: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdPtrCast: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdBitCast: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdWidenOrShorten: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdPtrToInt: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdIntToPtr: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdIntToEnum: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdIntToErr: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdErrToInt: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdTagName: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdPanic: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdFieldParentPtr: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdAlignCast: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdErrorReturnTrace: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdAtomicRmw: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdSaveErrRetAddr: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdFloatOp: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdMulAdd: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdAtomicLoad: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdAtomicStore: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdDeclVar: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdArrayToVector: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdVectorToArray: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdPtrOfArrayToSlice: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdAssertZero: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdAssertNonNull: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdAlloca: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdSuspendBegin: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdSuspendFinish: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdResume: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdAwait: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdSpillBegin: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdSpillEnd: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdVectorExtractElem: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdBinaryNot: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdNegation: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdNegationWrapping: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdWasmMemorySize: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + case IrInstGenIdWasmMemoryGrow: + return heap::c_allocator.destroy(reinterpret_cast(inst)); + } + zig_unreachable(); +} + +static void ira_ref(IrAnalyze *ira) { + ira->ref_count += 1; +} +static void ira_deref(IrAnalyze *ira) { + if (ira->ref_count > 1) { + ira->ref_count -= 1; + + // immediate destruction of dangling IrInstGenConst is not possible + // free tracking memory because it will never be used + ira->new_irb.constants.deinit(&heap::c_allocator); + return; + } + assert(ira->ref_count != 0); + + for (size_t bb_i = 0; bb_i < ira->old_irb.exec->basic_block_list.length; bb_i += 1) { + IrBasicBlockSrc *pass1_bb = ira->old_irb.exec->basic_block_list.items[bb_i]; + for (size_t inst_i = 0; inst_i < pass1_bb->instruction_list.length; inst_i += 1) { + IrInstSrc *pass1_inst = pass1_bb->instruction_list.items[inst_i]; + destroy_instruction_src(pass1_inst); + } + heap::c_allocator.destroy(pass1_bb); + } + ira->old_irb.exec->basic_block_list.deinit(); + ira->old_irb.exec->tld_list.deinit(); + heap::c_allocator.destroy(ira->old_irb.exec); + ira->src_implicit_return_type_list.deinit(); + ira->resume_stack.deinit(); + + // destroy dangling IrInstGenConst + for (size_t i = 0; i < ira->new_irb.constants.length; i += 1) { + auto constant = ira->new_irb.constants.items[i]; + if (constant->base.base.ref_count == 0 && !ir_inst_gen_has_side_effects(&constant->base)) + destroy_instruction_gen(&constant->base); + } + ira->new_irb.constants.deinit(&heap::c_allocator); + + heap::c_allocator.destroy(ira); +} + +static ZigValue *const_ptr_pointee_unchecked_no_isf(CodeGen *g, ZigValue *const_val) { + assert(get_src_ptr_type(const_val->type) != nullptr); + assert(const_val->special == ConstValSpecialStatic); + + switch (type_has_one_possible_value(g, const_val->type->data.pointer.child_type)) { + case OnePossibleValueInvalid: + return nullptr; + case OnePossibleValueYes: + return get_the_one_possible_value(g, const_val->type->data.pointer.child_type); + case OnePossibleValueNo: + break; + } + + ZigValue *result; + switch (const_val->data.x_ptr.special) { + case ConstPtrSpecialInvalid: + zig_unreachable(); + case ConstPtrSpecialRef: + result = const_val->data.x_ptr.data.ref.pointee; + break; + case ConstPtrSpecialBaseArray: { + ZigValue *array_val = const_val->data.x_ptr.data.base_array.array_val; + size_t elem_index = const_val->data.x_ptr.data.base_array.elem_index; + if (elem_index == array_val->type->data.array.len) { + result = array_val->type->data.array.sentinel; + } else { + expand_undef_array(g, array_val); + result = &array_val->data.x_array.data.s_none.elements[elem_index]; + } + break; + } + case ConstPtrSpecialSubArray: { + ZigValue *array_val = const_val->data.x_ptr.data.base_array.array_val; + size_t elem_index = const_val->data.x_ptr.data.base_array.elem_index; + + expand_undef_array(g, array_val); + result = g->pass1_arena->create(); + result->special = array_val->special; + result->type = get_array_type(g, array_val->type->data.array.child_type, + array_val->type->data.array.len - elem_index, array_val->type->data.array.sentinel); + result->data.x_array.special = ConstArraySpecialNone; + result->data.x_array.data.s_none.elements = &array_val->data.x_array.data.s_none.elements[elem_index]; + result->parent.id = ConstParentIdArray; + result->parent.data.p_array.array_val = array_val; + result->parent.data.p_array.elem_index = elem_index; + break; + } + case ConstPtrSpecialBaseStruct: { + ZigValue *struct_val = const_val->data.x_ptr.data.base_struct.struct_val; + expand_undef_struct(g, struct_val); + result = struct_val->data.x_struct.fields[const_val->data.x_ptr.data.base_struct.field_index]; + break; + } + case ConstPtrSpecialBaseErrorUnionCode: + result = const_val->data.x_ptr.data.base_err_union_code.err_union_val->data.x_err_union.error_set; + break; + case ConstPtrSpecialBaseErrorUnionPayload: + result = const_val->data.x_ptr.data.base_err_union_payload.err_union_val->data.x_err_union.payload; + break; + case ConstPtrSpecialBaseOptionalPayload: + result = const_val->data.x_ptr.data.base_optional_payload.optional_val->data.x_optional; + break; + case ConstPtrSpecialNull: + result = const_val; + break; + case ConstPtrSpecialHardCodedAddr: + zig_unreachable(); + case ConstPtrSpecialDiscard: + zig_unreachable(); + case ConstPtrSpecialFunction: + zig_unreachable(); + } + assert(result != nullptr); + return result; +} + +static ZigValue *const_ptr_pointee_unchecked(CodeGen *g, ZigValue *const_val) { + assert(get_src_ptr_type(const_val->type) != nullptr); + assert(const_val->special == ConstValSpecialStatic); + + InferredStructField *isf = const_val->type->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) { + assert(field->init_val != nullptr); + return field->init_val; + } + ZigValue *struct_val = const_ptr_pointee_unchecked_no_isf(g, const_val); + assert(struct_val->type->id == ZigTypeIdStruct); + return struct_val->data.x_struct.fields[field->src_index]; + } + + return const_ptr_pointee_unchecked_no_isf(g, const_val); +} + +static bool is_tuple(ZigType *type) { + return type->id == ZigTypeIdStruct && type->data.structure.special == StructSpecialInferredTuple; +} + +static bool is_slice(ZigType *type) { + return type->id == ZigTypeIdStruct && type->data.structure.special == StructSpecialSlice; +} + +// This function returns true when you can change the type of a ZigValue and the +// value remains meaningful. +static bool types_have_same_zig_comptime_repr(CodeGen *codegen, ZigType *expected, ZigType *actual) { + if (expected == actual) + return true; + + if (get_src_ptr_type(expected) != nullptr && get_src_ptr_type(actual) != nullptr) + return true; + + if (is_opt_err_set(expected) && is_opt_err_set(actual)) + return true; + + if (expected->id != actual->id) + return false; + + switch (expected->id) { + case ZigTypeIdInvalid: + case ZigTypeIdUnreachable: + zig_unreachable(); + case ZigTypeIdMetaType: + case ZigTypeIdVoid: + case ZigTypeIdBool: + case ZigTypeIdComptimeFloat: + case ZigTypeIdComptimeInt: + case ZigTypeIdEnumLiteral: + case ZigTypeIdUndefined: + case ZigTypeIdNull: + case ZigTypeIdBoundFn: + case ZigTypeIdErrorSet: + case ZigTypeIdOpaque: + case ZigTypeIdAnyFrame: + case ZigTypeIdFn: + return true; + case ZigTypeIdPointer: + return expected->data.pointer.inferred_struct_field == actual->data.pointer.inferred_struct_field; + case ZigTypeIdFloat: + return expected->data.floating.bit_count == actual->data.floating.bit_count; + case ZigTypeIdInt: + return expected->data.integral.is_signed == actual->data.integral.is_signed; + case ZigTypeIdStruct: + return is_slice(expected) && is_slice(actual); + case ZigTypeIdOptional: + case ZigTypeIdErrorUnion: + case ZigTypeIdEnum: + case ZigTypeIdUnion: + case ZigTypeIdVector: + case ZigTypeIdFnFrame: + return false; + case ZigTypeIdArray: + return expected->data.array.len == actual->data.array.len && + expected->data.array.child_type == actual->data.array.child_type && + (expected->data.array.sentinel == nullptr || (actual->data.array.sentinel != nullptr && + const_values_equal(codegen, expected->data.array.sentinel, actual->data.array.sentinel))); + } + zig_unreachable(); +} + +static bool ir_should_inline(IrExecutableSrc *exec, Scope *scope) { + if (exec->is_inline) + return true; + + while (scope != nullptr) { + if (scope->id == ScopeIdCompTime) + return true; + if (scope->id == ScopeIdTypeOf) + return false; + if (scope->id == ScopeIdFnDef) + break; + scope = scope->parent; + } + return false; +} + +static void ir_instruction_append(IrBasicBlockSrc *basic_block, IrInstSrc *instruction) { + assert(basic_block); + assert(instruction); + basic_block->instruction_list.append(instruction); +} + +static void ir_inst_gen_append(IrBasicBlockGen *basic_block, IrInstGen *instruction) { + assert(basic_block); + assert(instruction); + basic_block->instruction_list.append(instruction); +} + +static size_t exec_next_debug_id(IrExecutableSrc *exec) { + size_t result = exec->next_debug_id; + exec->next_debug_id += 1; + return result; +} + +static size_t exec_next_debug_id_gen(IrExecutableGen *exec) { + size_t result = exec->next_debug_id; + exec->next_debug_id += 1; + return result; +} + +static ZigFn *exec_fn_entry(IrExecutableSrc *exec) { + return exec->fn_entry; +} + +static Buf *exec_c_import_buf(IrExecutableSrc *exec) { + return exec->c_import_buf; +} + +static bool value_is_comptime(ZigValue *const_val) { + return const_val->special != ConstValSpecialRuntime; +} + +static bool instr_is_comptime(IrInstGen *instruction) { + return value_is_comptime(instruction->value); +} + +static bool instr_is_unreachable(IrInstSrc *instruction) { + return instruction->is_noreturn; +} + +static void ir_ref_bb(IrBasicBlockSrc *bb) { + bb->ref_count += 1; +} + +static void ir_ref_instruction(IrInstSrc *instruction, IrBasicBlockSrc *cur_bb) { + assert(instruction->id != IrInstSrcIdInvalid); + instruction->base.ref_count += 1; + if (instruction->owner_bb != cur_bb && !instr_is_unreachable(instruction) + && instruction->id != IrInstSrcIdConst) + { + ir_ref_bb(instruction->owner_bb); + } +} + +static void ir_ref_inst_gen(IrInstGen *instruction) { + assert(instruction->id != IrInstGenIdInvalid); + instruction->base.ref_count += 1; +} + +static void ir_ref_var(ZigVar *var) { + var->ref_count += 1; +} + +static void create_result_ptr(CodeGen *codegen, ZigType *expected_type, + ZigValue **out_result, ZigValue **out_result_ptr) +{ + ZigValue *result = codegen->pass1_arena->create(); + ZigValue *result_ptr = codegen->pass1_arena->create(); + result->special = ConstValSpecialUndef; + result->type = expected_type; + result_ptr->special = ConstValSpecialStatic; + result_ptr->type = get_pointer_to_type(codegen, 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; + + *out_result = result; + *out_result_ptr = result_ptr; +} + +ZigType *ir_analyze_type_expr(IrAnalyze *ira, Scope *scope, AstNode *node) { + Error err; + + ZigValue *result; + ZigValue *result_ptr; + create_result_ptr(ira->codegen, ira->codegen->builtin_types.entry_type, &result, &result_ptr); + + if ((err = ir_eval_const_value(ira->codegen, scope, node, result_ptr, + ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, + nullptr, nullptr, node, nullptr, ira->new_irb.exec, nullptr, UndefBad))) + { + return ira->codegen->builtin_types.entry_invalid; + } + if (type_is_invalid(result->type)) + return ira->codegen->builtin_types.entry_invalid; + + assert(result->special != ConstValSpecialRuntime); + ZigType *res_type = result->data.x_type; + + return res_type; +} + +static IrBasicBlockSrc *ir_create_basic_block(IrBuilderSrc *irb, Scope *scope, const char *name_hint) { + IrBasicBlockSrc *result = heap::c_allocator.create(); + result->scope = scope; + result->name_hint = name_hint; + result->debug_id = exec_next_debug_id(irb->exec); + result->index = UINT32_MAX; // set later + return result; +} + +static IrBasicBlockGen *ir_create_basic_block_gen(IrAnalyze *ira, Scope *scope, const char *name_hint) { + IrBasicBlockGen *result = heap::c_allocator.create(); + result->scope = scope; + result->name_hint = name_hint; + result->debug_id = exec_next_debug_id_gen(ira->new_irb.exec); + return result; +} + +static IrBasicBlockGen *ir_build_bb_from(IrAnalyze *ira, IrBasicBlockSrc *other_bb) { + IrBasicBlockGen *new_bb = ir_create_basic_block_gen(ira, other_bb->scope, other_bb->name_hint); + other_bb->child = new_bb; + return new_bb; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcDeclVar *) { + return IrInstSrcIdDeclVar; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcBr *) { + return IrInstSrcIdBr; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcCondBr *) { + return IrInstSrcIdCondBr; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcSwitchBr *) { + return IrInstSrcIdSwitchBr; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcSwitchVar *) { + return IrInstSrcIdSwitchVar; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcSwitchElseVar *) { + return IrInstSrcIdSwitchElseVar; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcSwitchTarget *) { + return IrInstSrcIdSwitchTarget; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcPhi *) { + return IrInstSrcIdPhi; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcUnOp *) { + return IrInstSrcIdUnOp; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcBinOp *) { + return IrInstSrcIdBinOp; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcMergeErrSets *) { + return IrInstSrcIdMergeErrSets; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcLoadPtr *) { + return IrInstSrcIdLoadPtr; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcStorePtr *) { + return IrInstSrcIdStorePtr; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcFieldPtr *) { + return IrInstSrcIdFieldPtr; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcElemPtr *) { + return IrInstSrcIdElemPtr; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcVarPtr *) { + return IrInstSrcIdVarPtr; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcCall *) { + return IrInstSrcIdCall; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcCallArgs *) { + return IrInstSrcIdCallArgs; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcCallExtra *) { + return IrInstSrcIdCallExtra; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcAsyncCallExtra *) { + return IrInstSrcIdAsyncCallExtra; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcConst *) { + return IrInstSrcIdConst; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcReturn *) { + return IrInstSrcIdReturn; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcContainerInitList *) { + return IrInstSrcIdContainerInitList; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcContainerInitFields *) { + return IrInstSrcIdContainerInitFields; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcUnreachable *) { + return IrInstSrcIdUnreachable; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcTypeOf *) { + return IrInstSrcIdTypeOf; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcSetCold *) { + return IrInstSrcIdSetCold; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcSetRuntimeSafety *) { + return IrInstSrcIdSetRuntimeSafety; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcSetFloatMode *) { + return IrInstSrcIdSetFloatMode; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcArrayType *) { + return IrInstSrcIdArrayType; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcAnyFrameType *) { + return IrInstSrcIdAnyFrameType; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcSliceType *) { + return IrInstSrcIdSliceType; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcAsm *) { + return IrInstSrcIdAsm; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcSizeOf *) { + return IrInstSrcIdSizeOf; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcTestNonNull *) { + return IrInstSrcIdTestNonNull; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcOptionalUnwrapPtr *) { + return IrInstSrcIdOptionalUnwrapPtr; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcClz *) { + return IrInstSrcIdClz; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcCtz *) { + return IrInstSrcIdCtz; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcPopCount *) { + return IrInstSrcIdPopCount; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcBswap *) { + return IrInstSrcIdBswap; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcBitReverse *) { + return IrInstSrcIdBitReverse; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcImport *) { + return IrInstSrcIdImport; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcCImport *) { + return IrInstSrcIdCImport; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcCInclude *) { + return IrInstSrcIdCInclude; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcCDefine *) { + return IrInstSrcIdCDefine; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcCUndef *) { + return IrInstSrcIdCUndef; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcRef *) { + return IrInstSrcIdRef; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcCompileErr *) { + return IrInstSrcIdCompileErr; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcCompileLog *) { + return IrInstSrcIdCompileLog; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcErrName *) { + return IrInstSrcIdErrName; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcEmbedFile *) { + return IrInstSrcIdEmbedFile; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcCmpxchg *) { + return IrInstSrcIdCmpxchg; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcFence *) { + return IrInstSrcIdFence; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcTruncate *) { + return IrInstSrcIdTruncate; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcIntCast *) { + return IrInstSrcIdIntCast; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcFloatCast *) { + return IrInstSrcIdFloatCast; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcIntToFloat *) { + return IrInstSrcIdIntToFloat; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcFloatToInt *) { + return IrInstSrcIdFloatToInt; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcBoolToInt *) { + return IrInstSrcIdBoolToInt; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcVectorType *) { + return IrInstSrcIdVectorType; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcShuffleVector *) { + return IrInstSrcIdShuffleVector; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcSplat *) { + return IrInstSrcIdSplat; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcBoolNot *) { + return IrInstSrcIdBoolNot; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcMemset *) { + return IrInstSrcIdMemset; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcMemcpy *) { + return IrInstSrcIdMemcpy; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcSlice *) { + return IrInstSrcIdSlice; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcBreakpoint *) { + return IrInstSrcIdBreakpoint; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcReturnAddress *) { + return IrInstSrcIdReturnAddress; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcFrameAddress *) { + return IrInstSrcIdFrameAddress; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcFrameHandle *) { + return IrInstSrcIdFrameHandle; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcFrameType *) { + return IrInstSrcIdFrameType; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcFrameSize *) { + return IrInstSrcIdFrameSize; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcAlignOf *) { + return IrInstSrcIdAlignOf; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcOverflowOp *) { + return IrInstSrcIdOverflowOp; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcTestErr *) { + return IrInstSrcIdTestErr; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcMulAdd *) { + return IrInstSrcIdMulAdd; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcFloatOp *) { + return IrInstSrcIdFloatOp; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcUnwrapErrCode *) { + return IrInstSrcIdUnwrapErrCode; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcUnwrapErrPayload *) { + return IrInstSrcIdUnwrapErrPayload; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcFnProto *) { + return IrInstSrcIdFnProto; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcTestComptime *) { + return IrInstSrcIdTestComptime; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcPtrCast *) { + return IrInstSrcIdPtrCast; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcBitCast *) { + return IrInstSrcIdBitCast; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcIntToPtr *) { + return IrInstSrcIdIntToPtr; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcPtrToInt *) { + return IrInstSrcIdPtrToInt; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcIntToEnum *) { + return IrInstSrcIdIntToEnum; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcEnumToInt *) { + return IrInstSrcIdEnumToInt; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcIntToErr *) { + return IrInstSrcIdIntToErr; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcErrToInt *) { + return IrInstSrcIdErrToInt; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcCheckSwitchProngs *) { + return IrInstSrcIdCheckSwitchProngs; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcCheckStatementIsVoid *) { + return IrInstSrcIdCheckStatementIsVoid; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcTypeName *) { + return IrInstSrcIdTypeName; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcDeclRef *) { + return IrInstSrcIdDeclRef; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcPanic *) { + return IrInstSrcIdPanic; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcTagName *) { + return IrInstSrcIdTagName; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcTagType *) { + return IrInstSrcIdTagType; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcFieldParentPtr *) { + return IrInstSrcIdFieldParentPtr; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcByteOffsetOf *) { + return IrInstSrcIdByteOffsetOf; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcBitOffsetOf *) { + return IrInstSrcIdBitOffsetOf; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcTypeInfo *) { + return IrInstSrcIdTypeInfo; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcType *) { + return IrInstSrcIdType; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcHasField *) { + return IrInstSrcIdHasField; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcSetEvalBranchQuota *) { + return IrInstSrcIdSetEvalBranchQuota; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcPtrType *) { + return IrInstSrcIdPtrType; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcAlignCast *) { + return IrInstSrcIdAlignCast; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcImplicitCast *) { + return IrInstSrcIdImplicitCast; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcResolveResult *) { + return IrInstSrcIdResolveResult; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcResetResult *) { + return IrInstSrcIdResetResult; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcSetAlignStack *) { + return IrInstSrcIdSetAlignStack; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcArgType *) { + return IrInstSrcIdArgType; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcExport *) { + return IrInstSrcIdExport; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcErrorReturnTrace *) { + return IrInstSrcIdErrorReturnTrace; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcErrorUnion *) { + return IrInstSrcIdErrorUnion; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcAtomicRmw *) { + return IrInstSrcIdAtomicRmw; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcAtomicLoad *) { + return IrInstSrcIdAtomicLoad; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcAtomicStore *) { + return IrInstSrcIdAtomicStore; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcSaveErrRetAddr *) { + return IrInstSrcIdSaveErrRetAddr; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcAddImplicitReturnType *) { + return IrInstSrcIdAddImplicitReturnType; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcErrSetCast *) { + return IrInstSrcIdErrSetCast; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcCheckRuntimeScope *) { + return IrInstSrcIdCheckRuntimeScope; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcHasDecl *) { + return IrInstSrcIdHasDecl; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcUndeclaredIdent *) { + return IrInstSrcIdUndeclaredIdent; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcAlloca *) { + return IrInstSrcIdAlloca; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcEndExpr *) { + return IrInstSrcIdEndExpr; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcUnionInitNamedField *) { + return IrInstSrcIdUnionInitNamedField; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcSuspendBegin *) { + return IrInstSrcIdSuspendBegin; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcSuspendFinish *) { + return IrInstSrcIdSuspendFinish; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcAwait *) { + return IrInstSrcIdAwait; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcResume *) { + return IrInstSrcIdResume; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcSpillBegin *) { + return IrInstSrcIdSpillBegin; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcSpillEnd *) { + return IrInstSrcIdSpillEnd; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcWasmMemorySize *) { + return IrInstSrcIdWasmMemorySize; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcWasmMemoryGrow *) { + return IrInstSrcIdWasmMemoryGrow; +} + +static constexpr IrInstSrcId ir_inst_id(IrInstSrcSrc *) { + return IrInstSrcIdSrc; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenDeclVar *) { + return IrInstGenIdDeclVar; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenBr *) { + return IrInstGenIdBr; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenCondBr *) { + return IrInstGenIdCondBr; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenSwitchBr *) { + return IrInstGenIdSwitchBr; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenPhi *) { + return IrInstGenIdPhi; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenBinaryNot *) { + return IrInstGenIdBinaryNot; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenNegation *) { + return IrInstGenIdNegation; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenNegationWrapping *) { + return IrInstGenIdNegationWrapping; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenBinOp *) { + return IrInstGenIdBinOp; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenLoadPtr *) { + return IrInstGenIdLoadPtr; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenStorePtr *) { + return IrInstGenIdStorePtr; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenVectorStoreElem *) { + return IrInstGenIdVectorStoreElem; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenStructFieldPtr *) { + return IrInstGenIdStructFieldPtr; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenUnionFieldPtr *) { + return IrInstGenIdUnionFieldPtr; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenElemPtr *) { + return IrInstGenIdElemPtr; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenVarPtr *) { + return IrInstGenIdVarPtr; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenReturnPtr *) { + return IrInstGenIdReturnPtr; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenCall *) { + return IrInstGenIdCall; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenReturn *) { + return IrInstGenIdReturn; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenCast *) { + return IrInstGenIdCast; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenUnreachable *) { + return IrInstGenIdUnreachable; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenAsm *) { + return IrInstGenIdAsm; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenTestNonNull *) { + return IrInstGenIdTestNonNull; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenOptionalUnwrapPtr *) { + return IrInstGenIdOptionalUnwrapPtr; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenOptionalWrap *) { + return IrInstGenIdOptionalWrap; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenUnionTag *) { + return IrInstGenIdUnionTag; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenClz *) { + return IrInstGenIdClz; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenCtz *) { + return IrInstGenIdCtz; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenPopCount *) { + return IrInstGenIdPopCount; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenBswap *) { + return IrInstGenIdBswap; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenBitReverse *) { + return IrInstGenIdBitReverse; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenRef *) { + return IrInstGenIdRef; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenErrName *) { + return IrInstGenIdErrName; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenCmpxchg *) { + return IrInstGenIdCmpxchg; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenFence *) { + return IrInstGenIdFence; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenTruncate *) { + return IrInstGenIdTruncate; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenShuffleVector *) { + return IrInstGenIdShuffleVector; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenSplat *) { + return IrInstGenIdSplat; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenBoolNot *) { + return IrInstGenIdBoolNot; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenMemset *) { + return IrInstGenIdMemset; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenMemcpy *) { + return IrInstGenIdMemcpy; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenSlice *) { + return IrInstGenIdSlice; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenBreakpoint *) { + return IrInstGenIdBreakpoint; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenReturnAddress *) { + return IrInstGenIdReturnAddress; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenFrameAddress *) { + return IrInstGenIdFrameAddress; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenFrameHandle *) { + return IrInstGenIdFrameHandle; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenFrameSize *) { + return IrInstGenIdFrameSize; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenOverflowOp *) { + return IrInstGenIdOverflowOp; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenTestErr *) { + return IrInstGenIdTestErr; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenMulAdd *) { + return IrInstGenIdMulAdd; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenFloatOp *) { + return IrInstGenIdFloatOp; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenUnwrapErrCode *) { + return IrInstGenIdUnwrapErrCode; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenUnwrapErrPayload *) { + return IrInstGenIdUnwrapErrPayload; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenErrWrapCode *) { + return IrInstGenIdErrWrapCode; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenErrWrapPayload *) { + return IrInstGenIdErrWrapPayload; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenPtrCast *) { + return IrInstGenIdPtrCast; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenBitCast *) { + return IrInstGenIdBitCast; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenWidenOrShorten *) { + return IrInstGenIdWidenOrShorten; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenIntToPtr *) { + return IrInstGenIdIntToPtr; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenPtrToInt *) { + return IrInstGenIdPtrToInt; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenIntToEnum *) { + return IrInstGenIdIntToEnum; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenIntToErr *) { + return IrInstGenIdIntToErr; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenErrToInt *) { + return IrInstGenIdErrToInt; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenPanic *) { + return IrInstGenIdPanic; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenTagName *) { + return IrInstGenIdTagName; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenFieldParentPtr *) { + return IrInstGenIdFieldParentPtr; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenAlignCast *) { + return IrInstGenIdAlignCast; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenErrorReturnTrace *) { + return IrInstGenIdErrorReturnTrace; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenAtomicRmw *) { + return IrInstGenIdAtomicRmw; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenAtomicLoad *) { + return IrInstGenIdAtomicLoad; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenAtomicStore *) { + return IrInstGenIdAtomicStore; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenSaveErrRetAddr *) { + return IrInstGenIdSaveErrRetAddr; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenVectorToArray *) { + return IrInstGenIdVectorToArray; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenArrayToVector *) { + return IrInstGenIdArrayToVector; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenAssertZero *) { + return IrInstGenIdAssertZero; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenAssertNonNull *) { + return IrInstGenIdAssertNonNull; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenPtrOfArrayToSlice *) { + return IrInstGenIdPtrOfArrayToSlice; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenSuspendBegin *) { + return IrInstGenIdSuspendBegin; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenSuspendFinish *) { + return IrInstGenIdSuspendFinish; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenAwait *) { + return IrInstGenIdAwait; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenResume *) { + return IrInstGenIdResume; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenSpillBegin *) { + return IrInstGenIdSpillBegin; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenSpillEnd *) { + return IrInstGenIdSpillEnd; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenVectorExtractElem *) { + return IrInstGenIdVectorExtractElem; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenAlloca *) { + return IrInstGenIdAlloca; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenConst *) { + return IrInstGenIdConst; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenWasmMemorySize *) { + return IrInstGenIdWasmMemorySize; +} + +static constexpr IrInstGenId ir_inst_id(IrInstGenWasmMemoryGrow *) { + return IrInstGenIdWasmMemoryGrow; +} + +template +static T *ir_create_instruction(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { + T *special_instruction = heap::c_allocator.create(); + special_instruction->base.id = ir_inst_id(special_instruction); + special_instruction->base.base.scope = scope; + special_instruction->base.base.source_node = source_node; + special_instruction->base.base.debug_id = exec_next_debug_id(irb->exec); + special_instruction->base.owner_bb = irb->current_basic_block; + return special_instruction; +} + +template +static T *ir_create_inst_gen(IrBuilderGen *irb, Scope *scope, AstNode *source_node) { + T *special_instruction = heap::c_allocator.create(); + special_instruction->base.id = ir_inst_id(special_instruction); + special_instruction->base.base.scope = scope; + special_instruction->base.base.source_node = source_node; + special_instruction->base.base.debug_id = exec_next_debug_id_gen(irb->exec); + special_instruction->base.owner_bb = irb->current_basic_block; + special_instruction->base.value = irb->codegen->pass1_arena->create(); + return special_instruction; +} + +template +static T *ir_create_inst_noval(IrBuilderGen *irb, Scope *scope, AstNode *source_node) { + T *special_instruction = heap::c_allocator.create(); + special_instruction->base.id = ir_inst_id(special_instruction); + special_instruction->base.base.scope = scope; + special_instruction->base.base.source_node = source_node; + special_instruction->base.base.debug_id = exec_next_debug_id_gen(irb->exec); + special_instruction->base.owner_bb = irb->current_basic_block; + return special_instruction; +} + +template +static T *ir_build_instruction(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { + T *special_instruction = ir_create_instruction(irb, scope, source_node); + ir_instruction_append(irb->current_basic_block, &special_instruction->base); + return special_instruction; +} + +template +static T *ir_build_inst_gen(IrBuilderGen *irb, Scope *scope, AstNode *source_node) { + T *special_instruction = ir_create_inst_gen(irb, scope, source_node); + ir_inst_gen_append(irb->current_basic_block, &special_instruction->base); + return special_instruction; +} + +template +static T *ir_build_inst_noreturn(IrBuilderGen *irb, Scope *scope, AstNode *source_node) { + T *special_instruction = ir_create_inst_noval(irb, scope, source_node); + special_instruction->base.value = irb->codegen->intern.for_unreachable(); + ir_inst_gen_append(irb->current_basic_block, &special_instruction->base); + return special_instruction; +} + +template +static T *ir_build_inst_void(IrBuilderGen *irb, Scope *scope, AstNode *source_node) { + T *special_instruction = ir_create_inst_noval(irb, scope, source_node); + special_instruction->base.value = irb->codegen->intern.for_void(); + ir_inst_gen_append(irb->current_basic_block, &special_instruction->base); + return special_instruction; +} + +IrInstGen *ir_create_alloca(CodeGen *g, Scope *scope, AstNode *source_node, ZigFn *fn, + ZigType *var_type, const char *name_hint) +{ + IrInstGenAlloca *alloca_gen = heap::c_allocator.create(); + alloca_gen->base.id = IrInstGenIdAlloca; + alloca_gen->base.base.source_node = source_node; + alloca_gen->base.base.scope = scope; + alloca_gen->base.value = g->pass1_arena->create(); + alloca_gen->base.value->type = get_pointer_to_type(g, var_type, false); + alloca_gen->base.base.ref_count = 1; + alloca_gen->name_hint = name_hint; + fn->alloca_gen_list.append(alloca_gen); + return &alloca_gen->base; +} + +static IrInstGen *ir_build_cast(IrAnalyze *ira, IrInst *source_instr,ZigType *dest_type, + IrInstGen *value, CastOp cast_op) +{ + IrInstGenCast *inst = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); + inst->base.value->type = dest_type; + inst->value = value; + inst->cast_op = cast_op; + + ir_ref_inst_gen(value); + + return &inst->base; +} + +static IrInstSrc *ir_build_cond_br(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *condition, + IrBasicBlockSrc *then_block, IrBasicBlockSrc *else_block, IrInstSrc *is_comptime) +{ + IrInstSrcCondBr *inst = ir_build_instruction(irb, scope, source_node); + inst->base.is_noreturn = true; + inst->condition = condition; + inst->then_block = then_block; + inst->else_block = else_block; + inst->is_comptime = is_comptime; + + ir_ref_instruction(condition, irb->current_basic_block); + ir_ref_bb(then_block); + ir_ref_bb(else_block); + if (is_comptime != nullptr) ir_ref_instruction(is_comptime, irb->current_basic_block); + + return &inst->base; +} + +static IrInstGen *ir_build_cond_br_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *condition, + IrBasicBlockGen *then_block, IrBasicBlockGen *else_block) +{ + IrInstGenCondBr *inst = ir_build_inst_noreturn(&ira->new_irb, source_instr->scope, source_instr->source_node); + inst->condition = condition; + inst->then_block = then_block; + inst->else_block = else_block; + + ir_ref_inst_gen(condition); + + return &inst->base; +} + +static IrInstSrc *ir_build_return_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *operand) { + IrInstSrcReturn *inst = ir_build_instruction(irb, scope, source_node); + inst->base.is_noreturn = true; + inst->operand = operand; + + if (operand != nullptr) ir_ref_instruction(operand, irb->current_basic_block); + + return &inst->base; +} + +static IrInstGen *ir_build_return_gen(IrAnalyze *ira, IrInst *source_inst, IrInstGen *operand) { + IrInstGenReturn *inst = ir_build_inst_noreturn(&ira->new_irb, + source_inst->scope, source_inst->source_node); + inst->operand = operand; + + if (operand != nullptr) ir_ref_inst_gen(operand); + + return &inst->base; +} + +static IrInstSrc *ir_build_const_void(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { + IrInstSrcConst *const_instruction = ir_create_instruction(irb, scope, source_node); + ir_instruction_append(irb->current_basic_block, &const_instruction->base); + const_instruction->value = irb->codegen->intern.for_void(); + return &const_instruction->base; +} + +static IrInstSrc *ir_build_const_undefined(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { + IrInstSrcConst *const_instruction = ir_create_instruction(irb, scope, source_node); + ir_instruction_append(irb->current_basic_block, &const_instruction->base); + const_instruction->value = irb->codegen->intern.for_undefined(); + const_instruction->value->special = ConstValSpecialUndef; + return &const_instruction->base; +} + +static IrInstSrc *ir_build_const_uint(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, uint64_t value) { + IrInstSrcConst *const_instruction = ir_build_instruction(irb, scope, source_node); + const_instruction->value = irb->codegen->pass1_arena->create(); + const_instruction->value->type = irb->codegen->builtin_types.entry_num_lit_int; + const_instruction->value->special = ConstValSpecialStatic; + bigint_init_unsigned(&const_instruction->value->data.x_bigint, value); + return &const_instruction->base; +} + +static IrInstSrc *ir_build_const_bigint(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, BigInt *bigint) { + IrInstSrcConst *const_instruction = ir_build_instruction(irb, scope, source_node); + const_instruction->value = irb->codegen->pass1_arena->create(); + const_instruction->value->type = irb->codegen->builtin_types.entry_num_lit_int; + const_instruction->value->special = ConstValSpecialStatic; + bigint_init_bigint(&const_instruction->value->data.x_bigint, bigint); + return &const_instruction->base; +} + +static IrInstSrc *ir_build_const_bigfloat(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, BigFloat *bigfloat) { + IrInstSrcConst *const_instruction = ir_build_instruction(irb, scope, source_node); + const_instruction->value = irb->codegen->pass1_arena->create(); + const_instruction->value->type = irb->codegen->builtin_types.entry_num_lit_float; + const_instruction->value->special = ConstValSpecialStatic; + bigfloat_init_bigfloat(&const_instruction->value->data.x_bigfloat, bigfloat); + return &const_instruction->base; +} + +static IrInstSrc *ir_build_const_null(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { + IrInstSrcConst *const_instruction = ir_create_instruction(irb, scope, source_node); + ir_instruction_append(irb->current_basic_block, &const_instruction->base); + const_instruction->value = irb->codegen->intern.for_null(); + return &const_instruction->base; +} + +static IrInstSrc *ir_build_const_usize(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, uint64_t value) { + IrInstSrcConst *const_instruction = ir_build_instruction(irb, scope, source_node); + const_instruction->value = irb->codegen->pass1_arena->create(); + const_instruction->value->type = irb->codegen->builtin_types.entry_usize; + const_instruction->value->special = ConstValSpecialStatic; + bigint_init_unsigned(&const_instruction->value->data.x_bigint, value); + return &const_instruction->base; +} + +static IrInstSrc *ir_create_const_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + ZigType *type_entry) +{ + IrInstSrcConst *const_instruction = ir_create_instruction(irb, scope, source_node); + const_instruction->value = irb->codegen->pass1_arena->create(); + const_instruction->value->type = irb->codegen->builtin_types.entry_type; + const_instruction->value->special = ConstValSpecialStatic; + const_instruction->value->data.x_type = type_entry; + return &const_instruction->base; +} + +static IrInstSrc *ir_build_const_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + ZigType *type_entry) +{ + IrInstSrc *instruction = ir_create_const_type(irb, scope, source_node, type_entry); + ir_instruction_append(irb->current_basic_block, instruction); + return instruction; +} + +static IrInstSrc *ir_build_const_import(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, ZigType *import) { + IrInstSrcConst *const_instruction = ir_build_instruction(irb, scope, source_node); + const_instruction->value = irb->codegen->pass1_arena->create(); + const_instruction->value->type = irb->codegen->builtin_types.entry_type; + const_instruction->value->special = ConstValSpecialStatic; + const_instruction->value->data.x_type = import; + return &const_instruction->base; +} + +static IrInstSrc *ir_build_const_bool(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, bool value) { + IrInstSrcConst *const_instruction = ir_build_instruction(irb, scope, source_node); + const_instruction->value = irb->codegen->pass1_arena->create(); + const_instruction->value->type = irb->codegen->builtin_types.entry_bool; + const_instruction->value->special = ConstValSpecialStatic; + const_instruction->value->data.x_bool = value; + return &const_instruction->base; +} + +static IrInstSrc *ir_build_const_enum_literal(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, Buf *name) { + IrInstSrcConst *const_instruction = ir_build_instruction(irb, scope, source_node); + const_instruction->value = irb->codegen->pass1_arena->create(); + const_instruction->value->type = irb->codegen->builtin_types.entry_enum_literal; + const_instruction->value->special = ConstValSpecialStatic; + const_instruction->value->data.x_enum_literal = name; + return &const_instruction->base; +} + +static IrInstSrc *ir_create_const_str_lit(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, Buf *str) { + IrInstSrcConst *const_instruction = ir_create_instruction(irb, scope, source_node); + const_instruction->value = irb->codegen->pass1_arena->create(); + init_const_str_lit(irb->codegen, const_instruction->value, str); + + return &const_instruction->base; +} + +static IrInstSrc *ir_build_const_str_lit(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, Buf *str) { + IrInstSrc *instruction = ir_create_const_str_lit(irb, scope, source_node, str); + ir_instruction_append(irb->current_basic_block, instruction); + return instruction; +} + +static IrInstSrc *ir_build_bin_op(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrBinOp op_id, + IrInstSrc *op1, IrInstSrc *op2, bool safety_check_on) +{ + IrInstSrcBinOp *inst = ir_build_instruction(irb, scope, source_node); + inst->op_id = op_id; + inst->op1 = op1; + inst->op2 = op2; + inst->safety_check_on = safety_check_on; + + ir_ref_instruction(op1, irb->current_basic_block); + ir_ref_instruction(op2, irb->current_basic_block); + + return &inst->base; +} + +static IrInstGen *ir_build_bin_op_gen(IrAnalyze *ira, IrInst *source_instr, ZigType *res_type, + IrBinOp op_id, IrInstGen *op1, IrInstGen *op2, bool safety_check_on) +{ + IrInstGenBinOp *inst = ir_build_inst_gen(&ira->new_irb, + source_instr->scope, source_instr->source_node); + inst->base.value->type = res_type; + inst->op_id = op_id; + inst->op1 = op1; + inst->op2 = op2; + inst->safety_check_on = safety_check_on; + + ir_ref_inst_gen(op1); + ir_ref_inst_gen(op2); + + return &inst->base; +} + + +static IrInstSrc *ir_build_merge_err_sets(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *op1, IrInstSrc *op2, Buf *type_name) +{ + IrInstSrcMergeErrSets *inst = ir_build_instruction(irb, scope, source_node); + inst->op1 = op1; + inst->op2 = op2; + inst->type_name = type_name; + + ir_ref_instruction(op1, irb->current_basic_block); + ir_ref_instruction(op2, irb->current_basic_block); + + return &inst->base; +} + +static IrInstSrc *ir_build_var_ptr_x(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, ZigVar *var, + ScopeFnDef *crossed_fndef_scope) +{ + IrInstSrcVarPtr *instruction = ir_build_instruction(irb, scope, source_node); + instruction->var = var; + instruction->crossed_fndef_scope = crossed_fndef_scope; + + ir_ref_var(var); + + return &instruction->base; +} + +static IrInstSrc *ir_build_var_ptr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, ZigVar *var) { + return ir_build_var_ptr_x(irb, scope, source_node, var, nullptr); +} + +static IrInstGen *ir_build_var_ptr_gen(IrAnalyze *ira, IrInst *source_instr, ZigVar *var) { + IrInstGenVarPtr *instruction = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); + instruction->var = var; + + ir_ref_var(var); + + return &instruction->base; +} + +static IrInstGen *ir_build_return_ptr(IrAnalyze *ira, Scope *scope, AstNode *source_node, ZigType *ty) { + IrInstGenReturnPtr *instruction = ir_build_inst_gen(&ira->new_irb, scope, source_node); + instruction->base.value->type = ty; + return &instruction->base; +} + +static IrInstSrc *ir_build_elem_ptr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *array_ptr, IrInstSrc *elem_index, bool safety_check_on, PtrLen ptr_len, + AstNode *init_array_type_source_node) +{ + IrInstSrcElemPtr *instruction = ir_build_instruction(irb, scope, source_node); + instruction->array_ptr = array_ptr; + instruction->elem_index = elem_index; + instruction->safety_check_on = safety_check_on; + instruction->ptr_len = ptr_len; + instruction->init_array_type_source_node = init_array_type_source_node; + + ir_ref_instruction(array_ptr, irb->current_basic_block); + ir_ref_instruction(elem_index, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGen *ir_build_elem_ptr_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node, + IrInstGen *array_ptr, IrInstGen *elem_index, bool safety_check_on, ZigType *return_type) +{ + IrInstGenElemPtr *instruction = ir_build_inst_gen(&ira->new_irb, scope, source_node); + instruction->base.value->type = return_type; + instruction->array_ptr = array_ptr; + instruction->elem_index = elem_index; + instruction->safety_check_on = safety_check_on; + + ir_ref_inst_gen(array_ptr); + ir_ref_inst_gen(elem_index); + + return &instruction->base; +} + +static IrInstSrc *ir_build_field_ptr_instruction(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *container_ptr, IrInstSrc *field_name_expr, bool initializing) +{ + IrInstSrcFieldPtr *instruction = ir_build_instruction(irb, scope, source_node); + instruction->container_ptr = container_ptr; + instruction->field_name_buffer = nullptr; + instruction->field_name_expr = field_name_expr; + instruction->initializing = initializing; + + ir_ref_instruction(container_ptr, irb->current_basic_block); + ir_ref_instruction(field_name_expr, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_field_ptr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *container_ptr, Buf *field_name, bool initializing) +{ + IrInstSrcFieldPtr *instruction = ir_build_instruction(irb, scope, source_node); + instruction->container_ptr = container_ptr; + instruction->field_name_buffer = field_name; + instruction->field_name_expr = nullptr; + instruction->initializing = initializing; + + ir_ref_instruction(container_ptr, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_has_field(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *container_type, IrInstSrc *field_name) +{ + IrInstSrcHasField *instruction = ir_build_instruction(irb, scope, source_node); + instruction->container_type = container_type; + instruction->field_name = field_name; + + ir_ref_instruction(container_type, irb->current_basic_block); + ir_ref_instruction(field_name, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGen *ir_build_struct_field_ptr(IrAnalyze *ira, IrInst *source_instr, + IrInstGen *struct_ptr, TypeStructField *field, ZigType *ptr_type) +{ + IrInstGenStructFieldPtr *inst = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); + inst->base.value->type = ptr_type; + inst->struct_ptr = struct_ptr; + inst->field = field; + + ir_ref_inst_gen(struct_ptr); + + return &inst->base; +} + +static IrInstGen *ir_build_union_field_ptr(IrAnalyze *ira, IrInst *source_instr, + IrInstGen *union_ptr, TypeUnionField *field, bool safety_check_on, bool initializing, ZigType *ptr_type) +{ + IrInstGenUnionFieldPtr *inst = ir_build_inst_gen(&ira->new_irb, + source_instr->scope, source_instr->source_node); + inst->base.value->type = ptr_type; + inst->initializing = initializing; + inst->safety_check_on = safety_check_on; + inst->union_ptr = union_ptr; + inst->field = field; + + ir_ref_inst_gen(union_ptr); + + return &inst->base; +} + +static IrInstSrc *ir_build_call_extra(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *options, IrInstSrc *fn_ref, IrInstSrc *args, ResultLoc *result_loc) +{ + IrInstSrcCallExtra *call_instruction = ir_build_instruction(irb, scope, source_node); + call_instruction->options = options; + call_instruction->fn_ref = fn_ref; + call_instruction->args = args; + call_instruction->result_loc = result_loc; + + ir_ref_instruction(options, irb->current_basic_block); + ir_ref_instruction(fn_ref, irb->current_basic_block); + ir_ref_instruction(args, irb->current_basic_block); + + return &call_instruction->base; +} + +static IrInstSrc *ir_build_async_call_extra(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + CallModifier modifier, IrInstSrc *fn_ref, IrInstSrc *ret_ptr, IrInstSrc *new_stack, IrInstSrc *args, ResultLoc *result_loc) +{ + IrInstSrcAsyncCallExtra *call_instruction = ir_build_instruction(irb, scope, source_node); + call_instruction->modifier = modifier; + call_instruction->fn_ref = fn_ref; + call_instruction->ret_ptr = ret_ptr; + call_instruction->new_stack = new_stack; + call_instruction->args = args; + call_instruction->result_loc = result_loc; + + ir_ref_instruction(fn_ref, irb->current_basic_block); + if (ret_ptr != nullptr) ir_ref_instruction(ret_ptr, irb->current_basic_block); + ir_ref_instruction(new_stack, irb->current_basic_block); + ir_ref_instruction(args, irb->current_basic_block); + + return &call_instruction->base; +} + +static IrInstSrc *ir_build_call_args(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *options, IrInstSrc *fn_ref, IrInstSrc **args_ptr, size_t args_len, + ResultLoc *result_loc) +{ + IrInstSrcCallArgs *call_instruction = ir_build_instruction(irb, scope, source_node); + call_instruction->options = options; + call_instruction->fn_ref = fn_ref; + call_instruction->args_ptr = args_ptr; + call_instruction->args_len = args_len; + call_instruction->result_loc = result_loc; + + ir_ref_instruction(options, irb->current_basic_block); + ir_ref_instruction(fn_ref, irb->current_basic_block); + for (size_t i = 0; i < args_len; i += 1) + ir_ref_instruction(args_ptr[i], irb->current_basic_block); + + return &call_instruction->base; +} + +static IrInstSrc *ir_build_call_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + ZigFn *fn_entry, IrInstSrc *fn_ref, size_t arg_count, IrInstSrc **args, + IrInstSrc *ret_ptr, CallModifier modifier, bool is_async_call_builtin, + IrInstSrc *new_stack, ResultLoc *result_loc) +{ + IrInstSrcCall *call_instruction = ir_build_instruction(irb, scope, source_node); + call_instruction->fn_entry = fn_entry; + call_instruction->fn_ref = fn_ref; + call_instruction->args = args; + call_instruction->arg_count = arg_count; + call_instruction->modifier = modifier; + call_instruction->is_async_call_builtin = is_async_call_builtin; + call_instruction->new_stack = new_stack; + call_instruction->result_loc = result_loc; + call_instruction->ret_ptr = ret_ptr; + + if (fn_ref != nullptr) ir_ref_instruction(fn_ref, irb->current_basic_block); + for (size_t i = 0; i < arg_count; i += 1) + ir_ref_instruction(args[i], irb->current_basic_block); + if (ret_ptr != nullptr) ir_ref_instruction(ret_ptr, irb->current_basic_block); + if (new_stack != nullptr) ir_ref_instruction(new_stack, irb->current_basic_block); + + return &call_instruction->base; +} + +static IrInstGenCall *ir_build_call_gen(IrAnalyze *ira, IrInst *source_instruction, + ZigFn *fn_entry, IrInstGen *fn_ref, size_t arg_count, IrInstGen **args, + CallModifier modifier, IrInstGen *new_stack, bool is_async_call_builtin, + IrInstGen *result_loc, ZigType *return_type) +{ + IrInstGenCall *call_instruction = ir_build_inst_gen(&ira->new_irb, + source_instruction->scope, source_instruction->source_node); + call_instruction->base.value->type = return_type; + call_instruction->fn_entry = fn_entry; + call_instruction->fn_ref = fn_ref; + call_instruction->args = args; + call_instruction->arg_count = arg_count; + call_instruction->modifier = modifier; + call_instruction->is_async_call_builtin = is_async_call_builtin; + call_instruction->new_stack = new_stack; + call_instruction->result_loc = result_loc; + + if (fn_ref != nullptr) ir_ref_inst_gen(fn_ref); + for (size_t i = 0; i < arg_count; i += 1) + ir_ref_inst_gen(args[i]); + if (new_stack != nullptr) ir_ref_inst_gen(new_stack); + if (result_loc != nullptr) ir_ref_inst_gen(result_loc); + + return call_instruction; +} + +static IrInstSrc *ir_build_phi(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + size_t incoming_count, IrBasicBlockSrc **incoming_blocks, IrInstSrc **incoming_values, + ResultLocPeerParent *peer_parent) +{ + assert(incoming_count != 0); + assert(incoming_count != SIZE_MAX); + + IrInstSrcPhi *phi_instruction = ir_build_instruction(irb, scope, source_node); + phi_instruction->incoming_count = incoming_count; + phi_instruction->incoming_blocks = incoming_blocks; + phi_instruction->incoming_values = incoming_values; + phi_instruction->peer_parent = peer_parent; + + for (size_t i = 0; i < incoming_count; i += 1) { + ir_ref_bb(incoming_blocks[i]); + ir_ref_instruction(incoming_values[i], irb->current_basic_block); + } + + return &phi_instruction->base; +} + +static IrInstGen *ir_build_phi_gen(IrAnalyze *ira, IrInst *source_instr, size_t incoming_count, + IrBasicBlockGen **incoming_blocks, IrInstGen **incoming_values, ZigType *result_type) +{ + assert(incoming_count != 0); + assert(incoming_count != SIZE_MAX); + + IrInstGenPhi *phi_instruction = ir_build_inst_gen(&ira->new_irb, + source_instr->scope, source_instr->source_node); + phi_instruction->base.value->type = result_type; + phi_instruction->incoming_count = incoming_count; + phi_instruction->incoming_blocks = incoming_blocks; + phi_instruction->incoming_values = incoming_values; + + for (size_t i = 0; i < incoming_count; i += 1) { + ir_ref_inst_gen(incoming_values[i]); + } + + return &phi_instruction->base; +} + +static IrInstSrc *ir_build_br(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrBasicBlockSrc *dest_block, IrInstSrc *is_comptime) +{ + IrInstSrcBr *inst = ir_build_instruction(irb, scope, source_node); + inst->base.is_noreturn = true; + inst->dest_block = dest_block; + inst->is_comptime = is_comptime; + + ir_ref_bb(dest_block); + if (is_comptime) ir_ref_instruction(is_comptime, irb->current_basic_block); + + return &inst->base; +} + +static IrInstGen *ir_build_br_gen(IrAnalyze *ira, IrInst *source_instr, IrBasicBlockGen *dest_block) { + IrInstGenBr *inst = ir_build_inst_noreturn(&ira->new_irb, source_instr->scope, source_instr->source_node); + inst->dest_block = dest_block; + + return &inst->base; +} + +static IrInstSrc *ir_build_ptr_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *child_type, bool is_const, bool is_volatile, PtrLen ptr_len, + IrInstSrc *sentinel, IrInstSrc *align_value, + uint32_t bit_offset_start, uint32_t host_int_bytes, bool is_allow_zero) +{ + IrInstSrcPtrType *inst = ir_build_instruction(irb, scope, source_node); + inst->sentinel = sentinel; + inst->align_value = align_value; + inst->child_type = child_type; + inst->is_const = is_const; + inst->is_volatile = is_volatile; + inst->ptr_len = ptr_len; + inst->bit_offset_start = bit_offset_start; + inst->host_int_bytes = host_int_bytes; + inst->is_allow_zero = is_allow_zero; + + if (sentinel) ir_ref_instruction(sentinel, irb->current_basic_block); + if (align_value) ir_ref_instruction(align_value, irb->current_basic_block); + ir_ref_instruction(child_type, irb->current_basic_block); + + return &inst->base; +} + +static IrInstSrc *ir_build_un_op_lval(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrUnOp op_id, + IrInstSrc *value, LVal lval, ResultLoc *result_loc) +{ + IrInstSrcUnOp *instruction = ir_build_instruction(irb, scope, source_node); + instruction->op_id = op_id; + instruction->value = value; + instruction->lval = lval; + instruction->result_loc = result_loc; + + ir_ref_instruction(value, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_un_op(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrUnOp op_id, + IrInstSrc *value) +{ + return ir_build_un_op_lval(irb, scope, source_node, op_id, value, LValNone, nullptr); +} + +static IrInstGen *ir_build_negation(IrAnalyze *ira, IrInst *source_instr, IrInstGen *operand, ZigType *expr_type) { + IrInstGenNegation *instruction = ir_build_inst_gen(&ira->new_irb, + source_instr->scope, source_instr->source_node); + instruction->base.value->type = expr_type; + instruction->operand = operand; + + ir_ref_inst_gen(operand); + + return &instruction->base; +} + +static IrInstGen *ir_build_negation_wrapping(IrAnalyze *ira, IrInst *source_instr, IrInstGen *operand, + ZigType *expr_type) +{ + IrInstGenNegationWrapping *instruction = ir_build_inst_gen(&ira->new_irb, + source_instr->scope, source_instr->source_node); + instruction->base.value->type = expr_type; + instruction->operand = operand; + + ir_ref_inst_gen(operand); + + return &instruction->base; +} + +static IrInstGen *ir_build_binary_not(IrAnalyze *ira, IrInst *source_instr, IrInstGen *operand, + ZigType *expr_type) +{ + IrInstGenBinaryNot *instruction = ir_build_inst_gen(&ira->new_irb, + source_instr->scope, source_instr->source_node); + instruction->base.value->type = expr_type; + instruction->operand = operand; + + ir_ref_inst_gen(operand); + + return &instruction->base; +} + +static IrInstSrc *ir_build_container_init_list(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + size_t item_count, IrInstSrc **elem_result_loc_list, IrInstSrc *result_loc, + AstNode *init_array_type_source_node) +{ + IrInstSrcContainerInitList *container_init_list_instruction = + ir_build_instruction(irb, scope, source_node); + container_init_list_instruction->item_count = item_count; + container_init_list_instruction->elem_result_loc_list = elem_result_loc_list; + container_init_list_instruction->result_loc = result_loc; + container_init_list_instruction->init_array_type_source_node = init_array_type_source_node; + + for (size_t i = 0; i < item_count; i += 1) { + ir_ref_instruction(elem_result_loc_list[i], irb->current_basic_block); + } + if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block); + + return &container_init_list_instruction->base; +} + +static IrInstSrc *ir_build_container_init_fields(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + size_t field_count, IrInstSrcContainerInitFieldsField *fields, IrInstSrc *result_loc) +{ + IrInstSrcContainerInitFields *container_init_fields_instruction = + ir_build_instruction(irb, scope, source_node); + container_init_fields_instruction->field_count = field_count; + container_init_fields_instruction->fields = fields; + container_init_fields_instruction->result_loc = result_loc; + + for (size_t i = 0; i < field_count; i += 1) { + ir_ref_instruction(fields[i].result_loc, irb->current_basic_block); + } + if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block); + + return &container_init_fields_instruction->base; +} + +static IrInstSrc *ir_build_unreachable(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { + IrInstSrcUnreachable *inst = ir_build_instruction(irb, scope, source_node); + inst->base.is_noreturn = true; + return &inst->base; +} + +static IrInstGen *ir_build_unreachable_gen(IrAnalyze *ira, IrInst *source_instr) { + IrInstGenUnreachable *inst = ir_build_inst_noreturn(&ira->new_irb, source_instr->scope, source_instr->source_node); + return &inst->base; +} + +static IrInstSrcStorePtr *ir_build_store_ptr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *ptr, IrInstSrc *value) +{ + IrInstSrcStorePtr *instruction = ir_build_instruction(irb, scope, source_node); + instruction->ptr = ptr; + instruction->value = value; + + ir_ref_instruction(ptr, irb->current_basic_block); + ir_ref_instruction(value, irb->current_basic_block); + + return instruction; +} + +static IrInstGen *ir_build_store_ptr_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *ptr, IrInstGen *value) { + IrInstGenStorePtr *instruction = ir_build_inst_void(&ira->new_irb, + source_instr->scope, source_instr->source_node); + instruction->ptr = ptr; + instruction->value = value; + + ir_ref_inst_gen(ptr); + ir_ref_inst_gen(value); + + return &instruction->base; +} + +static IrInstGen *ir_build_vector_store_elem(IrAnalyze *ira, IrInst *src_inst, + IrInstGen *vector_ptr, IrInstGen *index, IrInstGen *value) +{ + IrInstGenVectorStoreElem *inst = ir_build_inst_void( + &ira->new_irb, src_inst->scope, src_inst->source_node); + inst->vector_ptr = vector_ptr; + inst->index = index; + inst->value = value; + + ir_ref_inst_gen(vector_ptr); + ir_ref_inst_gen(index); + ir_ref_inst_gen(value); + + return &inst->base; +} + +static IrInstSrc *ir_build_var_decl_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + ZigVar *var, IrInstSrc *align_value, IrInstSrc *ptr) +{ + IrInstSrcDeclVar *inst = ir_build_instruction(irb, scope, source_node); + inst->var = var; + inst->align_value = align_value; + inst->ptr = ptr; + + if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block); + ir_ref_instruction(ptr, irb->current_basic_block); + + return &inst->base; +} + +static IrInstGen *ir_build_var_decl_gen(IrAnalyze *ira, IrInst *source_instruction, + ZigVar *var, IrInstGen *var_ptr) +{ + IrInstGenDeclVar *inst = ir_build_inst_gen(&ira->new_irb, + source_instruction->scope, source_instruction->source_node); + inst->base.value->special = ConstValSpecialStatic; + inst->base.value->type = ira->codegen->builtin_types.entry_void; + inst->var = var; + inst->var_ptr = var_ptr; + + ir_ref_inst_gen(var_ptr); + + return &inst->base; +} + +static IrInstSrc *ir_build_export(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *target, IrInstSrc *options) +{ + IrInstSrcExport *export_instruction = ir_build_instruction( + irb, scope, source_node); + export_instruction->target = target; + export_instruction->options = options; + + ir_ref_instruction(target, irb->current_basic_block); + ir_ref_instruction(options, irb->current_basic_block); + + return &export_instruction->base; +} + +static IrInstSrc *ir_build_load_ptr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *ptr) { + IrInstSrcLoadPtr *instruction = ir_build_instruction(irb, scope, source_node); + instruction->ptr = ptr; + + ir_ref_instruction(ptr, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGen *ir_build_load_ptr_gen(IrAnalyze *ira, IrInst *source_instruction, + IrInstGen *ptr, ZigType *ty, IrInstGen *result_loc) +{ + IrInstGenLoadPtr *instruction = ir_build_inst_gen( + &ira->new_irb, source_instruction->scope, source_instruction->source_node); + instruction->base.value->type = ty; + instruction->ptr = ptr; + instruction->result_loc = result_loc; + + ir_ref_inst_gen(ptr); + if (result_loc != nullptr) ir_ref_inst_gen(result_loc); + + return &instruction->base; +} + +static IrInstSrc *ir_build_typeof_n(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc **values, size_t value_count) +{ + assert(value_count >= 2); + + IrInstSrcTypeOf *instruction = ir_build_instruction(irb, scope, source_node); + instruction->value.list = values; + instruction->value_count = value_count; + + for (size_t i = 0; i < value_count; i++) + ir_ref_instruction(values[i], irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_typeof_1(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *value) { + IrInstSrcTypeOf *instruction = ir_build_instruction(irb, scope, source_node); + instruction->value.scalar = value; + + ir_ref_instruction(value, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_set_cold(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *is_cold) { + IrInstSrcSetCold *instruction = ir_build_instruction(irb, scope, source_node); + instruction->is_cold = is_cold; + + ir_ref_instruction(is_cold, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_set_runtime_safety(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *safety_on) +{ + IrInstSrcSetRuntimeSafety *inst = ir_build_instruction(irb, scope, source_node); + inst->safety_on = safety_on; + + ir_ref_instruction(safety_on, irb->current_basic_block); + + return &inst->base; +} + +static IrInstSrc *ir_build_set_float_mode(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *mode_value) +{ + IrInstSrcSetFloatMode *instruction = ir_build_instruction(irb, scope, source_node); + instruction->mode_value = mode_value; + + ir_ref_instruction(mode_value, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_array_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *size, + IrInstSrc *sentinel, IrInstSrc *child_type) +{ + IrInstSrcArrayType *instruction = ir_build_instruction(irb, scope, source_node); + instruction->size = size; + instruction->sentinel = sentinel; + instruction->child_type = child_type; + + ir_ref_instruction(size, irb->current_basic_block); + if (sentinel != nullptr) ir_ref_instruction(sentinel, irb->current_basic_block); + ir_ref_instruction(child_type, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_anyframe_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *payload_type) +{ + IrInstSrcAnyFrameType *instruction = ir_build_instruction(irb, scope, source_node); + instruction->payload_type = payload_type; + + if (payload_type != nullptr) ir_ref_instruction(payload_type, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_slice_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *child_type, bool is_const, bool is_volatile, + IrInstSrc *sentinel, IrInstSrc *align_value, bool is_allow_zero) +{ + IrInstSrcSliceType *instruction = ir_build_instruction(irb, scope, source_node); + instruction->is_const = is_const; + instruction->is_volatile = is_volatile; + instruction->child_type = child_type; + instruction->sentinel = sentinel; + instruction->align_value = align_value; + instruction->is_allow_zero = is_allow_zero; + + if (sentinel != nullptr) ir_ref_instruction(sentinel, irb->current_basic_block); + if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block); + ir_ref_instruction(child_type, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_asm_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *asm_template, IrInstSrc **input_list, IrInstSrc **output_types, + ZigVar **output_vars, size_t return_count, bool has_side_effects, bool is_global) +{ + IrInstSrcAsm *instruction = ir_build_instruction(irb, scope, source_node); + instruction->asm_template = asm_template; + instruction->input_list = input_list; + instruction->output_types = output_types; + instruction->output_vars = output_vars; + instruction->return_count = return_count; + instruction->has_side_effects = has_side_effects; + instruction->is_global = is_global; + + assert(source_node->type == NodeTypeAsmExpr); + for (size_t i = 0; i < source_node->data.asm_expr.output_list.length; i += 1) { + IrInstSrc *output_type = output_types[i]; + if (output_type) ir_ref_instruction(output_type, irb->current_basic_block); + } + + for (size_t i = 0; i < source_node->data.asm_expr.input_list.length; i += 1) { + IrInstSrc *input_value = input_list[i]; + ir_ref_instruction(input_value, irb->current_basic_block); + } + + return &instruction->base; +} + +static IrInstGen *ir_build_asm_gen(IrAnalyze *ira, IrInst *source_instr, + 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, ZigType *return_type) +{ + IrInstGenAsm *instruction = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); + instruction->base.value->type = return_type; + instruction->asm_template = asm_template; + instruction->token_list = token_list; + instruction->token_list_len = token_list_len; + instruction->input_list = input_list; + instruction->output_types = output_types; + instruction->output_vars = output_vars; + instruction->return_count = return_count; + instruction->has_side_effects = has_side_effects; + + assert(source_instr->source_node->type == NodeTypeAsmExpr); + for (size_t i = 0; i < source_instr->source_node->data.asm_expr.output_list.length; i += 1) { + IrInstGen *output_type = output_types[i]; + if (output_type) ir_ref_inst_gen(output_type); + } + + for (size_t i = 0; i < source_instr->source_node->data.asm_expr.input_list.length; i += 1) { + IrInstGen *input_value = input_list[i]; + ir_ref_inst_gen(input_value); + } + + return &instruction->base; +} + +static IrInstSrc *ir_build_size_of(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type_value, + bool bit_size) +{ + IrInstSrcSizeOf *instruction = ir_build_instruction(irb, scope, source_node); + instruction->type_value = type_value; + instruction->bit_size = bit_size; + + ir_ref_instruction(type_value, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_test_non_null_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *value) +{ + IrInstSrcTestNonNull *instruction = ir_build_instruction(irb, scope, source_node); + instruction->value = value; + + ir_ref_instruction(value, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGen *ir_build_test_non_null_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *value) { + IrInstGenTestNonNull *inst = ir_build_inst_gen(&ira->new_irb, + source_instr->scope, source_instr->source_node); + inst->base.value->type = ira->codegen->builtin_types.entry_bool; + inst->value = value; + + ir_ref_inst_gen(value); + + return &inst->base; +} + +static IrInstSrc *ir_build_optional_unwrap_ptr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *base_ptr, bool safety_check_on) +{ + IrInstSrcOptionalUnwrapPtr *instruction = ir_build_instruction(irb, scope, source_node); + instruction->base_ptr = base_ptr; + instruction->safety_check_on = safety_check_on; + + ir_ref_instruction(base_ptr, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGen *ir_build_optional_unwrap_ptr_gen(IrAnalyze *ira, IrInst *source_instr, + IrInstGen *base_ptr, bool safety_check_on, bool initializing, ZigType *result_type) +{ + IrInstGenOptionalUnwrapPtr *inst = ir_build_inst_gen(&ira->new_irb, + source_instr->scope, source_instr->source_node); + inst->base.value->type = result_type; + inst->base_ptr = base_ptr; + inst->safety_check_on = safety_check_on; + inst->initializing = initializing; + + ir_ref_inst_gen(base_ptr); + + return &inst->base; +} + +static IrInstGen *ir_build_optional_wrap(IrAnalyze *ira, IrInst *source_instruction, ZigType *result_ty, + IrInstGen *operand, IrInstGen *result_loc) +{ + IrInstGenOptionalWrap *instruction = ir_build_inst_gen( + &ira->new_irb, source_instruction->scope, source_instruction->source_node); + instruction->base.value->type = result_ty; + instruction->operand = operand; + instruction->result_loc = result_loc; + + ir_ref_inst_gen(operand); + if (result_loc != nullptr) ir_ref_inst_gen(result_loc); + + return &instruction->base; +} + +static IrInstGen *ir_build_err_wrap_payload(IrAnalyze *ira, IrInst *source_instruction, + ZigType *result_type, IrInstGen *operand, IrInstGen *result_loc) +{ + IrInstGenErrWrapPayload *instruction = ir_build_inst_gen( + &ira->new_irb, source_instruction->scope, source_instruction->source_node); + instruction->base.value->type = result_type; + instruction->operand = operand; + instruction->result_loc = result_loc; + + ir_ref_inst_gen(operand); + if (result_loc != nullptr) ir_ref_inst_gen(result_loc); + + return &instruction->base; +} + +static IrInstGen *ir_build_err_wrap_code(IrAnalyze *ira, IrInst *source_instruction, + ZigType *result_type, IrInstGen *operand, IrInstGen *result_loc) +{ + IrInstGenErrWrapCode *instruction = ir_build_inst_gen( + &ira->new_irb, source_instruction->scope, source_instruction->source_node); + instruction->base.value->type = result_type; + instruction->operand = operand; + instruction->result_loc = result_loc; + + ir_ref_inst_gen(operand); + if (result_loc != nullptr) ir_ref_inst_gen(result_loc); + + return &instruction->base; +} + +static IrInstSrc *ir_build_clz(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type, + IrInstSrc *op) +{ + IrInstSrcClz *instruction = ir_build_instruction(irb, scope, source_node); + instruction->type = type; + instruction->op = op; + + ir_ref_instruction(type, irb->current_basic_block); + ir_ref_instruction(op, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGen *ir_build_clz_gen(IrAnalyze *ira, IrInst *source_instr, ZigType *result_type, IrInstGen *op) { + IrInstGenClz *instruction = ir_build_inst_gen(&ira->new_irb, + source_instr->scope, source_instr->source_node); + instruction->base.value->type = result_type; + instruction->op = op; + + ir_ref_inst_gen(op); + + return &instruction->base; +} + +static IrInstSrc *ir_build_ctz(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type, + IrInstSrc *op) +{ + IrInstSrcCtz *instruction = ir_build_instruction(irb, scope, source_node); + instruction->type = type; + instruction->op = op; + + ir_ref_instruction(type, irb->current_basic_block); + ir_ref_instruction(op, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGen *ir_build_ctz_gen(IrAnalyze *ira, IrInst *source_instr, ZigType *result_type, IrInstGen *op) { + IrInstGenCtz *instruction = ir_build_inst_gen(&ira->new_irb, + source_instr->scope, source_instr->source_node); + instruction->base.value->type = result_type; + instruction->op = op; + + ir_ref_inst_gen(op); + + return &instruction->base; +} + +static IrInstSrc *ir_build_pop_count(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type, + IrInstSrc *op) +{ + IrInstSrcPopCount *instruction = ir_build_instruction(irb, scope, source_node); + instruction->type = type; + instruction->op = op; + + ir_ref_instruction(type, irb->current_basic_block); + ir_ref_instruction(op, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGen *ir_build_pop_count_gen(IrAnalyze *ira, IrInst *source_instr, ZigType *result_type, + IrInstGen *op) +{ + IrInstGenPopCount *instruction = ir_build_inst_gen(&ira->new_irb, + source_instr->scope, source_instr->source_node); + instruction->base.value->type = result_type; + instruction->op = op; + + ir_ref_inst_gen(op); + + return &instruction->base; +} + +static IrInstSrc *ir_build_bswap(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type, + IrInstSrc *op) +{ + IrInstSrcBswap *instruction = ir_build_instruction(irb, scope, source_node); + instruction->type = type; + instruction->op = op; + + ir_ref_instruction(type, irb->current_basic_block); + ir_ref_instruction(op, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGen *ir_build_bswap_gen(IrAnalyze *ira, IrInst *source_instr, ZigType *op_type, + IrInstGen *op) +{ + IrInstGenBswap *instruction = ir_build_inst_gen(&ira->new_irb, + source_instr->scope, source_instr->source_node); + instruction->base.value->type = op_type; + instruction->op = op; + + ir_ref_inst_gen(op); + + return &instruction->base; +} + +static IrInstSrc *ir_build_bit_reverse(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type, + IrInstSrc *op) +{ + IrInstSrcBitReverse *instruction = ir_build_instruction(irb, scope, source_node); + instruction->type = type; + instruction->op = op; + + ir_ref_instruction(type, irb->current_basic_block); + ir_ref_instruction(op, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGen *ir_build_bit_reverse_gen(IrAnalyze *ira, IrInst *source_instr, ZigType *int_type, + IrInstGen *op) +{ + IrInstGenBitReverse *instruction = ir_build_inst_gen(&ira->new_irb, + source_instr->scope, source_instr->source_node); + instruction->base.value->type = int_type; + instruction->op = op; + + ir_ref_inst_gen(op); + + return &instruction->base; +} + +static IrInstSrcSwitchBr *ir_build_switch_br_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *target_value, IrBasicBlockSrc *else_block, size_t case_count, IrInstSrcSwitchBrCase *cases, + IrInstSrc *is_comptime, IrInstSrc *switch_prongs_void) +{ + IrInstSrcSwitchBr *instruction = ir_build_instruction(irb, scope, source_node); + instruction->base.is_noreturn = true; + instruction->target_value = target_value; + instruction->else_block = else_block; + instruction->case_count = case_count; + instruction->cases = cases; + instruction->is_comptime = is_comptime; + instruction->switch_prongs_void = switch_prongs_void; + + ir_ref_instruction(target_value, irb->current_basic_block); + ir_ref_instruction(is_comptime, irb->current_basic_block); + ir_ref_bb(else_block); + ir_ref_instruction(switch_prongs_void, irb->current_basic_block); + + for (size_t i = 0; i < case_count; i += 1) { + ir_ref_instruction(cases[i].value, irb->current_basic_block); + ir_ref_bb(cases[i].block); + } + + return instruction; +} + +static IrInstGenSwitchBr *ir_build_switch_br_gen(IrAnalyze *ira, IrInst *source_instr, + IrInstGen *target_value, IrBasicBlockGen *else_block, size_t case_count, IrInstGenSwitchBrCase *cases) +{ + IrInstGenSwitchBr *instruction = ir_build_inst_noreturn(&ira->new_irb, + source_instr->scope, source_instr->source_node); + instruction->target_value = target_value; + instruction->else_block = else_block; + instruction->case_count = case_count; + instruction->cases = cases; + + ir_ref_inst_gen(target_value); + + for (size_t i = 0; i < case_count; i += 1) { + ir_ref_inst_gen(cases[i].value); + } + + return instruction; +} + +static IrInstSrc *ir_build_switch_target(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *target_value_ptr) +{ + IrInstSrcSwitchTarget *instruction = ir_build_instruction(irb, scope, source_node); + instruction->target_value_ptr = target_value_ptr; + + ir_ref_instruction(target_value_ptr, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_switch_var(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *target_value_ptr, IrInstSrc **prongs_ptr, size_t prongs_len) +{ + IrInstSrcSwitchVar *instruction = ir_build_instruction(irb, scope, source_node); + instruction->target_value_ptr = target_value_ptr; + instruction->prongs_ptr = prongs_ptr; + instruction->prongs_len = prongs_len; + + ir_ref_instruction(target_value_ptr, irb->current_basic_block); + for (size_t i = 0; i < prongs_len; i += 1) { + ir_ref_instruction(prongs_ptr[i], irb->current_basic_block); + } + + return &instruction->base; +} + +// For this instruction the switch_br must be set later. +static IrInstSrcSwitchElseVar *ir_build_switch_else_var(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *target_value_ptr) +{ + IrInstSrcSwitchElseVar *instruction = ir_build_instruction(irb, scope, source_node); + instruction->target_value_ptr = target_value_ptr; + + ir_ref_instruction(target_value_ptr, irb->current_basic_block); + + return instruction; +} + +static IrInstGen *ir_build_union_tag(IrAnalyze *ira, IrInst *source_instr, IrInstGen *value, + ZigType *tag_type) +{ + IrInstGenUnionTag *instruction = ir_build_inst_gen(&ira->new_irb, + source_instr->scope, source_instr->source_node); + instruction->value = value; + instruction->base.value->type = tag_type; + + ir_ref_inst_gen(value); + + return &instruction->base; +} + +static IrInstSrc *ir_build_import(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *name) { + IrInstSrcImport *instruction = ir_build_instruction(irb, scope, source_node); + instruction->name = name; + + ir_ref_instruction(name, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_ref_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *value) { + IrInstSrcRef *instruction = ir_build_instruction(irb, scope, source_node); + instruction->value = value; + + ir_ref_instruction(value, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGen *ir_build_ref_gen(IrAnalyze *ira, IrInst *source_instruction, ZigType *result_type, + IrInstGen *operand, IrInstGen *result_loc) +{ + IrInstGenRef *instruction = ir_build_inst_gen(&ira->new_irb, + source_instruction->scope, source_instruction->source_node); + instruction->base.value->type = result_type; + instruction->operand = operand; + instruction->result_loc = result_loc; + + ir_ref_inst_gen(operand); + if (result_loc != nullptr) ir_ref_inst_gen(result_loc); + + return &instruction->base; +} + +static IrInstSrc *ir_build_compile_err(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *msg) { + IrInstSrcCompileErr *instruction = ir_build_instruction(irb, scope, source_node); + instruction->msg = msg; + + ir_ref_instruction(msg, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_compile_log(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + size_t msg_count, IrInstSrc **msg_list) +{ + IrInstSrcCompileLog *instruction = ir_build_instruction(irb, scope, source_node); + instruction->msg_count = msg_count; + instruction->msg_list = msg_list; + + for (size_t i = 0; i < msg_count; i += 1) { + ir_ref_instruction(msg_list[i], irb->current_basic_block); + } + + return &instruction->base; +} + +static IrInstSrc *ir_build_err_name(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *value) { + IrInstSrcErrName *instruction = ir_build_instruction(irb, scope, source_node); + instruction->value = value; + + ir_ref_instruction(value, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGen *ir_build_err_name_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *value, + ZigType *str_type) +{ + IrInstGenErrName *instruction = ir_build_inst_gen(&ira->new_irb, + source_instr->scope, source_instr->source_node); + instruction->base.value->type = str_type; + instruction->value = value; + + ir_ref_inst_gen(value); + + return &instruction->base; +} + +static IrInstSrc *ir_build_c_import(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { + IrInstSrcCImport *instruction = ir_build_instruction(irb, scope, source_node); + return &instruction->base; +} + +static IrInstSrc *ir_build_c_include(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *name) { + IrInstSrcCInclude *instruction = ir_build_instruction(irb, scope, source_node); + instruction->name = name; + + ir_ref_instruction(name, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_c_define(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *name, IrInstSrc *value) { + IrInstSrcCDefine *instruction = ir_build_instruction(irb, scope, source_node); + instruction->name = name; + instruction->value = value; + + ir_ref_instruction(name, irb->current_basic_block); + ir_ref_instruction(value, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_c_undef(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *name) { + IrInstSrcCUndef *instruction = ir_build_instruction(irb, scope, source_node); + instruction->name = name; + + ir_ref_instruction(name, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_embed_file(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *name) { + IrInstSrcEmbedFile *instruction = ir_build_instruction(irb, scope, source_node); + instruction->name = name; + + ir_ref_instruction(name, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_cmpxchg_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *type_value, IrInstSrc *ptr, IrInstSrc *cmp_value, IrInstSrc *new_value, + IrInstSrc *success_order_value, IrInstSrc *failure_order_value, bool is_weak, ResultLoc *result_loc) +{ + IrInstSrcCmpxchg *instruction = ir_build_instruction(irb, scope, source_node); + instruction->type_value = type_value; + instruction->ptr = ptr; + instruction->cmp_value = cmp_value; + instruction->new_value = new_value; + instruction->success_order_value = success_order_value; + instruction->failure_order_value = failure_order_value; + instruction->is_weak = is_weak; + instruction->result_loc = result_loc; + + ir_ref_instruction(type_value, irb->current_basic_block); + ir_ref_instruction(ptr, irb->current_basic_block); + ir_ref_instruction(cmp_value, irb->current_basic_block); + ir_ref_instruction(new_value, irb->current_basic_block); + ir_ref_instruction(success_order_value, irb->current_basic_block); + ir_ref_instruction(failure_order_value, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGen *ir_build_cmpxchg_gen(IrAnalyze *ira, IrInst *source_instruction, ZigType *result_type, + IrInstGen *ptr, IrInstGen *cmp_value, IrInstGen *new_value, + AtomicOrder success_order, AtomicOrder failure_order, bool is_weak, IrInstGen *result_loc) +{ + IrInstGenCmpxchg *instruction = ir_build_inst_gen(&ira->new_irb, + source_instruction->scope, source_instruction->source_node); + instruction->base.value->type = result_type; + instruction->ptr = ptr; + instruction->cmp_value = cmp_value; + instruction->new_value = new_value; + instruction->success_order = success_order; + instruction->failure_order = failure_order; + instruction->is_weak = is_weak; + instruction->result_loc = result_loc; + + ir_ref_inst_gen(ptr); + ir_ref_inst_gen(cmp_value); + ir_ref_inst_gen(new_value); + if (result_loc != nullptr) ir_ref_inst_gen(result_loc); + + return &instruction->base; +} + +static IrInstSrc *ir_build_fence(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *order) { + IrInstSrcFence *instruction = ir_build_instruction(irb, scope, source_node); + instruction->order = order; + + ir_ref_instruction(order, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGen *ir_build_fence_gen(IrAnalyze *ira, IrInst *source_instr, AtomicOrder order) { + IrInstGenFence *instruction = ir_build_inst_void(&ira->new_irb, + source_instr->scope, source_instr->source_node); + instruction->order = order; + + return &instruction->base; +} + +static IrInstSrc *ir_build_truncate(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *dest_type, IrInstSrc *target) +{ + IrInstSrcTruncate *instruction = ir_build_instruction(irb, scope, source_node); + instruction->dest_type = dest_type; + instruction->target = target; + + ir_ref_instruction(dest_type, irb->current_basic_block); + ir_ref_instruction(target, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGen *ir_build_truncate_gen(IrAnalyze *ira, IrInst *source_instr, ZigType *dest_type, + IrInstGen *target) +{ + IrInstGenTruncate *instruction = ir_build_inst_gen(&ira->new_irb, + source_instr->scope, source_instr->source_node); + instruction->base.value->type = dest_type; + instruction->target = target; + + ir_ref_inst_gen(target); + + return &instruction->base; +} + +static IrInstSrc *ir_build_int_cast(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *dest_type, + IrInstSrc *target) +{ + IrInstSrcIntCast *instruction = ir_build_instruction(irb, scope, source_node); + instruction->dest_type = dest_type; + instruction->target = target; + + ir_ref_instruction(dest_type, irb->current_basic_block); + ir_ref_instruction(target, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_float_cast(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *dest_type, + IrInstSrc *target) +{ + IrInstSrcFloatCast *instruction = ir_build_instruction(irb, scope, source_node); + instruction->dest_type = dest_type; + instruction->target = target; + + ir_ref_instruction(dest_type, irb->current_basic_block); + ir_ref_instruction(target, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_err_set_cast(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *dest_type, IrInstSrc *target) +{ + IrInstSrcErrSetCast *instruction = ir_build_instruction(irb, scope, source_node); + instruction->dest_type = dest_type; + instruction->target = target; + + ir_ref_instruction(dest_type, irb->current_basic_block); + ir_ref_instruction(target, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_int_to_float(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *dest_type, IrInstSrc *target) +{ + IrInstSrcIntToFloat *instruction = ir_build_instruction(irb, scope, source_node); + instruction->dest_type = dest_type; + instruction->target = target; + + ir_ref_instruction(dest_type, irb->current_basic_block); + ir_ref_instruction(target, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_float_to_int(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *dest_type, IrInstSrc *target) +{ + IrInstSrcFloatToInt *instruction = ir_build_instruction(irb, scope, source_node); + instruction->dest_type = dest_type; + instruction->target = target; + + ir_ref_instruction(dest_type, irb->current_basic_block); + ir_ref_instruction(target, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_bool_to_int(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *target) { + IrInstSrcBoolToInt *instruction = ir_build_instruction(irb, scope, source_node); + instruction->target = target; + + ir_ref_instruction(target, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_vector_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *len, + IrInstSrc *elem_type) +{ + IrInstSrcVectorType *instruction = ir_build_instruction(irb, scope, source_node); + instruction->len = len; + instruction->elem_type = elem_type; + + ir_ref_instruction(len, irb->current_basic_block); + ir_ref_instruction(elem_type, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_shuffle_vector(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *scalar_type, IrInstSrc *a, IrInstSrc *b, IrInstSrc *mask) +{ + IrInstSrcShuffleVector *instruction = ir_build_instruction(irb, scope, source_node); + instruction->scalar_type = scalar_type; + instruction->a = a; + instruction->b = b; + instruction->mask = mask; + + if (scalar_type != nullptr) ir_ref_instruction(scalar_type, irb->current_basic_block); + ir_ref_instruction(a, irb->current_basic_block); + ir_ref_instruction(b, irb->current_basic_block); + ir_ref_instruction(mask, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGen *ir_build_shuffle_vector_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node, + ZigType *result_type, IrInstGen *a, IrInstGen *b, IrInstGen *mask) +{ + IrInstGenShuffleVector *inst = ir_build_inst_gen(&ira->new_irb, scope, source_node); + inst->base.value->type = result_type; + inst->a = a; + inst->b = b; + inst->mask = mask; + + ir_ref_inst_gen(a); + ir_ref_inst_gen(b); + ir_ref_inst_gen(mask); + + return &inst->base; +} + +static IrInstSrc *ir_build_splat_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *len, IrInstSrc *scalar) +{ + IrInstSrcSplat *instruction = ir_build_instruction(irb, scope, source_node); + instruction->len = len; + instruction->scalar = scalar; + + ir_ref_instruction(len, irb->current_basic_block); + ir_ref_instruction(scalar, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGen *ir_build_splat_gen(IrAnalyze *ira, IrInst *source_instruction, ZigType *result_type, + IrInstGen *scalar) +{ + IrInstGenSplat *instruction = ir_build_inst_gen( + &ira->new_irb, source_instruction->scope, source_instruction->source_node); + instruction->base.value->type = result_type; + instruction->scalar = scalar; + + ir_ref_inst_gen(scalar); + + return &instruction->base; +} + +static IrInstSrc *ir_build_bool_not(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *value) { + IrInstSrcBoolNot *instruction = ir_build_instruction(irb, scope, source_node); + instruction->value = value; + + ir_ref_instruction(value, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGen *ir_build_bool_not_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *value) { + IrInstGenBoolNot *instruction = ir_build_inst_gen(&ira->new_irb, + source_instr->scope, source_instr->source_node); + instruction->base.value->type = ira->codegen->builtin_types.entry_bool; + instruction->value = value; + + ir_ref_inst_gen(value); + + return &instruction->base; +} + +static IrInstSrc *ir_build_memset_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *dest_ptr, IrInstSrc *byte, IrInstSrc *count) +{ + IrInstSrcMemset *instruction = ir_build_instruction(irb, scope, source_node); + instruction->dest_ptr = dest_ptr; + instruction->byte = byte; + instruction->count = count; + + ir_ref_instruction(dest_ptr, irb->current_basic_block); + ir_ref_instruction(byte, irb->current_basic_block); + ir_ref_instruction(count, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGen *ir_build_memset_gen(IrAnalyze *ira, IrInst *source_instr, + IrInstGen *dest_ptr, IrInstGen *byte, IrInstGen *count) +{ + IrInstGenMemset *instruction = ir_build_inst_void(&ira->new_irb, + source_instr->scope, source_instr->source_node); + instruction->dest_ptr = dest_ptr; + instruction->byte = byte; + instruction->count = count; + + ir_ref_inst_gen(dest_ptr); + ir_ref_inst_gen(byte); + ir_ref_inst_gen(count); + + return &instruction->base; +} + +static IrInstSrc *ir_build_memcpy_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *dest_ptr, IrInstSrc *src_ptr, IrInstSrc *count) +{ + IrInstSrcMemcpy *instruction = ir_build_instruction(irb, scope, source_node); + instruction->dest_ptr = dest_ptr; + instruction->src_ptr = src_ptr; + instruction->count = count; + + ir_ref_instruction(dest_ptr, irb->current_basic_block); + ir_ref_instruction(src_ptr, irb->current_basic_block); + ir_ref_instruction(count, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGen *ir_build_memcpy_gen(IrAnalyze *ira, IrInst *source_instr, + IrInstGen *dest_ptr, IrInstGen *src_ptr, IrInstGen *count) +{ + IrInstGenMemcpy *instruction = ir_build_inst_void(&ira->new_irb, + source_instr->scope, source_instr->source_node); + instruction->dest_ptr = dest_ptr; + instruction->src_ptr = src_ptr; + instruction->count = count; + + ir_ref_inst_gen(dest_ptr); + ir_ref_inst_gen(src_ptr); + ir_ref_inst_gen(count); + + return &instruction->base; +} + +static IrInstSrc *ir_build_slice_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *ptr, IrInstSrc *start, IrInstSrc *end, IrInstSrc *sentinel, + bool safety_check_on, ResultLoc *result_loc) +{ + IrInstSrcSlice *instruction = ir_build_instruction(irb, scope, source_node); + instruction->ptr = ptr; + instruction->start = start; + instruction->end = end; + instruction->sentinel = sentinel; + instruction->safety_check_on = safety_check_on; + instruction->result_loc = result_loc; + + ir_ref_instruction(ptr, irb->current_basic_block); + ir_ref_instruction(start, irb->current_basic_block); + if (end) ir_ref_instruction(end, irb->current_basic_block); + if (sentinel) ir_ref_instruction(sentinel, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGen *ir_build_slice_gen(IrAnalyze *ira, IrInst *source_instruction, ZigType *slice_type, + IrInstGen *ptr, IrInstGen *start, IrInstGen *end, bool safety_check_on, IrInstGen *result_loc, + ZigValue *sentinel) +{ + IrInstGenSlice *instruction = ir_build_inst_gen( + &ira->new_irb, source_instruction->scope, source_instruction->source_node); + instruction->base.value->type = slice_type; + instruction->ptr = ptr; + instruction->start = start; + instruction->end = end; + instruction->safety_check_on = safety_check_on; + instruction->result_loc = result_loc; + instruction->sentinel = sentinel; + + ir_ref_inst_gen(ptr); + ir_ref_inst_gen(start); + if (end != nullptr) ir_ref_inst_gen(end); + if (result_loc != nullptr) ir_ref_inst_gen(result_loc); + + return &instruction->base; +} + +static IrInstSrc *ir_build_breakpoint(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { + IrInstSrcBreakpoint *instruction = ir_build_instruction(irb, scope, source_node); + return &instruction->base; +} + +static IrInstGen *ir_build_breakpoint_gen(IrAnalyze *ira, IrInst *source_instr) { + IrInstGenBreakpoint *instruction = ir_build_inst_void(&ira->new_irb, + source_instr->scope, source_instr->source_node); + return &instruction->base; +} + +static IrInstSrc *ir_build_return_address_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { + IrInstSrcReturnAddress *instruction = ir_build_instruction(irb, scope, source_node); + return &instruction->base; +} + +static IrInstGen *ir_build_return_address_gen(IrAnalyze *ira, IrInst *source_instr) { + IrInstGenReturnAddress *inst = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); + inst->base.value->type = ira->codegen->builtin_types.entry_usize; + return &inst->base; +} + +static IrInstSrc *ir_build_frame_address_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { + IrInstSrcFrameAddress *inst = ir_build_instruction(irb, scope, source_node); + return &inst->base; +} + +static IrInstGen *ir_build_frame_address_gen(IrAnalyze *ira, IrInst *source_instr) { + IrInstGenFrameAddress *inst = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); + inst->base.value->type = ira->codegen->builtin_types.entry_usize; + return &inst->base; +} + +static IrInstSrc *ir_build_handle_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { + IrInstSrcFrameHandle *inst = ir_build_instruction(irb, scope, source_node); + return &inst->base; +} + +static IrInstGen *ir_build_handle_gen(IrAnalyze *ira, IrInst *source_instr, ZigType *ty) { + IrInstGenFrameHandle *inst = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); + inst->base.value->type = ty; + return &inst->base; +} + +static IrInstSrc *ir_build_frame_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *fn) { + IrInstSrcFrameType *inst = ir_build_instruction(irb, scope, source_node); + inst->fn = fn; + + ir_ref_instruction(fn, irb->current_basic_block); + + return &inst->base; +} + +static IrInstSrc *ir_build_frame_size_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *fn) { + IrInstSrcFrameSize *inst = ir_build_instruction(irb, scope, source_node); + inst->fn = fn; + + ir_ref_instruction(fn, irb->current_basic_block); + + return &inst->base; +} + +static IrInstGen *ir_build_frame_size_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *fn) +{ + IrInstGenFrameSize *inst = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); + inst->base.value->type = ira->codegen->builtin_types.entry_usize; + inst->fn = fn; + + ir_ref_inst_gen(fn); + + return &inst->base; +} + +static IrInstSrc *ir_build_overflow_op_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrOverflowOp op, IrInstSrc *type_value, IrInstSrc *op1, IrInstSrc *op2, IrInstSrc *result_ptr) +{ + IrInstSrcOverflowOp *instruction = ir_build_instruction(irb, scope, source_node); + instruction->op = op; + instruction->type_value = type_value; + instruction->op1 = op1; + instruction->op2 = op2; + instruction->result_ptr = result_ptr; + + ir_ref_instruction(type_value, irb->current_basic_block); + ir_ref_instruction(op1, irb->current_basic_block); + ir_ref_instruction(op2, irb->current_basic_block); + ir_ref_instruction(result_ptr, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGen *ir_build_overflow_op_gen(IrAnalyze *ira, IrInst *source_instr, + IrOverflowOp op, IrInstGen *op1, IrInstGen *op2, IrInstGen *result_ptr, + ZigType *result_ptr_type) +{ + IrInstGenOverflowOp *instruction = ir_build_inst_gen(&ira->new_irb, + source_instr->scope, source_instr->source_node); + instruction->base.value->type = ira->codegen->builtin_types.entry_bool; + instruction->op = op; + instruction->op1 = op1; + instruction->op2 = op2; + instruction->result_ptr = result_ptr; + instruction->result_ptr_type = result_ptr_type; + + ir_ref_inst_gen(op1); + ir_ref_inst_gen(op2); + ir_ref_inst_gen(result_ptr); + + return &instruction->base; +} + +static IrInstSrc *ir_build_float_op_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *operand, + BuiltinFnId fn_id) +{ + IrInstSrcFloatOp *instruction = ir_build_instruction(irb, scope, source_node); + instruction->operand = operand; + instruction->fn_id = fn_id; + + ir_ref_instruction(operand, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGen *ir_build_float_op_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *operand, + BuiltinFnId fn_id, ZigType *operand_type) +{ + IrInstGenFloatOp *instruction = ir_build_inst_gen(&ira->new_irb, + source_instr->scope, source_instr->source_node); + instruction->base.value->type = operand_type; + instruction->operand = operand; + instruction->fn_id = fn_id; + + ir_ref_inst_gen(operand); + + return &instruction->base; +} + +static IrInstSrc *ir_build_mul_add_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *type_value, IrInstSrc *op1, IrInstSrc *op2, IrInstSrc *op3) +{ + IrInstSrcMulAdd *instruction = ir_build_instruction(irb, scope, source_node); + instruction->type_value = type_value; + instruction->op1 = op1; + instruction->op2 = op2; + instruction->op3 = op3; + + ir_ref_instruction(type_value, irb->current_basic_block); + ir_ref_instruction(op1, irb->current_basic_block); + ir_ref_instruction(op2, irb->current_basic_block); + ir_ref_instruction(op3, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGen *ir_build_mul_add_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *op1, IrInstGen *op2, + IrInstGen *op3, ZigType *expr_type) +{ + IrInstGenMulAdd *instruction = ir_build_inst_gen(&ira->new_irb, + source_instr->scope, source_instr->source_node); + instruction->base.value->type = expr_type; + instruction->op1 = op1; + instruction->op2 = op2; + instruction->op3 = op3; + + ir_ref_inst_gen(op1); + ir_ref_inst_gen(op2); + ir_ref_inst_gen(op3); + + return &instruction->base; +} + +static IrInstSrc *ir_build_align_of(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type_value) { + IrInstSrcAlignOf *instruction = ir_build_instruction(irb, scope, source_node); + instruction->type_value = type_value; + + ir_ref_instruction(type_value, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_test_err_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *base_ptr, bool resolve_err_set, bool base_ptr_is_payload) +{ + IrInstSrcTestErr *instruction = ir_build_instruction(irb, scope, source_node); + instruction->base_ptr = base_ptr; + instruction->resolve_err_set = resolve_err_set; + instruction->base_ptr_is_payload = base_ptr_is_payload; + + ir_ref_instruction(base_ptr, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGen *ir_build_test_err_gen(IrAnalyze *ira, IrInst *source_instruction, IrInstGen *err_union) { + IrInstGenTestErr *instruction = ir_build_inst_gen( + &ira->new_irb, source_instruction->scope, source_instruction->source_node); + instruction->base.value->type = ira->codegen->builtin_types.entry_bool; + instruction->err_union = err_union; + + ir_ref_inst_gen(err_union); + + return &instruction->base; +} + +static IrInstSrc *ir_build_unwrap_err_code_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *err_union_ptr) +{ + IrInstSrcUnwrapErrCode *inst = ir_build_instruction(irb, scope, source_node); + inst->err_union_ptr = err_union_ptr; + + ir_ref_instruction(err_union_ptr, irb->current_basic_block); + + return &inst->base; +} + +static IrInstGen *ir_build_unwrap_err_code_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node, + IrInstGen *err_union_ptr, ZigType *result_type) +{ + IrInstGenUnwrapErrCode *inst = ir_build_inst_gen(&ira->new_irb, scope, source_node); + inst->base.value->type = result_type; + inst->err_union_ptr = err_union_ptr; + + ir_ref_inst_gen(err_union_ptr); + + return &inst->base; +} + +static IrInstSrc *ir_build_unwrap_err_payload_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *value, bool safety_check_on, bool initializing) +{ + IrInstSrcUnwrapErrPayload *inst = ir_build_instruction(irb, scope, source_node); + inst->value = value; + inst->safety_check_on = safety_check_on; + inst->initializing = initializing; + + ir_ref_instruction(value, irb->current_basic_block); + + return &inst->base; +} + +static IrInstGen *ir_build_unwrap_err_payload_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node, + IrInstGen *value, bool safety_check_on, bool initializing, ZigType *result_type) +{ + IrInstGenUnwrapErrPayload *inst = ir_build_inst_gen(&ira->new_irb, scope, source_node); + inst->base.value->type = result_type; + inst->value = value; + inst->safety_check_on = safety_check_on; + inst->initializing = initializing; + + ir_ref_inst_gen(value); + + return &inst->base; +} + +static IrInstSrc *ir_build_fn_proto(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc **param_types, IrInstSrc *align_value, IrInstSrc *callconv_value, + IrInstSrc *return_type, bool is_var_args) +{ + IrInstSrcFnProto *instruction = ir_build_instruction(irb, scope, source_node); + instruction->param_types = param_types; + instruction->align_value = align_value; + instruction->callconv_value = callconv_value; + instruction->return_type = return_type; + instruction->is_var_args = is_var_args; + + assert(source_node->type == NodeTypeFnProto); + size_t param_count = source_node->data.fn_proto.params.length; + if (is_var_args) param_count -= 1; + for (size_t i = 0; i < param_count; i += 1) { + if (param_types[i] != nullptr) ir_ref_instruction(param_types[i], irb->current_basic_block); + } + if (align_value != nullptr) ir_ref_instruction(align_value, irb->current_basic_block); + if (callconv_value != nullptr) ir_ref_instruction(callconv_value, irb->current_basic_block); + ir_ref_instruction(return_type, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_test_comptime(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *value) { + IrInstSrcTestComptime *instruction = ir_build_instruction(irb, scope, source_node); + instruction->value = value; + + ir_ref_instruction(value, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_ptr_cast_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *dest_type, IrInstSrc *ptr, bool safety_check_on) +{ + IrInstSrcPtrCast *instruction = ir_build_instruction( + irb, scope, source_node); + instruction->dest_type = dest_type; + instruction->ptr = ptr; + instruction->safety_check_on = safety_check_on; + + ir_ref_instruction(dest_type, irb->current_basic_block); + ir_ref_instruction(ptr, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGen *ir_build_ptr_cast_gen(IrAnalyze *ira, IrInst *source_instruction, + ZigType *ptr_type, IrInstGen *ptr, bool safety_check_on) +{ + IrInstGenPtrCast *instruction = ir_build_inst_gen( + &ira->new_irb, source_instruction->scope, source_instruction->source_node); + instruction->base.value->type = ptr_type; + instruction->ptr = ptr; + instruction->safety_check_on = safety_check_on; + + ir_ref_inst_gen(ptr); + + return &instruction->base; +} + +static IrInstSrc *ir_build_implicit_cast(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *operand, ResultLocCast *result_loc_cast) +{ + IrInstSrcImplicitCast *instruction = ir_build_instruction(irb, scope, source_node); + instruction->operand = operand; + instruction->result_loc_cast = result_loc_cast; + + ir_ref_instruction(operand, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_bit_cast_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *operand, ResultLocBitCast *result_loc_bit_cast) +{ + IrInstSrcBitCast *instruction = ir_build_instruction(irb, scope, source_node); + instruction->operand = operand; + instruction->result_loc_bit_cast = result_loc_bit_cast; + + ir_ref_instruction(operand, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGen *ir_build_bit_cast_gen(IrAnalyze *ira, IrInst *source_instruction, + IrInstGen *operand, ZigType *ty) +{ + IrInstGenBitCast *instruction = ir_build_inst_gen( + &ira->new_irb, source_instruction->scope, source_instruction->source_node); + instruction->base.value->type = ty; + instruction->operand = operand; + + ir_ref_inst_gen(operand); + + return &instruction->base; +} + +static IrInstGen *ir_build_widen_or_shorten(IrAnalyze *ira, Scope *scope, AstNode *source_node, IrInstGen *target, + ZigType *result_type) +{ + IrInstGenWidenOrShorten *inst = ir_build_inst_gen(&ira->new_irb, scope, source_node); + inst->base.value->type = result_type; + inst->target = target; + + ir_ref_inst_gen(target); + + return &inst->base; +} + +static IrInstSrc *ir_build_int_to_ptr_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *dest_type, IrInstSrc *target) +{ + IrInstSrcIntToPtr *instruction = ir_build_instruction(irb, scope, source_node); + instruction->dest_type = dest_type; + instruction->target = target; + + ir_ref_instruction(dest_type, irb->current_basic_block); + ir_ref_instruction(target, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGen *ir_build_int_to_ptr_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node, + IrInstGen *target, ZigType *ptr_type) +{ + IrInstGenIntToPtr *instruction = ir_build_inst_gen(&ira->new_irb, scope, source_node); + instruction->base.value->type = ptr_type; + instruction->target = target; + + ir_ref_inst_gen(target); + + return &instruction->base; +} + +static IrInstSrc *ir_build_ptr_to_int_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *target) +{ + IrInstSrcPtrToInt *inst = ir_build_instruction(irb, scope, source_node); + inst->target = target; + + ir_ref_instruction(target, irb->current_basic_block); + + return &inst->base; +} + +static IrInstGen *ir_build_ptr_to_int_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *target) { + IrInstGenPtrToInt *inst = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); + inst->base.value->type = ira->codegen->builtin_types.entry_usize; + inst->target = target; + + ir_ref_inst_gen(target); + + return &inst->base; +} + +static IrInstSrc *ir_build_int_to_enum_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *dest_type, IrInstSrc *target) +{ + IrInstSrcIntToEnum *instruction = ir_build_instruction(irb, scope, source_node); + instruction->dest_type = dest_type; + instruction->target = target; + + if (dest_type) ir_ref_instruction(dest_type, irb->current_basic_block); + ir_ref_instruction(target, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGen *ir_build_int_to_enum_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node, + ZigType *dest_type, IrInstGen *target) +{ + IrInstGenIntToEnum *instruction = ir_build_inst_gen(&ira->new_irb, scope, source_node); + instruction->base.value->type = dest_type; + instruction->target = target; + + ir_ref_inst_gen(target); + + return &instruction->base; +} + +static IrInstSrc *ir_build_enum_to_int(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *target) +{ + IrInstSrcEnumToInt *instruction = ir_build_instruction( + irb, scope, source_node); + instruction->target = target; + + ir_ref_instruction(target, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_int_to_err_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *target) +{ + IrInstSrcIntToErr *instruction = ir_build_instruction(irb, scope, source_node); + instruction->target = target; + + ir_ref_instruction(target, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGen *ir_build_int_to_err_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node, IrInstGen *target, + ZigType *wanted_type) +{ + IrInstGenIntToErr *instruction = ir_build_inst_gen(&ira->new_irb, scope, source_node); + instruction->base.value->type = wanted_type; + instruction->target = target; + + ir_ref_inst_gen(target); + + return &instruction->base; +} + +static IrInstSrc *ir_build_err_to_int_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *target) +{ + IrInstSrcErrToInt *instruction = ir_build_instruction( + irb, scope, source_node); + instruction->target = target; + + ir_ref_instruction(target, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGen *ir_build_err_to_int_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node, IrInstGen *target, + ZigType *wanted_type) +{ + IrInstGenErrToInt *instruction = ir_build_inst_gen(&ira->new_irb, scope, source_node); + instruction->base.value->type = wanted_type; + instruction->target = target; + + ir_ref_inst_gen(target); + + return &instruction->base; +} + +static IrInstSrc *ir_build_check_switch_prongs(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *target_value, IrInstSrcCheckSwitchProngsRange *ranges, size_t range_count, + AstNode* else_prong, bool have_underscore_prong) +{ + IrInstSrcCheckSwitchProngs *instruction = ir_build_instruction( + irb, scope, source_node); + instruction->target_value = target_value; + instruction->ranges = ranges; + instruction->range_count = range_count; + instruction->else_prong = else_prong; + instruction->have_underscore_prong = have_underscore_prong; + + ir_ref_instruction(target_value, irb->current_basic_block); + for (size_t i = 0; i < range_count; i += 1) { + ir_ref_instruction(ranges[i].start, irb->current_basic_block); + ir_ref_instruction(ranges[i].end, irb->current_basic_block); + } + + return &instruction->base; +} + +static IrInstSrc *ir_build_check_statement_is_void(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc* statement_value) +{ + IrInstSrcCheckStatementIsVoid *instruction = ir_build_instruction( + irb, scope, source_node); + instruction->statement_value = statement_value; + + ir_ref_instruction(statement_value, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_type_name(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *type_value) +{ + IrInstSrcTypeName *instruction = ir_build_instruction(irb, scope, source_node); + instruction->type_value = type_value; + + ir_ref_instruction(type_value, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_decl_ref(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, Tld *tld, LVal lval) { + IrInstSrcDeclRef *instruction = ir_build_instruction(irb, scope, source_node); + instruction->tld = tld; + instruction->lval = lval; + + return &instruction->base; +} + +static IrInstSrc *ir_build_panic_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *msg) { + IrInstSrcPanic *instruction = ir_build_instruction(irb, scope, source_node); + instruction->base.is_noreturn = true; + instruction->msg = msg; + + ir_ref_instruction(msg, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGen *ir_build_panic_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *msg) { + IrInstGenPanic *instruction = ir_build_inst_noreturn(&ira->new_irb, + source_instr->scope, source_instr->source_node); + instruction->msg = msg; + + ir_ref_inst_gen(msg); + + return &instruction->base; +} + +static IrInstSrc *ir_build_tag_name_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *target) { + IrInstSrcTagName *instruction = ir_build_instruction(irb, scope, source_node); + instruction->target = target; + + ir_ref_instruction(target, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGen *ir_build_tag_name_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *target, + ZigType *result_type) +{ + IrInstGenTagName *instruction = ir_build_inst_gen(&ira->new_irb, + source_instr->scope, source_instr->source_node); + instruction->base.value->type = result_type; + instruction->target = target; + + ir_ref_inst_gen(target); + + return &instruction->base; +} + +static IrInstSrc *ir_build_tag_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *target) +{ + IrInstSrcTagType *instruction = ir_build_instruction(irb, scope, source_node); + instruction->target = target; + + ir_ref_instruction(target, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_field_parent_ptr_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *type_value, IrInstSrc *field_name, IrInstSrc *field_ptr) +{ + IrInstSrcFieldParentPtr *inst = ir_build_instruction( + irb, scope, source_node); + inst->type_value = type_value; + inst->field_name = field_name; + inst->field_ptr = field_ptr; + + ir_ref_instruction(type_value, irb->current_basic_block); + ir_ref_instruction(field_name, irb->current_basic_block); + ir_ref_instruction(field_ptr, irb->current_basic_block); + + return &inst->base; +} + +static IrInstGen *ir_build_field_parent_ptr_gen(IrAnalyze *ira, IrInst *source_instr, + IrInstGen *field_ptr, TypeStructField *field, ZigType *result_type) +{ + IrInstGenFieldParentPtr *inst = ir_build_inst_gen(&ira->new_irb, + source_instr->scope, source_instr->source_node); + inst->base.value->type = result_type; + inst->field_ptr = field_ptr; + inst->field = field; + + ir_ref_inst_gen(field_ptr); + + return &inst->base; +} + +static IrInstSrc *ir_build_byte_offset_of(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *type_value, IrInstSrc *field_name) +{ + IrInstSrcByteOffsetOf *instruction = ir_build_instruction(irb, scope, source_node); + instruction->type_value = type_value; + instruction->field_name = field_name; + + ir_ref_instruction(type_value, irb->current_basic_block); + ir_ref_instruction(field_name, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_bit_offset_of(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *type_value, IrInstSrc *field_name) +{ + IrInstSrcBitOffsetOf *instruction = ir_build_instruction(irb, scope, source_node); + instruction->type_value = type_value; + instruction->field_name = field_name; + + ir_ref_instruction(type_value, irb->current_basic_block); + ir_ref_instruction(field_name, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_type_info(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type_value) { + IrInstSrcTypeInfo *instruction = ir_build_instruction(irb, scope, source_node); + instruction->type_value = type_value; + + ir_ref_instruction(type_value, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *type_info) { + IrInstSrcType *instruction = ir_build_instruction(irb, scope, source_node); + instruction->type_info = type_info; + + ir_ref_instruction(type_info, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_set_eval_branch_quota(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *new_quota) +{ + IrInstSrcSetEvalBranchQuota *instruction = ir_build_instruction(irb, scope, source_node); + instruction->new_quota = new_quota; + + ir_ref_instruction(new_quota, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_align_cast_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *align_bytes, IrInstSrc *target) +{ + IrInstSrcAlignCast *instruction = ir_build_instruction(irb, scope, source_node); + instruction->align_bytes = align_bytes; + instruction->target = target; + + ir_ref_instruction(align_bytes, irb->current_basic_block); + ir_ref_instruction(target, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGen *ir_build_align_cast_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node, IrInstGen *target, + ZigType *result_type) +{ + IrInstGenAlignCast *instruction = ir_build_inst_gen(&ira->new_irb, scope, source_node); + instruction->base.value->type = result_type; + instruction->target = target; + + ir_ref_inst_gen(target); + + return &instruction->base; +} + +static IrInstSrc *ir_build_resolve_result(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + ResultLoc *result_loc, IrInstSrc *ty) +{ + IrInstSrcResolveResult *instruction = ir_build_instruction(irb, scope, source_node); + instruction->result_loc = result_loc; + instruction->ty = ty; + + if (ty != nullptr) ir_ref_instruction(ty, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_reset_result(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + ResultLoc *result_loc) +{ + IrInstSrcResetResult *instruction = ir_build_instruction(irb, scope, source_node); + instruction->result_loc = result_loc; + instruction->base.is_gen = true; + + return &instruction->base; +} + +static IrInstSrc *ir_build_set_align_stack(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *align_bytes) +{ + IrInstSrcSetAlignStack *instruction = ir_build_instruction(irb, scope, source_node); + instruction->align_bytes = align_bytes; + + ir_ref_instruction(align_bytes, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_arg_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *fn_type, IrInstSrc *arg_index, bool allow_var) +{ + IrInstSrcArgType *instruction = ir_build_instruction(irb, scope, source_node); + instruction->fn_type = fn_type; + instruction->arg_index = arg_index; + instruction->allow_var = allow_var; + + ir_ref_instruction(fn_type, irb->current_basic_block); + ir_ref_instruction(arg_index, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_error_return_trace_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstErrorReturnTraceOptional optional) +{ + IrInstSrcErrorReturnTrace *inst = ir_build_instruction(irb, scope, source_node); + inst->optional = optional; + + return &inst->base; +} + +static IrInstGen *ir_build_error_return_trace_gen(IrAnalyze *ira, Scope *scope, AstNode *source_node, + IrInstErrorReturnTraceOptional optional, ZigType *result_type) +{ + IrInstGenErrorReturnTrace *inst = ir_build_inst_gen(&ira->new_irb, scope, source_node); + inst->base.value->type = result_type; + inst->optional = optional; + + return &inst->base; +} + +static IrInstSrc *ir_build_error_union(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *err_set, IrInstSrc *payload) +{ + IrInstSrcErrorUnion *instruction = ir_build_instruction(irb, scope, source_node); + instruction->err_set = err_set; + instruction->payload = payload; + + ir_ref_instruction(err_set, irb->current_basic_block); + ir_ref_instruction(payload, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_atomic_rmw_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *operand_type, IrInstSrc *ptr, IrInstSrc *op, IrInstSrc *operand, + IrInstSrc *ordering) +{ + IrInstSrcAtomicRmw *instruction = ir_build_instruction(irb, scope, source_node); + instruction->operand_type = operand_type; + instruction->ptr = ptr; + instruction->op = op; + instruction->operand = operand; + instruction->ordering = ordering; + + ir_ref_instruction(operand_type, irb->current_basic_block); + ir_ref_instruction(ptr, irb->current_basic_block); + ir_ref_instruction(op, irb->current_basic_block); + ir_ref_instruction(operand, irb->current_basic_block); + ir_ref_instruction(ordering, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGen *ir_build_atomic_rmw_gen(IrAnalyze *ira, IrInst *source_instr, + IrInstGen *ptr, IrInstGen *operand, AtomicRmwOp op, AtomicOrder ordering, ZigType *operand_type) +{ + IrInstGenAtomicRmw *instruction = ir_build_inst_gen(&ira->new_irb, source_instr->scope, source_instr->source_node); + instruction->base.value->type = operand_type; + instruction->ptr = ptr; + instruction->op = op; + instruction->operand = operand; + instruction->ordering = ordering; + + ir_ref_inst_gen(ptr); + ir_ref_inst_gen(operand); + + return &instruction->base; +} + +static IrInstSrc *ir_build_atomic_load_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *operand_type, IrInstSrc *ptr, IrInstSrc *ordering) +{ + IrInstSrcAtomicLoad *instruction = ir_build_instruction(irb, scope, source_node); + instruction->operand_type = operand_type; + instruction->ptr = ptr; + instruction->ordering = ordering; + + ir_ref_instruction(operand_type, irb->current_basic_block); + ir_ref_instruction(ptr, irb->current_basic_block); + ir_ref_instruction(ordering, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGen *ir_build_atomic_load_gen(IrAnalyze *ira, IrInst *source_instr, + IrInstGen *ptr, AtomicOrder ordering, ZigType *operand_type) +{ + IrInstGenAtomicLoad *instruction = ir_build_inst_gen(&ira->new_irb, + source_instr->scope, source_instr->source_node); + instruction->base.value->type = operand_type; + instruction->ptr = ptr; + instruction->ordering = ordering; + + ir_ref_inst_gen(ptr); + + return &instruction->base; +} + +static IrInstSrc *ir_build_atomic_store_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *operand_type, IrInstSrc *ptr, IrInstSrc *value, IrInstSrc *ordering) +{ + IrInstSrcAtomicStore *instruction = ir_build_instruction(irb, scope, source_node); + instruction->operand_type = operand_type; + instruction->ptr = ptr; + instruction->value = value; + instruction->ordering = ordering; + + ir_ref_instruction(operand_type, irb->current_basic_block); + ir_ref_instruction(ptr, irb->current_basic_block); + ir_ref_instruction(value, irb->current_basic_block); + ir_ref_instruction(ordering, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGen *ir_build_atomic_store_gen(IrAnalyze *ira, IrInst *source_instr, + IrInstGen *ptr, IrInstGen *value, AtomicOrder ordering) +{ + IrInstGenAtomicStore *instruction = ir_build_inst_void(&ira->new_irb, + source_instr->scope, source_instr->source_node); + instruction->ptr = ptr; + instruction->value = value; + instruction->ordering = ordering; + + ir_ref_inst_gen(ptr); + ir_ref_inst_gen(value); + + return &instruction->base; +} + +static IrInstSrc *ir_build_save_err_ret_addr_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { + IrInstSrcSaveErrRetAddr *inst = ir_build_instruction(irb, scope, source_node); + return &inst->base; +} + +static IrInstGen *ir_build_save_err_ret_addr_gen(IrAnalyze *ira, IrInst *source_instr) { + IrInstGenSaveErrRetAddr *inst = ir_build_inst_void(&ira->new_irb, + source_instr->scope, source_instr->source_node); + return &inst->base; +} + +static IrInstSrc *ir_build_add_implicit_return_type(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *value, ResultLocReturn *result_loc_ret) +{ + IrInstSrcAddImplicitReturnType *inst = ir_build_instruction(irb, scope, source_node); + inst->value = value; + inst->result_loc_ret = result_loc_ret; + + ir_ref_instruction(value, irb->current_basic_block); + + return &inst->base; +} + +static IrInstSrc *ir_build_has_decl(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *container, IrInstSrc *name) +{ + IrInstSrcHasDecl *instruction = ir_build_instruction(irb, scope, source_node); + instruction->container = container; + instruction->name = name; + + ir_ref_instruction(container, irb->current_basic_block); + ir_ref_instruction(name, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_undeclared_identifier(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, Buf *name) { + IrInstSrcUndeclaredIdent *instruction = ir_build_instruction(irb, scope, source_node); + instruction->name = name; + + return &instruction->base; +} + +static IrInstSrc *ir_build_check_runtime_scope(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *scope_is_comptime, IrInstSrc *is_comptime) { + IrInstSrcCheckRuntimeScope *instruction = ir_build_instruction(irb, scope, source_node); + instruction->scope_is_comptime = scope_is_comptime; + instruction->is_comptime = is_comptime; + + ir_ref_instruction(scope_is_comptime, irb->current_basic_block); + ir_ref_instruction(is_comptime, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrc *ir_build_union_init_named_field(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *union_type, IrInstSrc *field_name, IrInstSrc *field_result_loc, IrInstSrc *result_loc) +{ + IrInstSrcUnionInitNamedField *instruction = ir_build_instruction(irb, scope, source_node); + instruction->union_type = union_type; + instruction->field_name = field_name; + instruction->field_result_loc = field_result_loc; + instruction->result_loc = result_loc; + + ir_ref_instruction(union_type, irb->current_basic_block); + ir_ref_instruction(field_name, irb->current_basic_block); + ir_ref_instruction(field_result_loc, irb->current_basic_block); + if (result_loc != nullptr) ir_ref_instruction(result_loc, irb->current_basic_block); + + return &instruction->base; +} + + +static IrInstGen *ir_build_vector_to_array(IrAnalyze *ira, IrInst *source_instruction, + ZigType *result_type, IrInstGen *vector, IrInstGen *result_loc) +{ + IrInstGenVectorToArray *instruction = ir_build_inst_gen(&ira->new_irb, + source_instruction->scope, source_instruction->source_node); + instruction->base.value->type = result_type; + instruction->vector = vector; + instruction->result_loc = result_loc; + + ir_ref_inst_gen(vector); + ir_ref_inst_gen(result_loc); + + return &instruction->base; +} + +static IrInstGen *ir_build_ptr_of_array_to_slice(IrAnalyze *ira, IrInst *source_instruction, + ZigType *result_type, IrInstGen *operand, IrInstGen *result_loc) +{ + IrInstGenPtrOfArrayToSlice *instruction = ir_build_inst_gen(&ira->new_irb, + source_instruction->scope, source_instruction->source_node); + instruction->base.value->type = result_type; + instruction->operand = operand; + instruction->result_loc = result_loc; + + ir_ref_inst_gen(operand); + ir_ref_inst_gen(result_loc); + + return &instruction->base; +} + +static IrInstGen *ir_build_array_to_vector(IrAnalyze *ira, IrInst *source_instruction, + IrInstGen *array, ZigType *result_type) +{ + IrInstGenArrayToVector *instruction = ir_build_inst_gen(&ira->new_irb, + source_instruction->scope, source_instruction->source_node); + instruction->base.value->type = result_type; + instruction->array = array; + + ir_ref_inst_gen(array); + + return &instruction->base; +} + +static IrInstGen *ir_build_assert_zero(IrAnalyze *ira, IrInst *source_instruction, + IrInstGen *target) +{ + IrInstGenAssertZero *instruction = ir_build_inst_gen(&ira->new_irb, + source_instruction->scope, source_instruction->source_node); + instruction->base.value->type = ira->codegen->builtin_types.entry_void; + instruction->target = target; + + ir_ref_inst_gen(target); + + return &instruction->base; +} + +static IrInstGen *ir_build_assert_non_null(IrAnalyze *ira, IrInst *source_instruction, + IrInstGen *target) +{ + IrInstGenAssertNonNull *instruction = ir_build_inst_gen(&ira->new_irb, + source_instruction->scope, source_instruction->source_node); + instruction->base.value->type = ira->codegen->builtin_types.entry_void; + instruction->target = target; + + ir_ref_inst_gen(target); + + return &instruction->base; +} + +static IrInstSrc *ir_build_alloca_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *align, const char *name_hint, IrInstSrc *is_comptime) +{ + IrInstSrcAlloca *instruction = ir_build_instruction(irb, scope, source_node); + instruction->base.is_gen = true; + instruction->align = align; + instruction->name_hint = name_hint; + instruction->is_comptime = is_comptime; + + if (align != nullptr) ir_ref_instruction(align, irb->current_basic_block); + if (is_comptime != nullptr) ir_ref_instruction(is_comptime, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGenAlloca *ir_build_alloca_gen(IrAnalyze *ira, IrInst *source_instruction, + uint32_t align, const char *name_hint) +{ + IrInstGenAlloca *instruction = ir_create_inst_gen(&ira->new_irb, + source_instruction->scope, source_instruction->source_node); + instruction->align = align; + instruction->name_hint = name_hint; + + return instruction; +} + +static IrInstSrc *ir_build_end_expr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *value, ResultLoc *result_loc) +{ + IrInstSrcEndExpr *instruction = ir_build_instruction(irb, scope, source_node); + instruction->base.is_gen = true; + instruction->value = value; + instruction->result_loc = result_loc; + + ir_ref_instruction(value, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstSrcSuspendBegin *ir_build_suspend_begin_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { + return ir_build_instruction(irb, scope, source_node); +} + +static IrInstGen *ir_build_suspend_begin_gen(IrAnalyze *ira, IrInst *source_instr) { + IrInstGenSuspendBegin *inst = ir_build_inst_void(&ira->new_irb, + source_instr->scope, source_instr->source_node); + return &inst->base; +} + +static IrInstSrc *ir_build_suspend_finish_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrcSuspendBegin *begin) +{ + IrInstSrcSuspendFinish *inst = ir_build_instruction(irb, scope, source_node); + inst->begin = begin; + + ir_ref_instruction(&begin->base, irb->current_basic_block); + + return &inst->base; +} + +static IrInstGen *ir_build_suspend_finish_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGenSuspendBegin *begin) { + IrInstGenSuspendFinish *inst = ir_build_inst_void(&ira->new_irb, + source_instr->scope, source_instr->source_node); + inst->begin = begin; + + ir_ref_inst_gen(&begin->base); + + return &inst->base; +} + +static IrInstSrc *ir_build_await_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *frame, ResultLoc *result_loc, bool is_nosuspend) +{ + IrInstSrcAwait *instruction = ir_build_instruction(irb, scope, source_node); + instruction->frame = frame; + instruction->result_loc = result_loc; + instruction->is_nosuspend = is_nosuspend; + + ir_ref_instruction(frame, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGenAwait *ir_build_await_gen(IrAnalyze *ira, IrInst *source_instruction, + IrInstGen *frame, ZigType *result_type, IrInstGen *result_loc, bool is_nosuspend) +{ + IrInstGenAwait *instruction = ir_build_inst_gen(&ira->new_irb, + source_instruction->scope, source_instruction->source_node); + instruction->base.value->type = result_type; + instruction->frame = frame; + instruction->result_loc = result_loc; + instruction->is_nosuspend = is_nosuspend; + + ir_ref_inst_gen(frame); + if (result_loc != nullptr) ir_ref_inst_gen(result_loc); + + return instruction; +} + +static IrInstSrc *ir_build_resume_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *frame) { + IrInstSrcResume *instruction = ir_build_instruction(irb, scope, source_node); + instruction->frame = frame; + + ir_ref_instruction(frame, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGen *ir_build_resume_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *frame) { + IrInstGenResume *instruction = ir_build_inst_void(&ira->new_irb, + source_instr->scope, source_instr->source_node); + instruction->frame = frame; + + ir_ref_inst_gen(frame); + + return &instruction->base; +} + +static IrInstSrcSpillBegin *ir_build_spill_begin_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *operand, SpillId spill_id) +{ + IrInstSrcSpillBegin *instruction = ir_build_instruction(irb, scope, source_node); + instruction->operand = operand; + instruction->spill_id = spill_id; + + ir_ref_instruction(operand, irb->current_basic_block); + + return instruction; +} + +static IrInstGen *ir_build_spill_begin_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *operand, + SpillId spill_id) +{ + IrInstGenSpillBegin *instruction = ir_build_inst_void(&ira->new_irb, + source_instr->scope, source_instr->source_node); + instruction->operand = operand; + instruction->spill_id = spill_id; + + ir_ref_inst_gen(operand); + + return &instruction->base; +} + +static IrInstSrc *ir_build_spill_end_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrcSpillBegin *begin) +{ + IrInstSrcSpillEnd *instruction = ir_build_instruction(irb, scope, source_node); + instruction->begin = begin; + + ir_ref_instruction(&begin->base, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGen *ir_build_spill_end_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGenSpillBegin *begin, + ZigType *result_type) +{ + IrInstGenSpillEnd *instruction = ir_build_inst_gen(&ira->new_irb, + source_instr->scope, source_instr->source_node); + instruction->base.value->type = result_type; + instruction->begin = begin; + + ir_ref_inst_gen(&begin->base); + + return &instruction->base; +} + +static IrInstGen *ir_build_vector_extract_elem(IrAnalyze *ira, IrInst *source_instruction, + IrInstGen *vector, IrInstGen *index) +{ + IrInstGenVectorExtractElem *instruction = ir_build_inst_gen( + &ira->new_irb, source_instruction->scope, source_instruction->source_node); + instruction->base.value->type = vector->value->type->data.vector.elem_type; + instruction->vector = vector; + instruction->index = index; + + ir_ref_inst_gen(vector); + ir_ref_inst_gen(index); + + return &instruction->base; +} + +static IrInstSrc *ir_build_wasm_memory_size_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *index) { + IrInstSrcWasmMemorySize *instruction = ir_build_instruction(irb, scope, source_node); + instruction->index = index; + + ir_ref_instruction(index, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGen *ir_build_wasm_memory_size_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *index) { + IrInstGenWasmMemorySize *instruction = ir_build_inst_gen(&ira->new_irb, + source_instr->scope, source_instr->source_node); + instruction->base.value->type = ira->codegen->builtin_types.entry_u32; + instruction->index = index; + + ir_ref_inst_gen(index); + + return &instruction->base; +} + +static IrInstSrc *ir_build_wasm_memory_grow_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, IrInstSrc *index, IrInstSrc *delta) { + IrInstSrcWasmMemoryGrow *instruction = ir_build_instruction(irb, scope, source_node); + instruction->index = index; + instruction->delta = delta; + + ir_ref_instruction(index, irb->current_basic_block); + ir_ref_instruction(delta, irb->current_basic_block); + + return &instruction->base; +} + +static IrInstGen *ir_build_wasm_memory_grow_gen(IrAnalyze *ira, IrInst *source_instr, IrInstGen *index, IrInstGen *delta) { + IrInstGenWasmMemoryGrow *instruction = ir_build_inst_gen(&ira->new_irb, + source_instr->scope, source_instr->source_node); + instruction->base.value->type = ira->codegen->builtin_types.entry_i32; + instruction->index = index; + instruction->delta = delta; + + ir_ref_inst_gen(index); + ir_ref_inst_gen(delta); + + return &instruction->base; +} + +static IrInstSrc *ir_build_src(IrBuilderSrc *irb, Scope *scope, AstNode *source_node) { + IrInstSrcSrc *instruction = ir_build_instruction(irb, scope, source_node); + + return &instruction->base; +} + +static void ir_count_defers(IrBuilderSrc *irb, Scope *inner_scope, Scope *outer_scope, size_t *results) { + results[ReturnKindUnconditional] = 0; + results[ReturnKindError] = 0; + + Scope *scope = inner_scope; + + while (scope != outer_scope) { + assert(scope); + switch (scope->id) { + case ScopeIdDefer: { + AstNode *defer_node = scope->source_node; + assert(defer_node->type == NodeTypeDefer); + ReturnKind defer_kind = defer_node->data.defer.kind; + results[defer_kind] += 1; + scope = scope->parent; + continue; + } + case ScopeIdDecls: + case ScopeIdFnDef: + return; + case ScopeIdBlock: + case ScopeIdVarDecl: + case ScopeIdLoop: + case ScopeIdSuspend: + case ScopeIdCompTime: + case ScopeIdNoSuspend: + case ScopeIdRuntime: + case ScopeIdTypeOf: + case ScopeIdExpr: + scope = scope->parent; + continue; + case ScopeIdDeferExpr: + case ScopeIdCImport: + zig_unreachable(); + } + } +} + +static IrInstSrc *ir_mark_gen(IrInstSrc *instruction) { + instruction->is_gen = true; + return instruction; +} + +static bool ir_gen_defers_for_block(IrBuilderSrc *irb, Scope *inner_scope, Scope *outer_scope, bool *is_noreturn, IrInstSrc *err_value) { + Scope *scope = inner_scope; + if (is_noreturn != nullptr) *is_noreturn = false; + while (scope != outer_scope) { + if (!scope) + return true; + + switch (scope->id) { + case ScopeIdDefer: { + AstNode *defer_node = scope->source_node; + assert(defer_node->type == NodeTypeDefer); + ReturnKind defer_kind = defer_node->data.defer.kind; + AstNode *defer_expr_node = defer_node->data.defer.expr; + AstNode *defer_var_node = defer_node->data.defer.err_payload; + + if (defer_kind == ReturnKindError && err_value == nullptr) { + // This is an `errdefer` but we're generating code for a + // `return` that doesn't return an error, skip it + scope = scope->parent; + continue; + } + + Scope *defer_expr_scope = defer_node->data.defer.expr_scope; + if (defer_var_node != nullptr) { + assert(defer_kind == ReturnKindError); + assert(defer_var_node->type == NodeTypeSymbol); + Buf *var_name = defer_var_node->data.symbol_expr.symbol; + + if (defer_expr_node->type == NodeTypeUnreachable) { + add_node_error(irb->codegen, defer_var_node, + buf_sprintf("unused variable: '%s'", buf_ptr(var_name))); + return false; + } + + IrInstSrc *is_comptime; + if (ir_should_inline(irb->exec, defer_expr_scope)) { + is_comptime = ir_build_const_bool(irb, defer_expr_scope, + defer_expr_node, true); + } else { + is_comptime = ir_build_test_comptime(irb, defer_expr_scope, + defer_expr_node, err_value); + } + + ZigVar *err_var = ir_create_var(irb, defer_var_node, defer_expr_scope, + var_name, true, true, false, is_comptime); + build_decl_var_and_init(irb, defer_expr_scope, defer_var_node, err_var, err_value, + buf_ptr(var_name), is_comptime); + + defer_expr_scope = err_var->child_scope; + } + + IrInstSrc *defer_expr_value = ir_gen_node(irb, defer_expr_node, defer_expr_scope); + if (defer_expr_value == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + + if (defer_expr_value->is_noreturn) { + if (is_noreturn != nullptr) *is_noreturn = true; + } else { + ir_mark_gen(ir_build_check_statement_is_void(irb, defer_expr_scope, defer_expr_node, + defer_expr_value)); + } + scope = scope->parent; + continue; + } + case ScopeIdDecls: + case ScopeIdFnDef: + return true; + case ScopeIdBlock: + case ScopeIdVarDecl: + case ScopeIdLoop: + case ScopeIdSuspend: + case ScopeIdCompTime: + case ScopeIdNoSuspend: + case ScopeIdRuntime: + case ScopeIdTypeOf: + case ScopeIdExpr: + scope = scope->parent; + continue; + case ScopeIdDeferExpr: + case ScopeIdCImport: + zig_unreachable(); + } + } + return true; +} + +static void ir_set_cursor_at_end_gen(IrBuilderGen *irb, IrBasicBlockGen *basic_block) { + assert(basic_block); + irb->current_basic_block = basic_block; +} + +static void ir_set_cursor_at_end(IrBuilderSrc *irb, IrBasicBlockSrc *basic_block) { + assert(basic_block); + irb->current_basic_block = basic_block; +} + +static void ir_append_basic_block_gen(IrBuilderGen *irb, IrBasicBlockGen *bb) { + assert(!bb->already_appended); + bb->already_appended = true; + irb->exec->basic_block_list.append(bb); +} + +static void ir_set_cursor_at_end_and_append_block_gen(IrBuilderGen *irb, IrBasicBlockGen *basic_block) { + ir_append_basic_block_gen(irb, basic_block); + ir_set_cursor_at_end_gen(irb, basic_block); +} + +static void ir_set_cursor_at_end_and_append_block(IrBuilderSrc *irb, IrBasicBlockSrc *basic_block) { + basic_block->index = irb->exec->basic_block_list.length; + irb->exec->basic_block_list.append(basic_block); + ir_set_cursor_at_end(irb, basic_block); +} + +static ScopeSuspend *get_scope_suspend(Scope *scope) { + while (scope) { + if (scope->id == ScopeIdSuspend) + return (ScopeSuspend *)scope; + if (scope->id == ScopeIdFnDef) + return nullptr; + + scope = scope->parent; + } + return nullptr; +} + +static ScopeDeferExpr *get_scope_defer_expr(Scope *scope) { + while (scope) { + if (scope->id == ScopeIdDeferExpr) + return (ScopeDeferExpr *)scope; + if (scope->id == ScopeIdFnDef) + return nullptr; + + scope = scope->parent; + } + return nullptr; +} + +static IrInstSrc *ir_gen_return(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *result_loc) { + assert(node->type == NodeTypeReturnExpr); + + ScopeDeferExpr *scope_defer_expr = get_scope_defer_expr(scope); + if (scope_defer_expr) { + if (!scope_defer_expr->reported_err) { + add_node_error(irb->codegen, node, buf_sprintf("cannot return from defer expression")); + scope_defer_expr->reported_err = true; + } + return irb->codegen->invalid_inst_src; + } + + Scope *outer_scope = irb->exec->begin_scope; + + AstNode *expr_node = node->data.return_expr.expr; + switch (node->data.return_expr.kind) { + case ReturnKindUnconditional: + { + ResultLocReturn *result_loc_ret = heap::c_allocator.create(); + result_loc_ret->base.id = ResultLocIdReturn; + ir_build_reset_result(irb, scope, node, &result_loc_ret->base); + + IrInstSrc *return_value; + if (expr_node) { + // Temporarily set this so that if we return a type it gets the name of the function + ZigFn *prev_name_fn = irb->exec->name_fn; + irb->exec->name_fn = exec_fn_entry(irb->exec); + return_value = ir_gen_node_extra(irb, expr_node, scope, LValNone, &result_loc_ret->base); + irb->exec->name_fn = prev_name_fn; + if (return_value == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + } else { + return_value = ir_build_const_void(irb, scope, node); + ir_build_end_expr(irb, scope, node, return_value, &result_loc_ret->base); + } + + ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, node, return_value, result_loc_ret)); + + size_t defer_counts[2]; + ir_count_defers(irb, scope, outer_scope, defer_counts); + bool have_err_defers = defer_counts[ReturnKindError] > 0; + if (!have_err_defers && !irb->codegen->have_err_ret_tracing) { + // only generate unconditional defers + if (!ir_gen_defers_for_block(irb, scope, outer_scope, nullptr, nullptr)) + return irb->codegen->invalid_inst_src; + IrInstSrc *result = ir_build_return_src(irb, scope, node, nullptr); + result_loc_ret->base.source_instruction = result; + return result; + } + bool should_inline = ir_should_inline(irb->exec, scope); + + IrBasicBlockSrc *err_block = ir_create_basic_block(irb, scope, "ErrRetErr"); + IrBasicBlockSrc *ok_block = ir_create_basic_block(irb, scope, "ErrRetOk"); + + IrInstSrc *is_err = ir_build_test_err_src(irb, scope, node, return_value, false, true); + + IrInstSrc *is_comptime; + if (should_inline) { + is_comptime = ir_build_const_bool(irb, scope, node, should_inline); + } else { + is_comptime = ir_build_test_comptime(irb, scope, node, is_err); + } + + ir_mark_gen(ir_build_cond_br(irb, scope, node, is_err, err_block, ok_block, is_comptime)); + IrBasicBlockSrc *ret_stmt_block = ir_create_basic_block(irb, scope, "RetStmt"); + + ir_set_cursor_at_end_and_append_block(irb, err_block); + if (!ir_gen_defers_for_block(irb, scope, outer_scope, nullptr, return_value)) + return irb->codegen->invalid_inst_src; + if (irb->codegen->have_err_ret_tracing && !should_inline) { + ir_build_save_err_ret_addr_src(irb, scope, node); + } + ir_build_br(irb, scope, node, ret_stmt_block, is_comptime); + + ir_set_cursor_at_end_and_append_block(irb, ok_block); + if (!ir_gen_defers_for_block(irb, scope, outer_scope, nullptr, nullptr)) + return irb->codegen->invalid_inst_src; + ir_build_br(irb, scope, node, ret_stmt_block, is_comptime); + + ir_set_cursor_at_end_and_append_block(irb, ret_stmt_block); + IrInstSrc *result = ir_build_return_src(irb, scope, node, nullptr); + result_loc_ret->base.source_instruction = result; + return result; + } + case ReturnKindError: + { + assert(expr_node); + IrInstSrc *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr, nullptr); + if (err_union_ptr == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + IrInstSrc *is_err_val = ir_build_test_err_src(irb, scope, node, err_union_ptr, true, false); + + IrBasicBlockSrc *return_block = ir_create_basic_block(irb, scope, "ErrRetReturn"); + IrBasicBlockSrc *continue_block = ir_create_basic_block(irb, scope, "ErrRetContinue"); + IrInstSrc *is_comptime; + bool should_inline = ir_should_inline(irb->exec, scope); + if (should_inline) { + is_comptime = ir_build_const_bool(irb, scope, node, true); + } else { + is_comptime = ir_build_test_comptime(irb, scope, node, is_err_val); + } + ir_mark_gen(ir_build_cond_br(irb, scope, node, is_err_val, return_block, continue_block, is_comptime)); + + ir_set_cursor_at_end_and_append_block(irb, return_block); + IrInstSrc *err_val_ptr = ir_build_unwrap_err_code_src(irb, scope, node, err_union_ptr); + IrInstSrc *err_val = ir_build_load_ptr(irb, scope, node, err_val_ptr); + ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, node, err_val, nullptr)); + IrInstSrcSpillBegin *spill_begin = ir_build_spill_begin_src(irb, scope, node, err_val, + SpillIdRetErrCode); + ResultLocReturn *result_loc_ret = heap::c_allocator.create(); + result_loc_ret->base.id = ResultLocIdReturn; + ir_build_reset_result(irb, scope, node, &result_loc_ret->base); + ir_build_end_expr(irb, scope, node, err_val, &result_loc_ret->base); + + bool is_noreturn = false; + if (!ir_gen_defers_for_block(irb, scope, outer_scope, &is_noreturn, err_val)) { + return irb->codegen->invalid_inst_src; + } + if (!is_noreturn) { + if (irb->codegen->have_err_ret_tracing && !should_inline) { + ir_build_save_err_ret_addr_src(irb, scope, node); + } + err_val = ir_build_spill_end_src(irb, scope, node, spill_begin); + IrInstSrc *ret_inst = ir_build_return_src(irb, scope, node, err_val); + result_loc_ret->base.source_instruction = ret_inst; + } + + ir_set_cursor_at_end_and_append_block(irb, continue_block); + IrInstSrc *unwrapped_ptr = ir_build_unwrap_err_payload_src(irb, scope, node, err_union_ptr, false, false); + if (lval == LValPtr) + return unwrapped_ptr; + else + return ir_expr_wrap(irb, scope, ir_build_load_ptr(irb, scope, node, unwrapped_ptr), result_loc); + } + } + zig_unreachable(); +} + +static ZigVar *create_local_var(CodeGen *codegen, AstNode *node, Scope *parent_scope, + Buf *name, bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstSrc *is_comptime, + bool skip_name_check) +{ + ZigVar *variable_entry = heap::c_allocator.create(); + variable_entry->parent_scope = parent_scope; + variable_entry->shadowable = is_shadowable; + variable_entry->is_comptime = is_comptime; + variable_entry->src_arg_index = SIZE_MAX; + variable_entry->const_value = codegen->pass1_arena->create(); + + if (is_comptime != nullptr) { + is_comptime->base.ref_count += 1; + } + + if (name) { + variable_entry->name = strdup(buf_ptr(name)); + + if (!skip_name_check) { + ZigVar *existing_var = find_variable(codegen, 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(codegen, node, + buf_sprintf("redeclaration of variable '%s'", buf_ptr(name))); + add_error_note(codegen, msg, existing_var->decl_node, buf_sprintf("previous declaration is here")); + } + variable_entry->var_type = codegen->builtin_types.entry_invalid; + } else { + ZigType *type; + if (get_primitive_type(codegen, name, &type) != ErrorPrimitiveTypeNotFound) { + add_node_error(codegen, node, + buf_sprintf("variable shadows primitive type '%s'", buf_ptr(name))); + variable_entry->var_type = codegen->builtin_types.entry_invalid; + } else { + Tld *tld = find_decl(codegen, parent_scope, name); + if (tld != nullptr) { + 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(codegen, node, + buf_sprintf("redefinition of '%s'", buf_ptr(name))); + add_error_note(codegen, msg, tld->source_node, buf_sprintf("previous definition is here")); + } + variable_entry->var_type = codegen->builtin_types.entry_invalid; + } + } + } + } + } else { + assert(is_shadowable); + // TODO make this name not actually be in scope. user should be able to make a variable called "_anon" + // might already be solved, let's just make sure it has test coverage + // maybe we put a prefix on this so the debug info doesn't clobber user debug info for same named variables + variable_entry->name = "_anon"; + } + + variable_entry->src_is_const = src_is_const; + variable_entry->gen_is_const = gen_is_const; + variable_entry->decl_node = node; + variable_entry->child_scope = create_var_scope(codegen, node, parent_scope, variable_entry); + + return variable_entry; +} + +// Set name to nullptr to make the variable anonymous (not visible to programmer). +// After you call this function var->child_scope has the variable in scope +static ZigVar *ir_create_var(IrBuilderSrc *irb, AstNode *node, Scope *scope, Buf *name, + bool src_is_const, bool gen_is_const, bool is_shadowable, IrInstSrc *is_comptime) +{ + bool is_underscored = name ? buf_eql_str(name, "_") : false; + ZigVar *var = create_local_var(irb->codegen, node, scope, + (is_underscored ? nullptr : name), src_is_const, gen_is_const, + (is_underscored ? true : is_shadowable), is_comptime, false); + assert(var->child_scope); + return var; +} + +static ResultLocPeer *create_peer_result(ResultLocPeerParent *peer_parent) { + ResultLocPeer *result = heap::c_allocator.create(); + result->base.id = ResultLocIdPeer; + result->base.source_instruction = peer_parent->base.source_instruction; + result->parent = peer_parent; + result->base.allow_write_through_const = peer_parent->parent->allow_write_through_const; + return result; +} + +static bool is_duplicate_label(CodeGen *g, Scope *scope, AstNode *node, Buf *name) { + if (name == nullptr) return false; + + for (;;) { + if (scope == nullptr || scope->id == ScopeIdFnDef) { + break; + } else if (scope->id == ScopeIdBlock || scope->id == ScopeIdLoop) { + Buf *this_block_name = scope->id == ScopeIdBlock ? ((ScopeBlock *)scope)->name : ((ScopeLoop *)scope)->name; + if (this_block_name != nullptr && buf_eql_buf(name, this_block_name)) { + ErrorMsg *msg = add_node_error(g, node, buf_sprintf("redeclaration of label '%s'", buf_ptr(name))); + add_error_note(g, msg, scope->source_node, buf_sprintf("previous declaration is here")); + return true; + } + } + scope = scope->parent; + } + return false; +} + +static IrInstSrc *ir_gen_block(IrBuilderSrc *irb, Scope *parent_scope, AstNode *block_node, LVal lval, + ResultLoc *result_loc) +{ + assert(block_node->type == NodeTypeBlock); + + ZigList incoming_values = {0}; + ZigList incoming_blocks = {0}; + + if (is_duplicate_label(irb->codegen, parent_scope, block_node, block_node->data.block.name)) + return irb->codegen->invalid_inst_src; + + ScopeBlock *scope_block = create_block_scope(irb->codegen, block_node, parent_scope); + + Scope *outer_block_scope = &scope_block->base; + Scope *child_scope = outer_block_scope; + + ZigFn *fn_entry = scope_fn_entry(parent_scope); + if (fn_entry && fn_entry->child_scope == parent_scope) { + fn_entry->def_scope = scope_block; + } + + if (block_node->data.block.statements.length == 0) { + if (scope_block->name != nullptr) { + add_node_error(irb->codegen, block_node, buf_sprintf("unused block label")); + } + // {} + return ir_lval_wrap(irb, parent_scope, ir_build_const_void(irb, child_scope, block_node), lval, result_loc); + } + + if (block_node->data.block.name != nullptr) { + scope_block->lval = lval; + scope_block->incoming_blocks = &incoming_blocks; + scope_block->incoming_values = &incoming_values; + scope_block->end_block = ir_create_basic_block(irb, parent_scope, "BlockEnd"); + scope_block->is_comptime = ir_build_const_bool(irb, parent_scope, block_node, + ir_should_inline(irb->exec, parent_scope)); + + scope_block->peer_parent = heap::c_allocator.create(); + scope_block->peer_parent->base.id = ResultLocIdPeerParent; + scope_block->peer_parent->base.source_instruction = scope_block->is_comptime; + scope_block->peer_parent->base.allow_write_through_const = result_loc->allow_write_through_const; + scope_block->peer_parent->end_bb = scope_block->end_block; + scope_block->peer_parent->is_comptime = scope_block->is_comptime; + scope_block->peer_parent->parent = result_loc; + ir_build_reset_result(irb, parent_scope, block_node, &scope_block->peer_parent->base); + } + + bool is_continuation_unreachable = false; + bool found_invalid_inst = false; + IrInstSrc *noreturn_return_value = nullptr; + for (size_t i = 0; i < block_node->data.block.statements.length; i += 1) { + AstNode *statement_node = block_node->data.block.statements.at(i); + + IrInstSrc *statement_value = ir_gen_node(irb, statement_node, child_scope); + if (statement_value == irb->codegen->invalid_inst_src) { + // keep generating all the elements of the block in case of error, + // we want to collect other compile errors + found_invalid_inst = true; + continue; + } + + is_continuation_unreachable = instr_is_unreachable(statement_value); + if (is_continuation_unreachable) { + // keep the last noreturn statement value around in case we need to return it + noreturn_return_value = statement_value; + } + // This logic must be kept in sync with + // [STMT_EXPR_TEST_THING] <--- (search this token) + if (statement_node->type == NodeTypeDefer) { + // defer starts a new scope + child_scope = statement_node->data.defer.child_scope; + assert(child_scope); + } else if (statement_value->id == IrInstSrcIdDeclVar) { + // variable declarations start a new scope + IrInstSrcDeclVar *decl_var_instruction = (IrInstSrcDeclVar *)statement_value; + child_scope = decl_var_instruction->var->child_scope; + } else if (!is_continuation_unreachable) { + // this statement's value must be void + ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, statement_node, statement_value)); + } + } + + if (scope_block->name != nullptr && scope_block->name_used == false) { + add_node_error(irb->codegen, block_node, buf_sprintf("unused block label")); + } + + if (found_invalid_inst) + return irb->codegen->invalid_inst_src; + + if (is_continuation_unreachable) { + assert(noreturn_return_value != nullptr); + if (block_node->data.block.name == nullptr || incoming_blocks.length == 0) { + return noreturn_return_value; + } + + if (scope_block->peer_parent != nullptr && scope_block->peer_parent->peers.length != 0) { + scope_block->peer_parent->peers.last()->next_bb = scope_block->end_block; + } + ir_set_cursor_at_end_and_append_block(irb, scope_block->end_block); + IrInstSrc *phi = ir_build_phi(irb, parent_scope, block_node, incoming_blocks.length, + incoming_blocks.items, incoming_values.items, scope_block->peer_parent); + return ir_expr_wrap(irb, parent_scope, phi, result_loc); + } else { + incoming_blocks.append(irb->current_basic_block); + IrInstSrc *else_expr_result = ir_mark_gen(ir_build_const_void(irb, parent_scope, block_node)); + + if (scope_block->peer_parent != nullptr) { + ResultLocPeer *peer_result = create_peer_result(scope_block->peer_parent); + scope_block->peer_parent->peers.append(peer_result); + ir_build_end_expr(irb, parent_scope, block_node, else_expr_result, &peer_result->base); + + if (scope_block->peer_parent->peers.length != 0) { + scope_block->peer_parent->peers.last()->next_bb = scope_block->end_block; + } + } + + incoming_values.append(else_expr_result); + } + + bool is_return_from_fn = block_node == irb->main_block_node; + if (!is_return_from_fn) { + if (!ir_gen_defers_for_block(irb, child_scope, outer_block_scope, nullptr, nullptr)) + return irb->codegen->invalid_inst_src; + } + + IrInstSrc *result; + if (block_node->data.block.name != nullptr) { + ir_mark_gen(ir_build_br(irb, parent_scope, block_node, scope_block->end_block, scope_block->is_comptime)); + ir_set_cursor_at_end_and_append_block(irb, scope_block->end_block); + IrInstSrc *phi = ir_build_phi(irb, parent_scope, block_node, incoming_blocks.length, + incoming_blocks.items, incoming_values.items, scope_block->peer_parent); + result = ir_expr_wrap(irb, parent_scope, phi, result_loc); + } else { + IrInstSrc *void_inst = ir_mark_gen(ir_build_const_void(irb, child_scope, block_node)); + result = ir_lval_wrap(irb, parent_scope, void_inst, lval, result_loc); + } + if (!is_return_from_fn) + return result; + + // no need for save_err_ret_addr because this cannot return error + // only generate unconditional defers + + ir_mark_gen(ir_build_add_implicit_return_type(irb, child_scope, block_node, result, nullptr)); + ResultLocReturn *result_loc_ret = heap::c_allocator.create(); + result_loc_ret->base.id = ResultLocIdReturn; + ir_build_reset_result(irb, parent_scope, block_node, &result_loc_ret->base); + ir_mark_gen(ir_build_end_expr(irb, parent_scope, block_node, result, &result_loc_ret->base)); + if (!ir_gen_defers_for_block(irb, child_scope, outer_block_scope, nullptr, nullptr)) + return irb->codegen->invalid_inst_src; + return ir_mark_gen(ir_build_return_src(irb, child_scope, result->base.source_node, result)); +} + +static IrInstSrc *ir_gen_bin_op_id(IrBuilderSrc *irb, Scope *scope, AstNode *node, IrBinOp op_id) { + Scope *inner_scope = scope; + if (op_id == IrBinOpArrayCat || op_id == IrBinOpArrayMult) { + inner_scope = create_comptime_scope(irb->codegen, node, scope); + } + + IrInstSrc *op1 = ir_gen_node(irb, node->data.bin_op_expr.op1, inner_scope); + IrInstSrc *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, inner_scope); + + if (op1 == irb->codegen->invalid_inst_src || op2 == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + + return ir_build_bin_op(irb, scope, node, op_id, op1, op2, true); +} + +static IrInstSrc *ir_gen_merge_err_sets(IrBuilderSrc *irb, Scope *scope, AstNode *node) { + IrInstSrc *op1 = ir_gen_node(irb, node->data.bin_op_expr.op1, scope); + IrInstSrc *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope); + + if (op1 == irb->codegen->invalid_inst_src || op2 == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + + // TODO only pass type_name when the || operator is the top level AST node in the var decl expr + Buf bare_name = BUF_INIT; + Buf *type_name = get_anon_type_name(irb->codegen, irb->exec, "error", scope, node, &bare_name); + + return ir_build_merge_err_sets(irb, scope, node, op1, op2, type_name); +} + +static IrInstSrc *ir_gen_assign(IrBuilderSrc *irb, Scope *scope, AstNode *node) { + IrInstSrc *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LValAssign, nullptr); + if (lvalue == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + + ResultLocInstruction *result_loc_inst = heap::c_allocator.create(); + result_loc_inst->base.id = ResultLocIdInstruction; + result_loc_inst->base.source_instruction = lvalue; + ir_ref_instruction(lvalue, irb->current_basic_block); + ir_build_reset_result(irb, scope, node, &result_loc_inst->base); + + IrInstSrc *rvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op2, scope, LValNone, + &result_loc_inst->base); + if (rvalue == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + + return ir_build_const_void(irb, scope, node); +} + +static IrInstSrc *ir_gen_assign_merge_err_sets(IrBuilderSrc *irb, Scope *scope, AstNode *node) { + IrInstSrc *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LValAssign, nullptr); + if (lvalue == irb->codegen->invalid_inst_src) + return lvalue; + IrInstSrc *op1 = ir_build_load_ptr(irb, scope, node->data.bin_op_expr.op1, lvalue); + IrInstSrc *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope); + if (op2 == irb->codegen->invalid_inst_src) + return op2; + IrInstSrc *result = ir_build_merge_err_sets(irb, scope, node, op1, op2, nullptr); + ir_build_store_ptr(irb, scope, node, lvalue, result); + return ir_build_const_void(irb, scope, node); +} + +static IrInstSrc *ir_gen_assign_op(IrBuilderSrc *irb, Scope *scope, AstNode *node, IrBinOp op_id) { + IrInstSrc *lvalue = ir_gen_node_extra(irb, node->data.bin_op_expr.op1, scope, LValAssign, nullptr); + if (lvalue == irb->codegen->invalid_inst_src) + return lvalue; + IrInstSrc *op1 = ir_build_load_ptr(irb, scope, node->data.bin_op_expr.op1, lvalue); + IrInstSrc *op2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope); + if (op2 == irb->codegen->invalid_inst_src) + return op2; + IrInstSrc *result = ir_build_bin_op(irb, scope, node, op_id, op1, op2, true); + ir_build_store_ptr(irb, scope, node, lvalue, result); + return ir_build_const_void(irb, scope, node); +} + +static IrInstSrc *ir_gen_bool_or(IrBuilderSrc *irb, Scope *scope, AstNode *node) { + assert(node->type == NodeTypeBinOpExpr); + + IrInstSrc *val1 = ir_gen_node(irb, node->data.bin_op_expr.op1, scope); + if (val1 == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + IrBasicBlockSrc *post_val1_block = irb->current_basic_block; + + IrInstSrc *is_comptime; + if (ir_should_inline(irb->exec, scope)) { + is_comptime = ir_build_const_bool(irb, scope, node, true); + } else { + is_comptime = ir_build_test_comptime(irb, scope, node, val1); + } + + // block for when val1 == false + IrBasicBlockSrc *false_block = ir_create_basic_block(irb, scope, "BoolOrFalse"); + // block for when val1 == true (don't even evaluate the second part) + IrBasicBlockSrc *true_block = ir_create_basic_block(irb, scope, "BoolOrTrue"); + + ir_build_cond_br(irb, scope, node, val1, true_block, false_block, is_comptime); + + ir_set_cursor_at_end_and_append_block(irb, false_block); + IrInstSrc *val2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope); + if (val2 == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + IrBasicBlockSrc *post_val2_block = irb->current_basic_block; + + ir_build_br(irb, scope, node, true_block, is_comptime); + + ir_set_cursor_at_end_and_append_block(irb, true_block); + + IrInstSrc **incoming_values = heap::c_allocator.allocate(2); + incoming_values[0] = val1; + incoming_values[1] = val2; + IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate(2); + incoming_blocks[0] = post_val1_block; + incoming_blocks[1] = post_val2_block; + + return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values, nullptr); +} + +static IrInstSrc *ir_gen_bool_and(IrBuilderSrc *irb, Scope *scope, AstNode *node) { + assert(node->type == NodeTypeBinOpExpr); + + IrInstSrc *val1 = ir_gen_node(irb, node->data.bin_op_expr.op1, scope); + if (val1 == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + IrBasicBlockSrc *post_val1_block = irb->current_basic_block; + + IrInstSrc *is_comptime; + if (ir_should_inline(irb->exec, scope)) { + is_comptime = ir_build_const_bool(irb, scope, node, true); + } else { + is_comptime = ir_build_test_comptime(irb, scope, node, val1); + } + + // block for when val1 == true + IrBasicBlockSrc *true_block = ir_create_basic_block(irb, scope, "BoolAndTrue"); + // block for when val1 == false (don't even evaluate the second part) + IrBasicBlockSrc *false_block = ir_create_basic_block(irb, scope, "BoolAndFalse"); + + ir_build_cond_br(irb, scope, node, val1, true_block, false_block, is_comptime); + + ir_set_cursor_at_end_and_append_block(irb, true_block); + IrInstSrc *val2 = ir_gen_node(irb, node->data.bin_op_expr.op2, scope); + if (val2 == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + IrBasicBlockSrc *post_val2_block = irb->current_basic_block; + + ir_build_br(irb, scope, node, false_block, is_comptime); + + ir_set_cursor_at_end_and_append_block(irb, false_block); + + IrInstSrc **incoming_values = heap::c_allocator.allocate(2); + incoming_values[0] = val1; + incoming_values[1] = val2; + IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate(2); + incoming_blocks[0] = post_val1_block; + incoming_blocks[1] = post_val2_block; + + return ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values, nullptr); +} + +static ResultLocPeerParent *ir_build_result_peers(IrBuilderSrc *irb, IrInstSrc *cond_br_inst, + IrBasicBlockSrc *end_block, ResultLoc *parent, IrInstSrc *is_comptime) +{ + ResultLocPeerParent *peer_parent = heap::c_allocator.create(); + peer_parent->base.id = ResultLocIdPeerParent; + peer_parent->base.source_instruction = cond_br_inst; + peer_parent->base.allow_write_through_const = parent->allow_write_through_const; + peer_parent->end_bb = end_block; + peer_parent->is_comptime = is_comptime; + peer_parent->parent = parent; + + IrInstSrc *popped_inst = irb->current_basic_block->instruction_list.pop(); + ir_assert(popped_inst == cond_br_inst, &cond_br_inst->base); + + ir_build_reset_result(irb, cond_br_inst->base.scope, cond_br_inst->base.source_node, &peer_parent->base); + irb->current_basic_block->instruction_list.append(popped_inst); + + return peer_parent; +} + +static ResultLocPeerParent *ir_build_binary_result_peers(IrBuilderSrc *irb, IrInstSrc *cond_br_inst, + IrBasicBlockSrc *else_block, IrBasicBlockSrc *end_block, ResultLoc *parent, IrInstSrc *is_comptime) +{ + ResultLocPeerParent *peer_parent = ir_build_result_peers(irb, cond_br_inst, end_block, parent, is_comptime); + + peer_parent->peers.append(create_peer_result(peer_parent)); + peer_parent->peers.last()->next_bb = else_block; + + peer_parent->peers.append(create_peer_result(peer_parent)); + peer_parent->peers.last()->next_bb = end_block; + + return peer_parent; +} + +static IrInstSrc *ir_gen_orelse(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node, LVal lval, + ResultLoc *result_loc) +{ + assert(node->type == NodeTypeBinOpExpr); + + AstNode *op1_node = node->data.bin_op_expr.op1; + AstNode *op2_node = node->data.bin_op_expr.op2; + + IrInstSrc *maybe_ptr = ir_gen_node_extra(irb, op1_node, parent_scope, LValPtr, nullptr); + if (maybe_ptr == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + + IrInstSrc *maybe_val = ir_build_load_ptr(irb, parent_scope, node, maybe_ptr); + IrInstSrc *is_non_null = ir_build_test_non_null_src(irb, parent_scope, node, maybe_val); + + IrInstSrc *is_comptime; + if (ir_should_inline(irb->exec, parent_scope)) { + is_comptime = ir_build_const_bool(irb, parent_scope, node, true); + } else { + is_comptime = ir_build_test_comptime(irb, parent_scope, node, is_non_null); + } + + IrBasicBlockSrc *ok_block = ir_create_basic_block(irb, parent_scope, "OptionalNonNull"); + IrBasicBlockSrc *null_block = ir_create_basic_block(irb, parent_scope, "OptionalNull"); + IrBasicBlockSrc *end_block = ir_create_basic_block(irb, parent_scope, "OptionalEnd"); + IrInstSrc *cond_br_inst = ir_build_cond_br(irb, parent_scope, node, is_non_null, ok_block, null_block, is_comptime); + + ResultLocPeerParent *peer_parent = ir_build_binary_result_peers(irb, cond_br_inst, ok_block, end_block, + result_loc, is_comptime); + + ir_set_cursor_at_end_and_append_block(irb, null_block); + IrInstSrc *null_result = ir_gen_node_extra(irb, op2_node, parent_scope, LValNone, + &peer_parent->peers.at(0)->base); + if (null_result == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + IrBasicBlockSrc *after_null_block = irb->current_basic_block; + if (!instr_is_unreachable(null_result)) + ir_mark_gen(ir_build_br(irb, parent_scope, node, end_block, is_comptime)); + + ir_set_cursor_at_end_and_append_block(irb, ok_block); + IrInstSrc *unwrapped_ptr = ir_build_optional_unwrap_ptr(irb, parent_scope, node, maybe_ptr, false); + IrInstSrc *unwrapped_payload = ir_build_load_ptr(irb, parent_scope, node, unwrapped_ptr); + ir_build_end_expr(irb, parent_scope, node, unwrapped_payload, &peer_parent->peers.at(1)->base); + IrBasicBlockSrc *after_ok_block = irb->current_basic_block; + ir_build_br(irb, parent_scope, node, end_block, is_comptime); + + ir_set_cursor_at_end_and_append_block(irb, end_block); + IrInstSrc **incoming_values = heap::c_allocator.allocate(2); + incoming_values[0] = null_result; + incoming_values[1] = unwrapped_payload; + IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate(2); + incoming_blocks[0] = after_null_block; + incoming_blocks[1] = after_ok_block; + IrInstSrc *phi = ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values, peer_parent); + return ir_lval_wrap(irb, parent_scope, phi, lval, result_loc); +} + +static IrInstSrc *ir_gen_error_union(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node) { + assert(node->type == NodeTypeBinOpExpr); + + AstNode *op1_node = node->data.bin_op_expr.op1; + AstNode *op2_node = node->data.bin_op_expr.op2; + + IrInstSrc *err_set = ir_gen_node(irb, op1_node, parent_scope); + if (err_set == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + + IrInstSrc *payload = ir_gen_node(irb, op2_node, parent_scope); + if (payload == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + + return ir_build_error_union(irb, parent_scope, node, err_set, payload); +} + +static IrInstSrc *ir_gen_bin_op(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *result_loc) { + assert(node->type == NodeTypeBinOpExpr); + + BinOpType bin_op_type = node->data.bin_op_expr.bin_op; + switch (bin_op_type) { + case BinOpTypeInvalid: + zig_unreachable(); + case BinOpTypeAssign: + return ir_lval_wrap(irb, scope, ir_gen_assign(irb, scope, node), lval, result_loc); + case BinOpTypeAssignTimes: + return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpMult), lval, result_loc); + case BinOpTypeAssignTimesWrap: + return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpMultWrap), lval, result_loc); + case BinOpTypeAssignDiv: + return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpDivUnspecified), lval, result_loc); + case BinOpTypeAssignMod: + return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpRemUnspecified), lval, result_loc); + case BinOpTypeAssignPlus: + return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpAdd), lval, result_loc); + case BinOpTypeAssignPlusWrap: + return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpAddWrap), lval, result_loc); + case BinOpTypeAssignMinus: + return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpSub), lval, result_loc); + case BinOpTypeAssignMinusWrap: + return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpSubWrap), lval, result_loc); + case BinOpTypeAssignBitShiftLeft: + return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftLeftLossy), lval, result_loc); + case BinOpTypeAssignBitShiftRight: + return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBitShiftRightLossy), lval, result_loc); + case BinOpTypeAssignBitAnd: + return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBinAnd), lval, result_loc); + case BinOpTypeAssignBitXor: + return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBinXor), lval, result_loc); + case BinOpTypeAssignBitOr: + return ir_lval_wrap(irb, scope, ir_gen_assign_op(irb, scope, node, IrBinOpBinOr), lval, result_loc); + case BinOpTypeAssignMergeErrorSets: + return ir_lval_wrap(irb, scope, ir_gen_assign_merge_err_sets(irb, scope, node), lval, result_loc); + case BinOpTypeBoolOr: + return ir_lval_wrap(irb, scope, ir_gen_bool_or(irb, scope, node), lval, result_loc); + case BinOpTypeBoolAnd: + return ir_lval_wrap(irb, scope, ir_gen_bool_and(irb, scope, node), lval, result_loc); + case BinOpTypeCmpEq: + return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpEq), lval, result_loc); + case BinOpTypeCmpNotEq: + return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpNotEq), lval, result_loc); + case BinOpTypeCmpLessThan: + return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpLessThan), lval, result_loc); + case BinOpTypeCmpGreaterThan: + return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpGreaterThan), lval, result_loc); + case BinOpTypeCmpLessOrEq: + return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpLessOrEq), lval, result_loc); + case BinOpTypeCmpGreaterOrEq: + return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpCmpGreaterOrEq), lval, result_loc); + case BinOpTypeBinOr: + return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBinOr), lval, result_loc); + case BinOpTypeBinXor: + return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBinXor), lval, result_loc); + case BinOpTypeBinAnd: + return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBinAnd), lval, result_loc); + case BinOpTypeBitShiftLeft: + return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftLeftLossy), lval, result_loc); + case BinOpTypeBitShiftRight: + return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpBitShiftRightLossy), lval, result_loc); + case BinOpTypeAdd: + return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpAdd), lval, result_loc); + case BinOpTypeAddWrap: + return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpAddWrap), lval, result_loc); + case BinOpTypeSub: + return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpSub), lval, result_loc); + case BinOpTypeSubWrap: + return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpSubWrap), lval, result_loc); + case BinOpTypeMult: + return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpMult), lval, result_loc); + case BinOpTypeMultWrap: + return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpMultWrap), lval, result_loc); + case BinOpTypeDiv: + return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpDivUnspecified), lval, result_loc); + case BinOpTypeMod: + return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpRemUnspecified), lval, result_loc); + case BinOpTypeArrayCat: + return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayCat), lval, result_loc); + case BinOpTypeArrayMult: + return ir_lval_wrap(irb, scope, ir_gen_bin_op_id(irb, scope, node, IrBinOpArrayMult), lval, result_loc); + case BinOpTypeMergeErrorSets: + return ir_lval_wrap(irb, scope, ir_gen_merge_err_sets(irb, scope, node), lval, result_loc); + case BinOpTypeUnwrapOptional: + return ir_gen_orelse(irb, scope, node, lval, result_loc); + case BinOpTypeErrorUnion: + return ir_lval_wrap(irb, scope, ir_gen_error_union(irb, scope, node), lval, result_loc); + } + zig_unreachable(); +} + +static IrInstSrc *ir_gen_int_lit(IrBuilderSrc *irb, Scope *scope, AstNode *node) { + assert(node->type == NodeTypeIntLiteral); + + return ir_build_const_bigint(irb, scope, node, node->data.int_literal.bigint); +} + +static IrInstSrc *ir_gen_float_lit(IrBuilderSrc *irb, Scope *scope, AstNode *node) { + assert(node->type == NodeTypeFloatLiteral); + + if (node->data.float_literal.overflow) { + add_node_error(irb->codegen, node, buf_sprintf("float literal out of range of any type")); + return irb->codegen->invalid_inst_src; + } + + return ir_build_const_bigfloat(irb, scope, node, node->data.float_literal.bigfloat); +} + +static IrInstSrc *ir_gen_char_lit(IrBuilderSrc *irb, Scope *scope, AstNode *node) { + assert(node->type == NodeTypeCharLiteral); + + return ir_build_const_uint(irb, scope, node, node->data.char_literal.value); +} + +static IrInstSrc *ir_gen_null_literal(IrBuilderSrc *irb, Scope *scope, AstNode *node) { + assert(node->type == NodeTypeNullLiteral); + + return ir_build_const_null(irb, scope, node); +} + +static void populate_invalid_variable_in_scope(CodeGen *g, Scope *scope, AstNode *node, Buf *var_name) { + ScopeDecls *scope_decls = nullptr; + while (scope != nullptr) { + if (scope->id == ScopeIdDecls) { + scope_decls = reinterpret_cast(scope); + } + scope = scope->parent; + } + TldVar *tld_var = heap::c_allocator.create(); + init_tld(&tld_var->base, TldIdVar, var_name, VisibModPub, node, &scope_decls->base); + tld_var->base.resolution = TldResolutionInvalid; + tld_var->var = add_variable(g, node, &scope_decls->base, var_name, false, + g->invalid_inst_gen->value, &tld_var->base, g->builtin_types.entry_invalid); + scope_decls->decl_table.put(var_name, &tld_var->base); +} + +static IrInstSrc *ir_gen_symbol(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *result_loc) { + Error err; + assert(node->type == NodeTypeSymbol); + + Buf *variable_name = node->data.symbol_expr.symbol; + + if (buf_eql_str(variable_name, "_")) { + if (lval == LValAssign) { + IrInstSrcConst *const_instruction = ir_build_instruction(irb, scope, node); + const_instruction->value = irb->codegen->pass1_arena->create(); + const_instruction->value->type = get_pointer_to_type(irb->codegen, + irb->codegen->builtin_types.entry_void, false); + const_instruction->value->special = ConstValSpecialStatic; + const_instruction->value->data.x_ptr.special = ConstPtrSpecialDiscard; + return &const_instruction->base; + } else { + add_node_error(irb->codegen, node, buf_sprintf("`_` may only be used to assign things to")); + return irb->codegen->invalid_inst_src; + } + } + + ZigType *primitive_type; + if ((err = get_primitive_type(irb->codegen, variable_name, &primitive_type))) { + if (err == ErrorOverflow) { + add_node_error(irb->codegen, node, + buf_sprintf("primitive integer type '%s' exceeds maximum bit width of 65535", + buf_ptr(variable_name))); + return irb->codegen->invalid_inst_src; + } + assert(err == ErrorPrimitiveTypeNotFound); + } else { + IrInstSrc *value = ir_build_const_type(irb, scope, node, primitive_type); + if (lval == LValPtr || lval == LValAssign) { + return ir_build_ref_src(irb, scope, node, value); + } else { + return ir_expr_wrap(irb, scope, value, result_loc); + } + } + + ScopeFnDef *crossed_fndef_scope; + ZigVar *var = find_variable(irb->codegen, scope, variable_name, &crossed_fndef_scope); + if (var) { + IrInstSrc *var_ptr = ir_build_var_ptr_x(irb, scope, node, var, crossed_fndef_scope); + if (lval == LValPtr || lval == LValAssign) { + return var_ptr; + } else { + return ir_expr_wrap(irb, scope, ir_build_load_ptr(irb, scope, node, var_ptr), result_loc); + } + } + + Tld *tld = find_decl(irb->codegen, scope, variable_name); + if (tld) { + IrInstSrc *decl_ref = ir_build_decl_ref(irb, scope, node, tld, lval); + if (lval == LValPtr || lval == LValAssign) { + return decl_ref; + } else { + return ir_expr_wrap(irb, scope, decl_ref, result_loc); + } + } + + if (get_container_scope(node->owner)->any_imports_failed) { + // skip the error message since we had a failing import in this file + // if an import breaks we don't need redundant undeclared identifier errors + return irb->codegen->invalid_inst_src; + } + + return ir_build_undeclared_identifier(irb, scope, node, variable_name); +} + +static IrInstSrc *ir_gen_array_access(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, + ResultLoc *result_loc) +{ + assert(node->type == NodeTypeArrayAccessExpr); + + AstNode *array_ref_node = node->data.array_access_expr.array_ref_expr; + IrInstSrc *array_ref_instruction = ir_gen_node_extra(irb, array_ref_node, scope, LValPtr, nullptr); + if (array_ref_instruction == irb->codegen->invalid_inst_src) + return array_ref_instruction; + + // Create an usize-typed result location to hold the subscript value, this + // makes it possible for the compiler to infer the subscript expression type + // if needed + IrInstSrc *usize_type_inst = ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_usize); + ResultLocCast *result_loc_cast = ir_build_cast_result_loc(irb, usize_type_inst, no_result_loc()); + + AstNode *subscript_node = node->data.array_access_expr.subscript; + IrInstSrc *subscript_value = ir_gen_node_extra(irb, subscript_node, scope, LValNone, &result_loc_cast->base); + if (subscript_value == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + + IrInstSrc *subscript_instruction = ir_build_implicit_cast(irb, scope, subscript_node, subscript_value, result_loc_cast); + + IrInstSrc *ptr_instruction = ir_build_elem_ptr(irb, scope, node, array_ref_instruction, + subscript_instruction, true, PtrLenSingle, nullptr); + if (lval == LValPtr || lval == LValAssign) + return ptr_instruction; + + IrInstSrc *load_ptr = ir_build_load_ptr(irb, scope, node, ptr_instruction); + return ir_expr_wrap(irb, scope, load_ptr, result_loc); +} + +static IrInstSrc *ir_gen_field_access(IrBuilderSrc *irb, Scope *scope, AstNode *node) { + assert(node->type == NodeTypeFieldAccessExpr); + + AstNode *container_ref_node = node->data.field_access_expr.struct_expr; + Buf *field_name = node->data.field_access_expr.field_name; + + IrInstSrc *container_ref_instruction = ir_gen_node_extra(irb, container_ref_node, scope, LValPtr, nullptr); + if (container_ref_instruction == irb->codegen->invalid_inst_src) + return container_ref_instruction; + + return ir_build_field_ptr(irb, scope, node, container_ref_instruction, field_name, false); +} + +static IrInstSrc *ir_gen_overflow_op(IrBuilderSrc *irb, Scope *scope, AstNode *node, IrOverflowOp op) { + assert(node->type == NodeTypeFnCallExpr); + + AstNode *type_node = node->data.fn_call_expr.params.at(0); + AstNode *op1_node = node->data.fn_call_expr.params.at(1); + AstNode *op2_node = node->data.fn_call_expr.params.at(2); + AstNode *result_ptr_node = node->data.fn_call_expr.params.at(3); + + + IrInstSrc *type_value = ir_gen_node(irb, type_node, scope); + if (type_value == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + + IrInstSrc *op1 = ir_gen_node(irb, op1_node, scope); + if (op1 == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + + IrInstSrc *op2 = ir_gen_node(irb, op2_node, scope); + if (op2 == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + + IrInstSrc *result_ptr = ir_gen_node(irb, result_ptr_node, scope); + if (result_ptr == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + + return ir_build_overflow_op_src(irb, scope, node, op, type_value, op1, op2, result_ptr); +} + +static IrInstSrc *ir_gen_mul_add(IrBuilderSrc *irb, Scope *scope, AstNode *node) { + assert(node->type == NodeTypeFnCallExpr); + + AstNode *type_node = node->data.fn_call_expr.params.at(0); + AstNode *op1_node = node->data.fn_call_expr.params.at(1); + AstNode *op2_node = node->data.fn_call_expr.params.at(2); + AstNode *op3_node = node->data.fn_call_expr.params.at(3); + + IrInstSrc *type_value = ir_gen_node(irb, type_node, scope); + if (type_value == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + + IrInstSrc *op1 = ir_gen_node(irb, op1_node, scope); + if (op1 == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + + IrInstSrc *op2 = ir_gen_node(irb, op2_node, scope); + if (op2 == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + + IrInstSrc *op3 = ir_gen_node(irb, op3_node, scope); + if (op3 == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + + return ir_build_mul_add_src(irb, scope, node, type_value, op1, op2, op3); +} + +static IrInstSrc *ir_gen_this(IrBuilderSrc *irb, Scope *orig_scope, AstNode *node) { + for (Scope *it_scope = orig_scope; it_scope != nullptr; it_scope = it_scope->parent) { + if (it_scope->id == ScopeIdDecls) { + ScopeDecls *decls_scope = (ScopeDecls *)it_scope; + ZigType *container_type = decls_scope->container_type; + if (container_type != nullptr) { + return ir_build_const_type(irb, orig_scope, node, container_type); + } else { + return ir_build_const_import(irb, orig_scope, node, decls_scope->import); + } + } + } + zig_unreachable(); +} + +static IrInstSrc *ir_gen_async_call(IrBuilderSrc *irb, Scope *scope, AstNode *await_node, AstNode *call_node, + LVal lval, ResultLoc *result_loc) +{ + if (call_node->data.fn_call_expr.params.length != 4) { + add_node_error(irb->codegen, call_node, + buf_sprintf("expected 4 arguments, found %" ZIG_PRI_usize, + call_node->data.fn_call_expr.params.length)); + return irb->codegen->invalid_inst_src; + } + + AstNode *bytes_node = call_node->data.fn_call_expr.params.at(0); + IrInstSrc *bytes = ir_gen_node(irb, bytes_node, scope); + if (bytes == irb->codegen->invalid_inst_src) + return bytes; + + AstNode *ret_ptr_node = call_node->data.fn_call_expr.params.at(1); + IrInstSrc *ret_ptr = ir_gen_node(irb, ret_ptr_node, scope); + if (ret_ptr == irb->codegen->invalid_inst_src) + return ret_ptr; + + AstNode *fn_ref_node = call_node->data.fn_call_expr.params.at(2); + IrInstSrc *fn_ref = ir_gen_node(irb, fn_ref_node, scope); + if (fn_ref == irb->codegen->invalid_inst_src) + return fn_ref; + + CallModifier modifier = (await_node == nullptr) ? CallModifierAsync : CallModifierNone; + bool is_async_call_builtin = true; + AstNode *args_node = call_node->data.fn_call_expr.params.at(3); + if (args_node->type == NodeTypeContainerInitExpr) { + if (args_node->data.container_init_expr.kind == ContainerInitKindArray || + args_node->data.container_init_expr.entries.length == 0) + { + size_t arg_count = args_node->data.container_init_expr.entries.length; + IrInstSrc **args = heap::c_allocator.allocate(arg_count); + for (size_t i = 0; i < arg_count; i += 1) { + AstNode *arg_node = args_node->data.container_init_expr.entries.at(i); + IrInstSrc *arg = ir_gen_node(irb, arg_node, scope); + if (arg == irb->codegen->invalid_inst_src) + return arg; + args[i] = arg; + } + + IrInstSrc *call = ir_build_call_src(irb, scope, call_node, nullptr, fn_ref, arg_count, args, + ret_ptr, modifier, is_async_call_builtin, bytes, result_loc); + return ir_lval_wrap(irb, scope, call, lval, result_loc); + } else { + exec_add_error_node(irb->codegen, irb->exec, args_node, + buf_sprintf("TODO: @asyncCall with anon struct literal")); + return irb->codegen->invalid_inst_src; + } + } + IrInstSrc *args = ir_gen_node(irb, args_node, scope); + if (args == irb->codegen->invalid_inst_src) + return args; + + IrInstSrc *call = ir_build_async_call_extra(irb, scope, call_node, modifier, fn_ref, ret_ptr, bytes, args, result_loc); + return ir_lval_wrap(irb, scope, call, lval, result_loc); +} + +static IrInstSrc *ir_gen_fn_call_with_args(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + AstNode *fn_ref_node, CallModifier modifier, IrInstSrc *options, + AstNode **args_ptr, size_t args_len, LVal lval, ResultLoc *result_loc) +{ + IrInstSrc *fn_ref = ir_gen_node(irb, fn_ref_node, scope); + if (fn_ref == irb->codegen->invalid_inst_src) + return fn_ref; + + IrInstSrc *fn_type = ir_build_typeof_1(irb, scope, source_node, fn_ref); + + IrInstSrc **args = heap::c_allocator.allocate(args_len); + for (size_t i = 0; i < args_len; i += 1) { + AstNode *arg_node = args_ptr[i]; + + IrInstSrc *arg_index = ir_build_const_usize(irb, scope, arg_node, i); + IrInstSrc *arg_type = ir_build_arg_type(irb, scope, source_node, fn_type, arg_index, true); + ResultLoc *no_result = no_result_loc(); + ir_build_reset_result(irb, scope, source_node, no_result); + ResultLocCast *result_loc_cast = ir_build_cast_result_loc(irb, arg_type, no_result); + + IrInstSrc *arg = ir_gen_node_extra(irb, arg_node, scope, LValNone, &result_loc_cast->base); + if (arg == irb->codegen->invalid_inst_src) + return arg; + + args[i] = ir_build_implicit_cast(irb, scope, arg_node, arg, result_loc_cast); + } + + IrInstSrc *fn_call; + if (options != nullptr) { + fn_call = ir_build_call_args(irb, scope, source_node, options, fn_ref, args, args_len, result_loc); + } else { + fn_call = ir_build_call_src(irb, scope, source_node, nullptr, fn_ref, args_len, args, nullptr, + modifier, false, nullptr, result_loc); + } + return ir_lval_wrap(irb, scope, fn_call, lval, result_loc); +} + +static IrInstSrc *ir_gen_builtin_fn_call(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, + ResultLoc *result_loc) +{ + assert(node->type == NodeTypeFnCallExpr); + + AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr; + Buf *name = fn_ref_expr->data.symbol_expr.symbol; + auto entry = irb->codegen->builtin_fn_table.maybe_get(name); + + if (!entry) { + add_node_error(irb->codegen, node, + buf_sprintf("invalid builtin function: '%s'", buf_ptr(name))); + return irb->codegen->invalid_inst_src; + } + + BuiltinFnEntry *builtin_fn = entry->value; + size_t actual_param_count = node->data.fn_call_expr.params.length; + + if (builtin_fn->param_count != SIZE_MAX && builtin_fn->param_count != actual_param_count) { + add_node_error(irb->codegen, node, + buf_sprintf("expected %" ZIG_PRI_usize " argument(s), found %" ZIG_PRI_usize, + builtin_fn->param_count, actual_param_count)); + return irb->codegen->invalid_inst_src; + } + + switch (builtin_fn->id) { + case BuiltinFnIdInvalid: + zig_unreachable(); + case BuiltinFnIdTypeof: + { + Scope *sub_scope = create_typeof_scope(irb->codegen, node, scope); + + size_t arg_count = node->data.fn_call_expr.params.length; + + IrInstSrc *type_of; + + if (arg_count == 0) { + add_node_error(irb->codegen, node, + buf_sprintf("expected at least 1 argument, found 0")); + return irb->codegen->invalid_inst_src; + } else if (arg_count == 1) { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, sub_scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + type_of = ir_build_typeof_1(irb, scope, node, arg0_value); + } else { + IrInstSrc **args = heap::c_allocator.allocate(arg_count); + for (size_t i = 0; i < arg_count; i += 1) { + AstNode *arg_node = node->data.fn_call_expr.params.at(i); + IrInstSrc *arg = ir_gen_node(irb, arg_node, sub_scope); + if (arg == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + args[i] = arg; + } + + type_of = ir_build_typeof_n(irb, scope, node, args, arg_count); + } + return ir_lval_wrap(irb, scope, type_of, lval, result_loc); + } + case BuiltinFnIdSetCold: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + IrInstSrc *set_cold = ir_build_set_cold(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, set_cold, lval, result_loc); + } + case BuiltinFnIdSetRuntimeSafety: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + IrInstSrc *set_safety = ir_build_set_runtime_safety(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, set_safety, lval, result_loc); + } + case BuiltinFnIdSetFloatMode: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + IrInstSrc *set_float_mode = ir_build_set_float_mode(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, set_float_mode, lval, result_loc); + } + case BuiltinFnIdSizeof: + case BuiltinFnIdBitSizeof: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + IrInstSrc *size_of = ir_build_size_of(irb, scope, node, arg0_value, builtin_fn->id == BuiltinFnIdBitSizeof); + return ir_lval_wrap(irb, scope, size_of, lval, result_loc); + } + case BuiltinFnIdImport: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + IrInstSrc *import = ir_build_import(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, import, lval, result_loc); + } + case BuiltinFnIdCImport: + { + IrInstSrc *c_import = ir_build_c_import(irb, scope, node); + return ir_lval_wrap(irb, scope, c_import, lval, result_loc); + } + case BuiltinFnIdCInclude: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + if (!exec_c_import_buf(irb->exec)) { + add_node_error(irb->codegen, node, buf_sprintf("C include valid only inside C import block")); + return irb->codegen->invalid_inst_src; + } + + IrInstSrc *c_include = ir_build_c_include(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, c_include, lval, result_loc); + } + case BuiltinFnIdCDefine: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_inst_src) + return arg1_value; + + if (!exec_c_import_buf(irb->exec)) { + add_node_error(irb->codegen, node, buf_sprintf("C define valid only inside C import block")); + return irb->codegen->invalid_inst_src; + } + + IrInstSrc *c_define = ir_build_c_define(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, c_define, lval, result_loc); + } + case BuiltinFnIdCUndef: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + if (!exec_c_import_buf(irb->exec)) { + add_node_error(irb->codegen, node, buf_sprintf("C undef valid only inside C import block")); + return irb->codegen->invalid_inst_src; + } + + IrInstSrc *c_undef = ir_build_c_undef(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, c_undef, lval, result_loc); + } + case BuiltinFnIdCompileErr: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + IrInstSrc *compile_err = ir_build_compile_err(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, compile_err, lval, result_loc); + } + case BuiltinFnIdCompileLog: + { + IrInstSrc **args = heap::c_allocator.allocate(actual_param_count); + + for (size_t i = 0; i < actual_param_count; i += 1) { + AstNode *arg_node = node->data.fn_call_expr.params.at(i); + args[i] = ir_gen_node(irb, arg_node, scope); + if (args[i] == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + } + + IrInstSrc *compile_log = ir_build_compile_log(irb, scope, node, actual_param_count, args); + return ir_lval_wrap(irb, scope, compile_log, lval, result_loc); + } + case BuiltinFnIdErrName: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + IrInstSrc *err_name = ir_build_err_name(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, err_name, lval, result_loc); + } + case BuiltinFnIdEmbedFile: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + IrInstSrc *embed_file = ir_build_embed_file(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, embed_file, lval, result_loc); + } + case BuiltinFnIdCmpxchgWeak: + case BuiltinFnIdCmpxchgStrong: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_inst_src) + return arg1_value; + + AstNode *arg2_node = node->data.fn_call_expr.params.at(2); + IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope); + if (arg2_value == irb->codegen->invalid_inst_src) + return arg2_value; + + AstNode *arg3_node = node->data.fn_call_expr.params.at(3); + IrInstSrc *arg3_value = ir_gen_node(irb, arg3_node, scope); + if (arg3_value == irb->codegen->invalid_inst_src) + return arg3_value; + + AstNode *arg4_node = node->data.fn_call_expr.params.at(4); + IrInstSrc *arg4_value = ir_gen_node(irb, arg4_node, scope); + if (arg4_value == irb->codegen->invalid_inst_src) + return arg4_value; + + AstNode *arg5_node = node->data.fn_call_expr.params.at(5); + IrInstSrc *arg5_value = ir_gen_node(irb, arg5_node, scope); + if (arg5_value == irb->codegen->invalid_inst_src) + return arg5_value; + + IrInstSrc *cmpxchg = ir_build_cmpxchg_src(irb, scope, node, arg0_value, arg1_value, + arg2_value, arg3_value, arg4_value, arg5_value, (builtin_fn->id == BuiltinFnIdCmpxchgWeak), + result_loc); + return ir_lval_wrap(irb, scope, cmpxchg, lval, result_loc); + } + case BuiltinFnIdFence: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + IrInstSrc *fence = ir_build_fence(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, fence, lval, result_loc); + } + case BuiltinFnIdDivExact: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_inst_src) + return arg1_value; + + IrInstSrc *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivExact, arg0_value, arg1_value, true); + return ir_lval_wrap(irb, scope, bin_op, lval, result_loc); + } + case BuiltinFnIdDivTrunc: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_inst_src) + return arg1_value; + + IrInstSrc *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivTrunc, arg0_value, arg1_value, true); + return ir_lval_wrap(irb, scope, bin_op, lval, result_loc); + } + case BuiltinFnIdDivFloor: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_inst_src) + return arg1_value; + + IrInstSrc *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpDivFloor, arg0_value, arg1_value, true); + return ir_lval_wrap(irb, scope, bin_op, lval, result_loc); + } + case BuiltinFnIdRem: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_inst_src) + return arg1_value; + + IrInstSrc *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpRemRem, arg0_value, arg1_value, true); + return ir_lval_wrap(irb, scope, bin_op, lval, result_loc); + } + case BuiltinFnIdMod: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_inst_src) + return arg1_value; + + IrInstSrc *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpRemMod, arg0_value, arg1_value, true); + return ir_lval_wrap(irb, scope, bin_op, lval, result_loc); + } + case BuiltinFnIdSqrt: + case BuiltinFnIdSin: + case BuiltinFnIdCos: + case BuiltinFnIdExp: + case BuiltinFnIdExp2: + case BuiltinFnIdLog: + case BuiltinFnIdLog2: + case BuiltinFnIdLog10: + case BuiltinFnIdFabs: + case BuiltinFnIdFloor: + case BuiltinFnIdCeil: + case BuiltinFnIdTrunc: + case BuiltinFnIdNearbyInt: + case BuiltinFnIdRound: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + IrInstSrc *inst = ir_build_float_op_src(irb, scope, node, arg0_value, builtin_fn->id); + return ir_lval_wrap(irb, scope, inst, lval, result_loc); + } + case BuiltinFnIdTruncate: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_inst_src) + return arg1_value; + + IrInstSrc *truncate = ir_build_truncate(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, truncate, lval, result_loc); + } + case BuiltinFnIdIntCast: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_inst_src) + return arg1_value; + + IrInstSrc *result = ir_build_int_cast(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, result, lval, result_loc); + } + case BuiltinFnIdFloatCast: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_inst_src) + return arg1_value; + + IrInstSrc *result = ir_build_float_cast(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, result, lval, result_loc); + } + case BuiltinFnIdErrSetCast: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_inst_src) + return arg1_value; + + IrInstSrc *result = ir_build_err_set_cast(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, result, lval, result_loc); + } + case BuiltinFnIdIntToFloat: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_inst_src) + return arg1_value; + + IrInstSrc *result = ir_build_int_to_float(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, result, lval, result_loc); + } + case BuiltinFnIdFloatToInt: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_inst_src) + return arg1_value; + + IrInstSrc *result = ir_build_float_to_int(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, result, lval, result_loc); + } + case BuiltinFnIdErrToInt: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + IrInstSrc *result = ir_build_err_to_int_src(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, result, lval, result_loc); + } + case BuiltinFnIdIntToErr: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + IrInstSrc *result = ir_build_int_to_err_src(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, result, lval, result_loc); + } + case BuiltinFnIdBoolToInt: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + IrInstSrc *result = ir_build_bool_to_int(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, result, lval, result_loc); + } + case BuiltinFnIdVectorType: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_inst_src) + return arg1_value; + + IrInstSrc *vector_type = ir_build_vector_type(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, vector_type, lval, result_loc); + } + case BuiltinFnIdShuffle: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_inst_src) + return arg1_value; + + AstNode *arg2_node = node->data.fn_call_expr.params.at(2); + IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope); + if (arg2_value == irb->codegen->invalid_inst_src) + return arg2_value; + + AstNode *arg3_node = node->data.fn_call_expr.params.at(3); + IrInstSrc *arg3_value = ir_gen_node(irb, arg3_node, scope); + if (arg3_value == irb->codegen->invalid_inst_src) + return arg3_value; + + IrInstSrc *shuffle_vector = ir_build_shuffle_vector(irb, scope, node, + arg0_value, arg1_value, arg2_value, arg3_value); + return ir_lval_wrap(irb, scope, shuffle_vector, lval, result_loc); + } + case BuiltinFnIdSplat: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_inst_src) + return arg1_value; + + IrInstSrc *splat = ir_build_splat_src(irb, scope, node, + arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, splat, lval, result_loc); + } + case BuiltinFnIdMemcpy: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_inst_src) + return arg1_value; + + AstNode *arg2_node = node->data.fn_call_expr.params.at(2); + IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope); + if (arg2_value == irb->codegen->invalid_inst_src) + return arg2_value; + + IrInstSrc *ir_memcpy = ir_build_memcpy_src(irb, scope, node, arg0_value, arg1_value, arg2_value); + return ir_lval_wrap(irb, scope, ir_memcpy, lval, result_loc); + } + case BuiltinFnIdMemset: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_inst_src) + return arg1_value; + + AstNode *arg2_node = node->data.fn_call_expr.params.at(2); + IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope); + if (arg2_value == irb->codegen->invalid_inst_src) + return arg2_value; + + IrInstSrc *ir_memset = ir_build_memset_src(irb, scope, node, arg0_value, arg1_value, arg2_value); + return ir_lval_wrap(irb, scope, ir_memset, lval, result_loc); + } + case BuiltinFnIdWasmMemorySize: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + IrInstSrc *ir_wasm_memory_size = ir_build_wasm_memory_size_src(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, ir_wasm_memory_size, lval, result_loc); + } + case BuiltinFnIdWasmMemoryGrow: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_inst_src) + return arg1_value; + + IrInstSrc *ir_wasm_memory_grow = ir_build_wasm_memory_grow_src(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, ir_wasm_memory_grow, lval, result_loc); + } + case BuiltinFnIdField: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node_extra(irb, arg0_node, scope, LValPtr, nullptr); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_inst_src) + return arg1_value; + + IrInstSrc *ptr_instruction = ir_build_field_ptr_instruction(irb, scope, node, + arg0_value, arg1_value, false); + + if (lval == LValPtr || lval == LValAssign) + return ptr_instruction; + + IrInstSrc *load_ptr = ir_build_load_ptr(irb, scope, node, ptr_instruction); + return ir_expr_wrap(irb, scope, load_ptr, result_loc); + } + case BuiltinFnIdHasField: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_inst_src) + return arg1_value; + + IrInstSrc *type_info = ir_build_has_field(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, type_info, lval, result_loc); + } + case BuiltinFnIdTypeInfo: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + IrInstSrc *type_info = ir_build_type_info(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, type_info, lval, result_loc); + } + case BuiltinFnIdType: + { + AstNode *arg_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg = ir_gen_node(irb, arg_node, scope); + if (arg == irb->codegen->invalid_inst_src) + return arg; + + IrInstSrc *type = ir_build_type(irb, scope, node, arg); + return ir_lval_wrap(irb, scope, type, lval, result_loc); + } + case BuiltinFnIdBreakpoint: + return ir_lval_wrap(irb, scope, ir_build_breakpoint(irb, scope, node), lval, result_loc); + case BuiltinFnIdReturnAddress: + return ir_lval_wrap(irb, scope, ir_build_return_address_src(irb, scope, node), lval, result_loc); + case BuiltinFnIdFrameAddress: + return ir_lval_wrap(irb, scope, ir_build_frame_address_src(irb, scope, node), lval, result_loc); + case BuiltinFnIdFrameHandle: + if (!irb->exec->fn_entry) { + add_node_error(irb->codegen, node, buf_sprintf("@frame() called outside of function definition")); + return irb->codegen->invalid_inst_src; + } + return ir_lval_wrap(irb, scope, ir_build_handle_src(irb, scope, node), lval, result_loc); + case BuiltinFnIdFrameType: { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + IrInstSrc *frame_type = ir_build_frame_type(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, frame_type, lval, result_loc); + } + case BuiltinFnIdFrameSize: { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + IrInstSrc *frame_size = ir_build_frame_size_src(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, frame_size, lval, result_loc); + } + case BuiltinFnIdAlignOf: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + IrInstSrc *align_of = ir_build_align_of(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, align_of, lval, result_loc); + } + case BuiltinFnIdAddWithOverflow: + return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpAdd), lval, result_loc); + case BuiltinFnIdSubWithOverflow: + return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpSub), lval, result_loc); + case BuiltinFnIdMulWithOverflow: + return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpMul), lval, result_loc); + case BuiltinFnIdShlWithOverflow: + return ir_lval_wrap(irb, scope, ir_gen_overflow_op(irb, scope, node, IrOverflowOpShl), lval, result_loc); + case BuiltinFnIdMulAdd: + return ir_lval_wrap(irb, scope, ir_gen_mul_add(irb, scope, node), lval, result_loc); + case BuiltinFnIdTypeName: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + IrInstSrc *type_name = ir_build_type_name(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, type_name, lval, result_loc); + } + case BuiltinFnIdPanic: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + IrInstSrc *panic = ir_build_panic_src(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, panic, lval, result_loc); + } + case BuiltinFnIdPtrCast: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_inst_src) + return arg1_value; + + IrInstSrc *ptr_cast = ir_build_ptr_cast_src(irb, scope, node, arg0_value, arg1_value, true); + return ir_lval_wrap(irb, scope, ptr_cast, lval, result_loc); + } + case BuiltinFnIdBitCast: + { + AstNode *dest_type_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *dest_type = ir_gen_node(irb, dest_type_node, scope); + if (dest_type == irb->codegen->invalid_inst_src) + return dest_type; + + ResultLocBitCast *result_loc_bit_cast = heap::c_allocator.create(); + result_loc_bit_cast->base.id = ResultLocIdBitCast; + result_loc_bit_cast->base.source_instruction = dest_type; + result_loc_bit_cast->base.allow_write_through_const = result_loc->allow_write_through_const; + ir_ref_instruction(dest_type, irb->current_basic_block); + result_loc_bit_cast->parent = result_loc; + + ir_build_reset_result(irb, scope, node, &result_loc_bit_cast->base); + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstSrc *arg1_value = ir_gen_node_extra(irb, arg1_node, scope, LValNone, + &result_loc_bit_cast->base); + if (arg1_value == irb->codegen->invalid_inst_src) + return arg1_value; + + IrInstSrc *bitcast = ir_build_bit_cast_src(irb, scope, arg1_node, arg1_value, result_loc_bit_cast); + return ir_lval_wrap(irb, scope, bitcast, lval, result_loc); + } + case BuiltinFnIdAs: + { + AstNode *dest_type_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *dest_type = ir_gen_node(irb, dest_type_node, scope); + if (dest_type == irb->codegen->invalid_inst_src) + return dest_type; + + ResultLocCast *result_loc_cast = ir_build_cast_result_loc(irb, dest_type, result_loc); + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstSrc *arg1_value = ir_gen_node_extra(irb, arg1_node, scope, LValNone, + &result_loc_cast->base); + if (arg1_value == irb->codegen->invalid_inst_src) + return arg1_value; + + IrInstSrc *result = ir_build_implicit_cast(irb, scope, node, arg1_value, result_loc_cast); + return ir_lval_wrap(irb, scope, result, lval, result_loc); + } + case BuiltinFnIdIntToPtr: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_inst_src) + return arg1_value; + + IrInstSrc *int_to_ptr = ir_build_int_to_ptr_src(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, int_to_ptr, lval, result_loc); + } + case BuiltinFnIdPtrToInt: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + IrInstSrc *ptr_to_int = ir_build_ptr_to_int_src(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, ptr_to_int, lval, result_loc); + } + case BuiltinFnIdTagName: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + IrInstSrc *tag_name = ir_build_tag_name_src(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, tag_name, lval, result_loc); + } + case BuiltinFnIdTagType: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + IrInstSrc *tag_type = ir_build_tag_type(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, tag_type, lval, result_loc); + } + case BuiltinFnIdFieldParentPtr: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_inst_src) + return arg1_value; + + AstNode *arg2_node = node->data.fn_call_expr.params.at(2); + IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope); + if (arg2_value == irb->codegen->invalid_inst_src) + return arg2_value; + + IrInstSrc *field_parent_ptr = ir_build_field_parent_ptr_src(irb, scope, node, + arg0_value, arg1_value, arg2_value); + return ir_lval_wrap(irb, scope, field_parent_ptr, lval, result_loc); + } + case BuiltinFnIdByteOffsetOf: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_inst_src) + return arg1_value; + + IrInstSrc *offset_of = ir_build_byte_offset_of(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, offset_of, lval, result_loc); + } + case BuiltinFnIdBitOffsetOf: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_inst_src) + return arg1_value; + + IrInstSrc *offset_of = ir_build_bit_offset_of(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, offset_of, lval, result_loc); + } + case BuiltinFnIdCall: { + // Cast the options parameter to the options type + ZigType *options_type = get_builtin_type(irb->codegen, "CallOptions"); + IrInstSrc *options_type_inst = ir_build_const_type(irb, scope, node, options_type); + ResultLocCast *result_loc_cast = ir_build_cast_result_loc(irb, options_type_inst, no_result_loc()); + + AstNode *options_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *options_inner = ir_gen_node_extra(irb, options_node, scope, + LValNone, &result_loc_cast->base); + if (options_inner == irb->codegen->invalid_inst_src) + return options_inner; + IrInstSrc *options = ir_build_implicit_cast(irb, scope, options_node, options_inner, result_loc_cast); + + AstNode *fn_ref_node = node->data.fn_call_expr.params.at(1); + AstNode *args_node = node->data.fn_call_expr.params.at(2); + if (args_node->type == NodeTypeContainerInitExpr) { + if (args_node->data.container_init_expr.kind == ContainerInitKindArray || + args_node->data.container_init_expr.entries.length == 0) + { + return ir_gen_fn_call_with_args(irb, scope, node, + fn_ref_node, CallModifierNone, options, + args_node->data.container_init_expr.entries.items, + args_node->data.container_init_expr.entries.length, + lval, result_loc); + } else { + exec_add_error_node(irb->codegen, irb->exec, args_node, + buf_sprintf("TODO: @call with anon struct literal")); + return irb->codegen->invalid_inst_src; + } + } else { + IrInstSrc *fn_ref = ir_gen_node(irb, fn_ref_node, scope); + if (fn_ref == irb->codegen->invalid_inst_src) + return fn_ref; + + IrInstSrc *args = ir_gen_node(irb, args_node, scope); + if (args == irb->codegen->invalid_inst_src) + return args; + + IrInstSrc *call = ir_build_call_extra(irb, scope, node, options, fn_ref, args, result_loc); + return ir_lval_wrap(irb, scope, call, lval, result_loc); + } + } + case BuiltinFnIdAsyncCall: + return ir_gen_async_call(irb, scope, nullptr, node, lval, result_loc); + case BuiltinFnIdShlExact: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_inst_src) + return arg1_value; + + IrInstSrc *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpBitShiftLeftExact, arg0_value, arg1_value, true); + return ir_lval_wrap(irb, scope, bin_op, lval, result_loc); + } + case BuiltinFnIdShrExact: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_inst_src) + return arg1_value; + + IrInstSrc *bin_op = ir_build_bin_op(irb, scope, node, IrBinOpBitShiftRightExact, arg0_value, arg1_value, true); + return ir_lval_wrap(irb, scope, bin_op, lval, result_loc); + } + case BuiltinFnIdSetEvalBranchQuota: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + IrInstSrc *set_eval_branch_quota = ir_build_set_eval_branch_quota(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, set_eval_branch_quota, lval, result_loc); + } + case BuiltinFnIdAlignCast: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_inst_src) + return arg1_value; + + IrInstSrc *align_cast = ir_build_align_cast_src(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, align_cast, lval, result_loc); + } + case BuiltinFnIdThis: + { + IrInstSrc *this_inst = ir_gen_this(irb, scope, node); + return ir_lval_wrap(irb, scope, this_inst, lval, result_loc); + } + case BuiltinFnIdSetAlignStack: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + IrInstSrc *set_align_stack = ir_build_set_align_stack(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, set_align_stack, lval, result_loc); + } + case BuiltinFnIdExport: + { + // Cast the options parameter to the options type + ZigType *options_type = get_builtin_type(irb->codegen, "ExportOptions"); + IrInstSrc *options_type_inst = ir_build_const_type(irb, scope, node, options_type); + ResultLocCast *result_loc_cast = ir_build_cast_result_loc(irb, options_type_inst, no_result_loc()); + + AstNode *target_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *target_value = ir_gen_node(irb, target_node, scope); + if (target_value == irb->codegen->invalid_inst_src) + return target_value; + + AstNode *options_node = node->data.fn_call_expr.params.at(1); + IrInstSrc *options_value = ir_gen_node_extra(irb, options_node, + scope, LValNone, &result_loc_cast->base); + if (options_value == irb->codegen->invalid_inst_src) + return options_value; + + IrInstSrc *casted_options_value = ir_build_implicit_cast( + irb, scope, options_node, options_value, result_loc_cast); + + IrInstSrc *ir_export = ir_build_export(irb, scope, node, target_value, casted_options_value); + return ir_lval_wrap(irb, scope, ir_export, lval, result_loc); + } + case BuiltinFnIdErrorReturnTrace: + { + IrInstSrc *error_return_trace = ir_build_error_return_trace_src(irb, scope, node, + IrInstErrorReturnTraceNull); + return ir_lval_wrap(irb, scope, error_return_trace, lval, result_loc); + } + case BuiltinFnIdAtomicRmw: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_inst_src) + return arg1_value; + + AstNode *arg2_node = node->data.fn_call_expr.params.at(2); + IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope); + if (arg2_value == irb->codegen->invalid_inst_src) + return arg2_value; + + AstNode *arg3_node = node->data.fn_call_expr.params.at(3); + IrInstSrc *arg3_value = ir_gen_node(irb, arg3_node, scope); + if (arg3_value == irb->codegen->invalid_inst_src) + return arg3_value; + + AstNode *arg4_node = node->data.fn_call_expr.params.at(4); + IrInstSrc *arg4_value = ir_gen_node(irb, arg4_node, scope); + if (arg4_value == irb->codegen->invalid_inst_src) + return arg4_value; + + IrInstSrc *inst = ir_build_atomic_rmw_src(irb, scope, node, + arg0_value, arg1_value, arg2_value, arg3_value, arg4_value); + return ir_lval_wrap(irb, scope, inst, lval, result_loc); + } + case BuiltinFnIdAtomicLoad: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_inst_src) + return arg1_value; + + AstNode *arg2_node = node->data.fn_call_expr.params.at(2); + IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope); + if (arg2_value == irb->codegen->invalid_inst_src) + return arg2_value; + + IrInstSrc *inst = ir_build_atomic_load_src(irb, scope, node, arg0_value, arg1_value, arg2_value); + return ir_lval_wrap(irb, scope, inst, lval, result_loc); + } + case BuiltinFnIdAtomicStore: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_inst_src) + return arg1_value; + + AstNode *arg2_node = node->data.fn_call_expr.params.at(2); + IrInstSrc *arg2_value = ir_gen_node(irb, arg2_node, scope); + if (arg2_value == irb->codegen->invalid_inst_src) + return arg2_value; + + AstNode *arg3_node = node->data.fn_call_expr.params.at(3); + IrInstSrc *arg3_value = ir_gen_node(irb, arg3_node, scope); + if (arg3_value == irb->codegen->invalid_inst_src) + return arg3_value; + + IrInstSrc *inst = ir_build_atomic_store_src(irb, scope, node, arg0_value, arg1_value, + arg2_value, arg3_value); + return ir_lval_wrap(irb, scope, inst, lval, result_loc); + } + case BuiltinFnIdIntToEnum: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_inst_src) + return arg1_value; + + IrInstSrc *result = ir_build_int_to_enum_src(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, result, lval, result_loc); + } + case BuiltinFnIdEnumToInt: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + IrInstSrc *result = ir_build_enum_to_int(irb, scope, node, arg0_value); + return ir_lval_wrap(irb, scope, result, lval, result_loc); + } + case BuiltinFnIdCtz: + case BuiltinFnIdPopCount: + case BuiltinFnIdClz: + case BuiltinFnIdBswap: + case BuiltinFnIdBitReverse: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_inst_src) + return arg1_value; + + IrInstSrc *result; + switch (builtin_fn->id) { + case BuiltinFnIdCtz: + result = ir_build_ctz(irb, scope, node, arg0_value, arg1_value); + break; + case BuiltinFnIdPopCount: + result = ir_build_pop_count(irb, scope, node, arg0_value, arg1_value); + break; + case BuiltinFnIdClz: + result = ir_build_clz(irb, scope, node, arg0_value, arg1_value); + break; + case BuiltinFnIdBswap: + result = ir_build_bswap(irb, scope, node, arg0_value, arg1_value); + break; + case BuiltinFnIdBitReverse: + result = ir_build_bit_reverse(irb, scope, node, arg0_value, arg1_value); + break; + default: + zig_unreachable(); + } + return ir_lval_wrap(irb, scope, result, lval, result_loc); + } + case BuiltinFnIdHasDecl: + { + AstNode *arg0_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *arg0_value = ir_gen_node(irb, arg0_node, scope); + if (arg0_value == irb->codegen->invalid_inst_src) + return arg0_value; + + AstNode *arg1_node = node->data.fn_call_expr.params.at(1); + IrInstSrc *arg1_value = ir_gen_node(irb, arg1_node, scope); + if (arg1_value == irb->codegen->invalid_inst_src) + return arg1_value; + + IrInstSrc *has_decl = ir_build_has_decl(irb, scope, node, arg0_value, arg1_value); + return ir_lval_wrap(irb, scope, has_decl, lval, result_loc); + } + case BuiltinFnIdUnionInit: + { + AstNode *union_type_node = node->data.fn_call_expr.params.at(0); + IrInstSrc *union_type_inst = ir_gen_node(irb, union_type_node, scope); + if (union_type_inst == irb->codegen->invalid_inst_src) + return union_type_inst; + + AstNode *name_node = node->data.fn_call_expr.params.at(1); + IrInstSrc *name_inst = ir_gen_node(irb, name_node, scope); + if (name_inst == irb->codegen->invalid_inst_src) + return name_inst; + + AstNode *init_node = node->data.fn_call_expr.params.at(2); + + return ir_gen_union_init_expr(irb, scope, node, union_type_inst, name_inst, init_node, + lval, result_loc); + } + case BuiltinFnIdSrc: + { + IrInstSrc *src_inst = ir_build_src(irb, scope, node); + return ir_lval_wrap(irb, scope, src_inst, lval, result_loc); + } + } + zig_unreachable(); +} + +static ScopeNoSuspend *get_scope_nosuspend(Scope *scope) { + while (scope) { + if (scope->id == ScopeIdNoSuspend) + return (ScopeNoSuspend *)scope; + if (scope->id == ScopeIdFnDef) + return nullptr; + + scope = scope->parent; + } + return nullptr; +} + +static IrInstSrc *ir_gen_fn_call(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, + ResultLoc *result_loc) +{ + assert(node->type == NodeTypeFnCallExpr); + + if (node->data.fn_call_expr.modifier == CallModifierBuiltin) + return ir_gen_builtin_fn_call(irb, scope, node, lval, result_loc); + + bool is_nosuspend = get_scope_nosuspend(scope) != nullptr; + CallModifier modifier = node->data.fn_call_expr.modifier; + if (is_nosuspend) { + if (modifier == CallModifierAsync) { + add_node_error(irb->codegen, node, + buf_sprintf("async call in nosuspend scope")); + return irb->codegen->invalid_inst_src; + } + modifier = CallModifierNoSuspend; + } + + AstNode *fn_ref_node = node->data.fn_call_expr.fn_ref_expr; + return ir_gen_fn_call_with_args(irb, scope, node, fn_ref_node, modifier, + nullptr, node->data.fn_call_expr.params.items, node->data.fn_call_expr.params.length, lval, result_loc); +} + +static IrInstSrc *ir_gen_if_bool_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, + ResultLoc *result_loc) +{ + assert(node->type == NodeTypeIfBoolExpr); + + IrInstSrc *condition = ir_gen_node(irb, node->data.if_bool_expr.condition, scope); + if (condition == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + + IrInstSrc *is_comptime; + if (ir_should_inline(irb->exec, scope)) { + is_comptime = ir_build_const_bool(irb, scope, node, true); + } else { + is_comptime = ir_build_test_comptime(irb, scope, node, condition); + } + + AstNode *then_node = node->data.if_bool_expr.then_block; + AstNode *else_node = node->data.if_bool_expr.else_node; + + IrBasicBlockSrc *then_block = ir_create_basic_block(irb, scope, "Then"); + IrBasicBlockSrc *else_block = ir_create_basic_block(irb, scope, "Else"); + IrBasicBlockSrc *endif_block = ir_create_basic_block(irb, scope, "EndIf"); + + IrInstSrc *cond_br_inst = ir_build_cond_br(irb, scope, node, condition, + then_block, else_block, is_comptime); + ResultLocPeerParent *peer_parent = ir_build_binary_result_peers(irb, cond_br_inst, else_block, endif_block, + result_loc, is_comptime); + + ir_set_cursor_at_end_and_append_block(irb, then_block); + + Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime); + IrInstSrc *then_expr_result = ir_gen_node_extra(irb, then_node, subexpr_scope, lval, + &peer_parent->peers.at(0)->base); + if (then_expr_result == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + IrBasicBlockSrc *after_then_block = irb->current_basic_block; + if (!instr_is_unreachable(then_expr_result)) + ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); + + ir_set_cursor_at_end_and_append_block(irb, else_block); + IrInstSrc *else_expr_result; + if (else_node) { + else_expr_result = ir_gen_node_extra(irb, else_node, subexpr_scope, lval, &peer_parent->peers.at(1)->base); + if (else_expr_result == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + } else { + else_expr_result = ir_build_const_void(irb, scope, node); + ir_build_end_expr(irb, scope, node, else_expr_result, &peer_parent->peers.at(1)->base); + } + IrBasicBlockSrc *after_else_block = irb->current_basic_block; + if (!instr_is_unreachable(else_expr_result)) + ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); + + ir_set_cursor_at_end_and_append_block(irb, endif_block); + IrInstSrc **incoming_values = heap::c_allocator.allocate(2); + incoming_values[0] = then_expr_result; + incoming_values[1] = else_expr_result; + IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate(2); + incoming_blocks[0] = after_then_block; + incoming_blocks[1] = after_else_block; + + IrInstSrc *phi = ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values, peer_parent); + return ir_expr_wrap(irb, scope, phi, result_loc); +} + +static IrInstSrc *ir_gen_prefix_op_id_lval(IrBuilderSrc *irb, Scope *scope, AstNode *node, IrUnOp op_id, LVal lval) { + assert(node->type == NodeTypePrefixOpExpr); + AstNode *expr_node = node->data.prefix_op_expr.primary_expr; + + IrInstSrc *value = ir_gen_node_extra(irb, expr_node, scope, lval, nullptr); + if (value == irb->codegen->invalid_inst_src) + return value; + + return ir_build_un_op(irb, scope, node, op_id, value); +} + +static IrInstSrc *ir_gen_prefix_op_id(IrBuilderSrc *irb, Scope *scope, AstNode *node, IrUnOp op_id) { + return ir_gen_prefix_op_id_lval(irb, scope, node, op_id, LValNone); +} + +static IrInstSrc *ir_expr_wrap(IrBuilderSrc *irb, Scope *scope, IrInstSrc *inst, ResultLoc *result_loc) { + if (inst == irb->codegen->invalid_inst_src) return inst; + ir_build_end_expr(irb, scope, inst->base.source_node, inst, result_loc); + return inst; +} + +static IrInstSrc *ir_lval_wrap(IrBuilderSrc *irb, Scope *scope, IrInstSrc *value, LVal lval, + ResultLoc *result_loc) +{ + // This logic must be kept in sync with + // [STMT_EXPR_TEST_THING] <--- (search this token) + if (value == irb->codegen->invalid_inst_src || + instr_is_unreachable(value) || + value->base.source_node->type == NodeTypeDefer || + value->id == IrInstSrcIdDeclVar) + { + return value; + } + + assert(lval != LValAssign); + if (lval == LValPtr) { + // We needed a pointer to a value, but we got a value. So we create + // an instruction which just makes a pointer of it. + return ir_build_ref_src(irb, scope, value->base.source_node, value); + } else if (result_loc != nullptr) { + return ir_expr_wrap(irb, scope, value, result_loc); + } else { + return value; + } + +} + +static PtrLen star_token_to_ptr_len(TokenId token_id) { + switch (token_id) { + case TokenIdStar: + case TokenIdStarStar: + return PtrLenSingle; + case TokenIdLBracket: + return PtrLenUnknown; + case TokenIdSymbol: + return PtrLenC; + default: + zig_unreachable(); + } +} + +static IrInstSrc *ir_gen_pointer_type(IrBuilderSrc *irb, Scope *scope, AstNode *node) { + assert(node->type == NodeTypePointerType); + + PtrLen ptr_len = star_token_to_ptr_len(node->data.pointer_type.star_token->id); + + bool is_const = node->data.pointer_type.is_const; + bool is_volatile = node->data.pointer_type.is_volatile; + bool is_allow_zero = node->data.pointer_type.allow_zero_token != nullptr; + AstNode *sentinel_expr = node->data.pointer_type.sentinel; + AstNode *expr_node = node->data.pointer_type.op_expr; + AstNode *align_expr = node->data.pointer_type.align_expr; + + IrInstSrc *sentinel; + if (sentinel_expr != nullptr) { + sentinel = ir_gen_node(irb, sentinel_expr, scope); + if (sentinel == irb->codegen->invalid_inst_src) + return sentinel; + } else { + sentinel = nullptr; + } + + IrInstSrc *align_value; + if (align_expr != nullptr) { + align_value = ir_gen_node(irb, align_expr, scope); + if (align_value == irb->codegen->invalid_inst_src) + return align_value; + } else { + align_value = nullptr; + } + + IrInstSrc *child_type = ir_gen_node(irb, expr_node, scope); + if (child_type == irb->codegen->invalid_inst_src) + return child_type; + + uint32_t bit_offset_start = 0; + if (node->data.pointer_type.bit_offset_start != nullptr) { + if (!bigint_fits_in_bits(node->data.pointer_type.bit_offset_start, 32, false)) { + Buf *val_buf = buf_alloc(); + bigint_append_buf(val_buf, node->data.pointer_type.bit_offset_start, 10); + exec_add_error_node(irb->codegen, irb->exec, node, + buf_sprintf("value %s too large for u32 bit offset", buf_ptr(val_buf))); + return irb->codegen->invalid_inst_src; + } + bit_offset_start = bigint_as_u32(node->data.pointer_type.bit_offset_start); + } + + uint32_t host_int_bytes = 0; + if (node->data.pointer_type.host_int_bytes != nullptr) { + if (!bigint_fits_in_bits(node->data.pointer_type.host_int_bytes, 32, false)) { + Buf *val_buf = buf_alloc(); + bigint_append_buf(val_buf, node->data.pointer_type.host_int_bytes, 10); + exec_add_error_node(irb->codegen, irb->exec, node, + buf_sprintf("value %s too large for u32 byte count", buf_ptr(val_buf))); + return irb->codegen->invalid_inst_src; + } + host_int_bytes = bigint_as_u32(node->data.pointer_type.host_int_bytes); + } + + if (host_int_bytes != 0 && bit_offset_start >= host_int_bytes * 8) { + exec_add_error_node(irb->codegen, irb->exec, node, + buf_sprintf("bit offset starts after end of host integer")); + return irb->codegen->invalid_inst_src; + } + + return ir_build_ptr_type(irb, scope, node, child_type, is_const, is_volatile, + ptr_len, sentinel, align_value, bit_offset_start, host_int_bytes, is_allow_zero); +} + +static IrInstSrc *ir_gen_catch_unreachable(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + AstNode *expr_node, LVal lval, ResultLoc *result_loc) +{ + IrInstSrc *err_union_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr, nullptr); + if (err_union_ptr == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + + IrInstSrc *payload_ptr = ir_build_unwrap_err_payload_src(irb, scope, source_node, err_union_ptr, true, false); + if (payload_ptr == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + + if (lval == LValPtr) + return payload_ptr; + + IrInstSrc *load_ptr = ir_build_load_ptr(irb, scope, source_node, payload_ptr); + return ir_expr_wrap(irb, scope, load_ptr, result_loc); +} + +static IrInstSrc *ir_gen_bool_not(IrBuilderSrc *irb, Scope *scope, AstNode *node) { + assert(node->type == NodeTypePrefixOpExpr); + AstNode *expr_node = node->data.prefix_op_expr.primary_expr; + + IrInstSrc *value = ir_gen_node(irb, expr_node, scope); + if (value == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + + return ir_build_bool_not(irb, scope, node, value); +} + +static IrInstSrc *ir_gen_prefix_op_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, + ResultLoc *result_loc) +{ + assert(node->type == NodeTypePrefixOpExpr); + + PrefixOp prefix_op = node->data.prefix_op_expr.prefix_op; + + switch (prefix_op) { + case PrefixOpInvalid: + zig_unreachable(); + case PrefixOpBoolNot: + return ir_lval_wrap(irb, scope, ir_gen_bool_not(irb, scope, node), lval, result_loc); + case PrefixOpBinNot: + return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpBinNot), lval, result_loc); + case PrefixOpNegation: + return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegation), lval, result_loc); + case PrefixOpNegationWrap: + return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpNegationWrap), lval, result_loc); + case PrefixOpOptional: + return ir_lval_wrap(irb, scope, ir_gen_prefix_op_id(irb, scope, node, IrUnOpOptional), lval, result_loc); + case PrefixOpAddrOf: { + AstNode *expr_node = node->data.prefix_op_expr.primary_expr; + return ir_lval_wrap(irb, scope, ir_gen_node_extra(irb, expr_node, scope, LValPtr, nullptr), lval, result_loc); + } + } + zig_unreachable(); +} + +static IrInstSrc *ir_gen_union_init_expr(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, + IrInstSrc *union_type, IrInstSrc *field_name, AstNode *expr_node, + LVal lval, ResultLoc *parent_result_loc) +{ + IrInstSrc *container_ptr = ir_build_resolve_result(irb, scope, source_node, parent_result_loc, union_type); + IrInstSrc *field_ptr = ir_build_field_ptr_instruction(irb, scope, source_node, container_ptr, + field_name, true); + + ResultLocInstruction *result_loc_inst = heap::c_allocator.create(); + result_loc_inst->base.id = ResultLocIdInstruction; + result_loc_inst->base.source_instruction = field_ptr; + ir_ref_instruction(field_ptr, irb->current_basic_block); + ir_build_reset_result(irb, scope, expr_node, &result_loc_inst->base); + + IrInstSrc *expr_value = ir_gen_node_extra(irb, expr_node, scope, LValNone, + &result_loc_inst->base); + if (expr_value == irb->codegen->invalid_inst_src) + return expr_value; + + IrInstSrc *init_union = ir_build_union_init_named_field(irb, scope, source_node, union_type, + field_name, field_ptr, container_ptr); + + return ir_lval_wrap(irb, scope, init_union, lval, parent_result_loc); +} + +static IrInstSrc *ir_gen_container_init_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, + ResultLoc *parent_result_loc) +{ + assert(node->type == NodeTypeContainerInitExpr); + + AstNodeContainerInitExpr *container_init_expr = &node->data.container_init_expr; + ContainerInitKind kind = container_init_expr->kind; + + ResultLocCast *result_loc_cast = nullptr; + ResultLoc *child_result_loc; + AstNode *init_array_type_source_node; + if (container_init_expr->type != nullptr) { + IrInstSrc *container_type; + if (container_init_expr->type->type == NodeTypeInferredArrayType) { + if (kind == ContainerInitKindStruct) { + add_node_error(irb->codegen, container_init_expr->type, + buf_sprintf("initializing array with struct syntax")); + return irb->codegen->invalid_inst_src; + } + IrInstSrc *sentinel; + if (container_init_expr->type->data.inferred_array_type.sentinel != nullptr) { + sentinel = ir_gen_node(irb, container_init_expr->type->data.inferred_array_type.sentinel, scope); + if (sentinel == irb->codegen->invalid_inst_src) + return sentinel; + } else { + sentinel = nullptr; + } + + IrInstSrc *elem_type = ir_gen_node(irb, + container_init_expr->type->data.inferred_array_type.child_type, scope); + if (elem_type == irb->codegen->invalid_inst_src) + return elem_type; + size_t item_count = container_init_expr->entries.length; + IrInstSrc *item_count_inst = ir_build_const_usize(irb, scope, node, item_count); + container_type = ir_build_array_type(irb, scope, node, item_count_inst, sentinel, elem_type); + } else { + container_type = ir_gen_node(irb, container_init_expr->type, scope); + if (container_type == irb->codegen->invalid_inst_src) + return container_type; + } + + result_loc_cast = ir_build_cast_result_loc(irb, container_type, parent_result_loc); + child_result_loc = &result_loc_cast->base; + init_array_type_source_node = container_type->base.source_node; + } else { + child_result_loc = parent_result_loc; + if (parent_result_loc->source_instruction != nullptr) { + init_array_type_source_node = parent_result_loc->source_instruction->base.source_node; + } else { + init_array_type_source_node = node; + } + } + + switch (kind) { + case ContainerInitKindStruct: { + IrInstSrc *container_ptr = ir_build_resolve_result(irb, scope, node, child_result_loc, + nullptr); + + size_t field_count = container_init_expr->entries.length; + IrInstSrcContainerInitFieldsField *fields = heap::c_allocator.allocate(field_count); + for (size_t i = 0; i < field_count; i += 1) { + AstNode *entry_node = container_init_expr->entries.at(i); + assert(entry_node->type == NodeTypeStructValueField); + + Buf *name = entry_node->data.struct_val_field.name; + AstNode *expr_node = entry_node->data.struct_val_field.expr; + + IrInstSrc *field_ptr = ir_build_field_ptr(irb, scope, entry_node, container_ptr, name, true); + ResultLocInstruction *result_loc_inst = heap::c_allocator.create(); + result_loc_inst->base.id = ResultLocIdInstruction; + result_loc_inst->base.source_instruction = field_ptr; + result_loc_inst->base.allow_write_through_const = true; + ir_ref_instruction(field_ptr, irb->current_basic_block); + ir_build_reset_result(irb, scope, expr_node, &result_loc_inst->base); + + IrInstSrc *expr_value = ir_gen_node_extra(irb, expr_node, scope, LValNone, + &result_loc_inst->base); + if (expr_value == irb->codegen->invalid_inst_src) + return expr_value; + + fields[i].name = name; + fields[i].source_node = entry_node; + fields[i].result_loc = field_ptr; + } + IrInstSrc *result = ir_build_container_init_fields(irb, scope, node, field_count, + fields, container_ptr); + + if (result_loc_cast != nullptr) { + result = ir_build_implicit_cast(irb, scope, node, result, result_loc_cast); + } + return ir_lval_wrap(irb, scope, result, lval, parent_result_loc); + } + case ContainerInitKindArray: { + size_t item_count = container_init_expr->entries.length; + + IrInstSrc *container_ptr = ir_build_resolve_result(irb, scope, node, child_result_loc, + nullptr); + + IrInstSrc **result_locs = heap::c_allocator.allocate(item_count); + for (size_t i = 0; i < item_count; i += 1) { + AstNode *expr_node = container_init_expr->entries.at(i); + + IrInstSrc *elem_index = ir_build_const_usize(irb, scope, expr_node, i); + IrInstSrc *elem_ptr = ir_build_elem_ptr(irb, scope, expr_node, container_ptr, + elem_index, false, PtrLenSingle, init_array_type_source_node); + ResultLocInstruction *result_loc_inst = heap::c_allocator.create(); + result_loc_inst->base.id = ResultLocIdInstruction; + result_loc_inst->base.source_instruction = elem_ptr; + result_loc_inst->base.allow_write_through_const = true; + ir_ref_instruction(elem_ptr, irb->current_basic_block); + ir_build_reset_result(irb, scope, expr_node, &result_loc_inst->base); + + IrInstSrc *expr_value = ir_gen_node_extra(irb, expr_node, scope, LValNone, + &result_loc_inst->base); + if (expr_value == irb->codegen->invalid_inst_src) + return expr_value; + + result_locs[i] = elem_ptr; + } + IrInstSrc *result = ir_build_container_init_list(irb, scope, node, item_count, + result_locs, container_ptr, init_array_type_source_node); + if (result_loc_cast != nullptr) { + result = ir_build_implicit_cast(irb, scope, node, result, result_loc_cast); + } + return ir_lval_wrap(irb, scope, result, lval, parent_result_loc); + } + } + zig_unreachable(); +} + +static ResultLocVar *ir_build_var_result_loc(IrBuilderSrc *irb, IrInstSrc *alloca, ZigVar *var) { + ResultLocVar *result_loc_var = heap::c_allocator.create(); + result_loc_var->base.id = ResultLocIdVar; + result_loc_var->base.source_instruction = alloca; + result_loc_var->base.allow_write_through_const = true; + result_loc_var->var = var; + + ir_build_reset_result(irb, alloca->base.scope, alloca->base.source_node, &result_loc_var->base); + + return result_loc_var; +} + +static ResultLocCast *ir_build_cast_result_loc(IrBuilderSrc *irb, IrInstSrc *dest_type, + ResultLoc *parent_result_loc) +{ + ResultLocCast *result_loc_cast = heap::c_allocator.create(); + result_loc_cast->base.id = ResultLocIdCast; + result_loc_cast->base.source_instruction = dest_type; + result_loc_cast->base.allow_write_through_const = parent_result_loc->allow_write_through_const; + ir_ref_instruction(dest_type, irb->current_basic_block); + result_loc_cast->parent = parent_result_loc; + + ir_build_reset_result(irb, dest_type->base.scope, dest_type->base.source_node, &result_loc_cast->base); + + return result_loc_cast; +} + +static void build_decl_var_and_init(IrBuilderSrc *irb, Scope *scope, AstNode *source_node, ZigVar *var, + IrInstSrc *init, const char *name_hint, IrInstSrc *is_comptime) +{ + IrInstSrc *alloca = ir_build_alloca_src(irb, scope, source_node, nullptr, name_hint, is_comptime); + ResultLocVar *var_result_loc = ir_build_var_result_loc(irb, alloca, var); + ir_build_end_expr(irb, scope, source_node, init, &var_result_loc->base); + ir_build_var_decl_src(irb, scope, source_node, var, nullptr, alloca); +} + +static IrInstSrc *ir_gen_var_decl(IrBuilderSrc *irb, Scope *scope, AstNode *node) { + assert(node->type == NodeTypeVariableDeclaration); + + AstNodeVariableDeclaration *variable_declaration = &node->data.variable_declaration; + + if (buf_eql_str(variable_declaration->symbol, "_")) { + add_node_error(irb->codegen, node, buf_sprintf("`_` is not a declarable symbol")); + return irb->codegen->invalid_inst_src; + } + + // Used for the type expr and the align expr + Scope *comptime_scope = create_comptime_scope(irb->codegen, node, scope); + + IrInstSrc *type_instruction; + if (variable_declaration->type != nullptr) { + type_instruction = ir_gen_node(irb, variable_declaration->type, comptime_scope); + if (type_instruction == irb->codegen->invalid_inst_src) + return type_instruction; + } else { + type_instruction = nullptr; + } + + bool is_shadowable = false; + bool is_const = variable_declaration->is_const; + bool is_extern = variable_declaration->is_extern; + + bool is_comptime_scalar = ir_should_inline(irb->exec, scope) || variable_declaration->is_comptime; + IrInstSrc *is_comptime = ir_build_const_bool(irb, scope, node, is_comptime_scalar); + ZigVar *var = ir_create_var(irb, node, scope, variable_declaration->symbol, + is_const, is_const, is_shadowable, is_comptime); + // we detect IrInstSrcDeclVar in gen_block to make sure the next node + // is inside var->child_scope + + if (!is_extern && !variable_declaration->expr) { + var->var_type = irb->codegen->builtin_types.entry_invalid; + add_node_error(irb->codegen, node, buf_sprintf("variables must be initialized")); + return irb->codegen->invalid_inst_src; + } + + IrInstSrc *align_value = nullptr; + if (variable_declaration->align_expr != nullptr) { + align_value = ir_gen_node(irb, variable_declaration->align_expr, comptime_scope); + if (align_value == irb->codegen->invalid_inst_src) + return align_value; + } + + if (variable_declaration->section_expr != nullptr) { + add_node_error(irb->codegen, variable_declaration->section_expr, + buf_sprintf("cannot set section of local variable '%s'", buf_ptr(variable_declaration->symbol))); + } + + // Parser should ensure that this never happens + assert(variable_declaration->threadlocal_tok == nullptr); + + IrInstSrc *alloca = ir_build_alloca_src(irb, scope, node, align_value, + buf_ptr(variable_declaration->symbol), is_comptime); + + // Create a result location for the initialization expression. + ResultLocVar *result_loc_var = ir_build_var_result_loc(irb, alloca, var); + ResultLoc *init_result_loc; + ResultLocCast *result_loc_cast; + if (type_instruction != nullptr) { + result_loc_cast = ir_build_cast_result_loc(irb, type_instruction, &result_loc_var->base); + init_result_loc = &result_loc_cast->base; + } else { + result_loc_cast = nullptr; + init_result_loc = &result_loc_var->base; + } + + Scope *init_scope = is_comptime_scalar ? + create_comptime_scope(irb->codegen, variable_declaration->expr, scope) : scope; + + // Temporarily set the name of the IrExecutableSrc to the VariableDeclaration + // so that the struct or enum from the init expression inherits the name. + Buf *old_exec_name = irb->exec->name; + irb->exec->name = variable_declaration->symbol; + IrInstSrc *init_value = ir_gen_node_extra(irb, variable_declaration->expr, init_scope, + LValNone, init_result_loc); + irb->exec->name = old_exec_name; + + if (init_value == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + + if (result_loc_cast != nullptr) { + IrInstSrc *implicit_cast = ir_build_implicit_cast(irb, scope, init_value->base.source_node, + init_value, result_loc_cast); + ir_build_end_expr(irb, scope, node, implicit_cast, &result_loc_var->base); + } + + return ir_build_var_decl_src(irb, scope, node, var, align_value, alloca); +} + +static IrInstSrc *ir_gen_while_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, + ResultLoc *result_loc) +{ + assert(node->type == NodeTypeWhileExpr); + + AstNode *continue_expr_node = node->data.while_expr.continue_expr; + AstNode *else_node = node->data.while_expr.else_node; + + IrBasicBlockSrc *cond_block = ir_create_basic_block(irb, scope, "WhileCond"); + IrBasicBlockSrc *body_block = ir_create_basic_block(irb, scope, "WhileBody"); + IrBasicBlockSrc *continue_block = continue_expr_node ? + ir_create_basic_block(irb, scope, "WhileContinue") : cond_block; + IrBasicBlockSrc *end_block = ir_create_basic_block(irb, scope, "WhileEnd"); + IrBasicBlockSrc *else_block = else_node ? + ir_create_basic_block(irb, scope, "WhileElse") : end_block; + + IrInstSrc *is_comptime = ir_build_const_bool(irb, scope, node, + ir_should_inline(irb->exec, scope) || node->data.while_expr.is_inline); + ir_build_br(irb, scope, node, cond_block, is_comptime); + + Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime); + Buf *var_symbol = node->data.while_expr.var_symbol; + Buf *err_symbol = node->data.while_expr.err_symbol; + if (err_symbol != nullptr) { + ir_set_cursor_at_end_and_append_block(irb, cond_block); + + Scope *payload_scope; + AstNode *symbol_node = node; // TODO make more accurate + ZigVar *payload_var; + if (var_symbol) { + // TODO make it an error to write to payload variable + payload_var = ir_create_var(irb, symbol_node, subexpr_scope, var_symbol, + true, false, false, is_comptime); + payload_scope = payload_var->child_scope; + } else { + payload_scope = subexpr_scope; + } + ScopeExpr *spill_scope = create_expr_scope(irb->codegen, node, payload_scope); + IrInstSrc *err_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, subexpr_scope, + LValPtr, nullptr); + if (err_val_ptr == irb->codegen->invalid_inst_src) + return err_val_ptr; + IrInstSrc *is_err = ir_build_test_err_src(irb, scope, node->data.while_expr.condition, err_val_ptr, + true, false); + IrBasicBlockSrc *after_cond_block = irb->current_basic_block; + IrInstSrc *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node)); + IrInstSrc *cond_br_inst; + if (!instr_is_unreachable(is_err)) { + cond_br_inst = ir_build_cond_br(irb, scope, node->data.while_expr.condition, is_err, + else_block, body_block, is_comptime); + cond_br_inst->is_gen = true; + } else { + // for the purposes of the source instruction to ir_build_result_peers + cond_br_inst = irb->current_basic_block->instruction_list.last(); + } + + ResultLocPeerParent *peer_parent = ir_build_result_peers(irb, cond_br_inst, end_block, result_loc, + is_comptime); + + ir_set_cursor_at_end_and_append_block(irb, body_block); + if (var_symbol) { + IrInstSrc *payload_ptr = ir_build_unwrap_err_payload_src(irb, &spill_scope->base, symbol_node, + err_val_ptr, false, false); + IrInstSrc *var_value = node->data.while_expr.var_is_ptr ? + payload_ptr : ir_build_load_ptr(irb, &spill_scope->base, symbol_node, payload_ptr); + build_decl_var_and_init(irb, payload_scope, symbol_node, payload_var, var_value, buf_ptr(var_symbol), is_comptime); + } + + ZigList incoming_values = {0}; + ZigList incoming_blocks = {0}; + + if (is_duplicate_label(irb->codegen, payload_scope, node, node->data.while_expr.name)) + return irb->codegen->invalid_inst_src; + + ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, payload_scope); + loop_scope->break_block = end_block; + loop_scope->continue_block = continue_block; + loop_scope->is_comptime = is_comptime; + loop_scope->incoming_blocks = &incoming_blocks; + loop_scope->incoming_values = &incoming_values; + loop_scope->lval = lval; + loop_scope->peer_parent = peer_parent; + loop_scope->spill_scope = spill_scope; + + // Note the body block of the loop is not the place that lval and result_loc are used - + // it's actually in break statements, handled similarly to return statements. + // That is why we set those values in loop_scope above and not in this ir_gen_node call. + IrInstSrc *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base); + if (body_result == irb->codegen->invalid_inst_src) + return body_result; + + if (loop_scope->name != nullptr && loop_scope->name_used == false) { + add_node_error(irb->codegen, node, buf_sprintf("unused while label")); + } + + if (!instr_is_unreachable(body_result)) { + ir_mark_gen(ir_build_check_statement_is_void(irb, payload_scope, node->data.while_expr.body, body_result)); + ir_mark_gen(ir_build_br(irb, payload_scope, node, continue_block, is_comptime)); + } + + if (continue_expr_node) { + ir_set_cursor_at_end_and_append_block(irb, continue_block); + IrInstSrc *expr_result = ir_gen_node(irb, continue_expr_node, payload_scope); + if (expr_result == irb->codegen->invalid_inst_src) + return expr_result; + if (!instr_is_unreachable(expr_result)) { + ir_mark_gen(ir_build_check_statement_is_void(irb, payload_scope, continue_expr_node, expr_result)); + ir_mark_gen(ir_build_br(irb, payload_scope, node, cond_block, is_comptime)); + } + } + + ir_set_cursor_at_end_and_append_block(irb, else_block); + assert(else_node != nullptr); + + // TODO make it an error to write to error variable + AstNode *err_symbol_node = else_node; // TODO make more accurate + ZigVar *err_var = ir_create_var(irb, err_symbol_node, scope, err_symbol, + true, false, false, is_comptime); + Scope *err_scope = err_var->child_scope; + IrInstSrc *err_ptr = ir_build_unwrap_err_code_src(irb, err_scope, err_symbol_node, err_val_ptr); + IrInstSrc *err_value = ir_build_load_ptr(irb, err_scope, err_symbol_node, err_ptr); + build_decl_var_and_init(irb, err_scope, err_symbol_node, err_var, err_value, buf_ptr(err_symbol), is_comptime); + + if (peer_parent->peers.length != 0) { + peer_parent->peers.last()->next_bb = else_block; + } + ResultLocPeer *peer_result = create_peer_result(peer_parent); + peer_parent->peers.append(peer_result); + IrInstSrc *else_result = ir_gen_node_extra(irb, else_node, err_scope, lval, &peer_result->base); + if (else_result == irb->codegen->invalid_inst_src) + return else_result; + if (!instr_is_unreachable(else_result)) + ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime)); + IrBasicBlockSrc *after_else_block = irb->current_basic_block; + ir_set_cursor_at_end_and_append_block(irb, end_block); + if (else_result) { + incoming_blocks.append(after_else_block); + incoming_values.append(else_result); + } else { + incoming_blocks.append(after_cond_block); + incoming_values.append(void_else_result); + } + if (peer_parent->peers.length != 0) { + peer_parent->peers.last()->next_bb = end_block; + } + + IrInstSrc *phi = ir_build_phi(irb, scope, node, incoming_blocks.length, + incoming_blocks.items, incoming_values.items, peer_parent); + return ir_expr_wrap(irb, scope, phi, result_loc); + } else if (var_symbol != nullptr) { + ir_set_cursor_at_end_and_append_block(irb, cond_block); + Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime); + // TODO make it an error to write to payload variable + AstNode *symbol_node = node; // TODO make more accurate + + ZigVar *payload_var = ir_create_var(irb, symbol_node, subexpr_scope, var_symbol, + true, false, false, is_comptime); + Scope *child_scope = payload_var->child_scope; + ScopeExpr *spill_scope = create_expr_scope(irb->codegen, node, child_scope); + IrInstSrc *maybe_val_ptr = ir_gen_node_extra(irb, node->data.while_expr.condition, subexpr_scope, + LValPtr, nullptr); + if (maybe_val_ptr == irb->codegen->invalid_inst_src) + return maybe_val_ptr; + IrInstSrc *maybe_val = ir_build_load_ptr(irb, scope, node->data.while_expr.condition, maybe_val_ptr); + IrInstSrc *is_non_null = ir_build_test_non_null_src(irb, scope, node->data.while_expr.condition, maybe_val); + IrBasicBlockSrc *after_cond_block = irb->current_basic_block; + IrInstSrc *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node)); + IrInstSrc *cond_br_inst; + if (!instr_is_unreachable(is_non_null)) { + cond_br_inst = ir_build_cond_br(irb, scope, node->data.while_expr.condition, is_non_null, + body_block, else_block, is_comptime); + cond_br_inst->is_gen = true; + } else { + // for the purposes of the source instruction to ir_build_result_peers + cond_br_inst = irb->current_basic_block->instruction_list.last(); + } + + ResultLocPeerParent *peer_parent = ir_build_result_peers(irb, cond_br_inst, end_block, result_loc, + is_comptime); + + ir_set_cursor_at_end_and_append_block(irb, body_block); + IrInstSrc *payload_ptr = ir_build_optional_unwrap_ptr(irb, &spill_scope->base, symbol_node, maybe_val_ptr, false); + IrInstSrc *var_value = node->data.while_expr.var_is_ptr ? + payload_ptr : ir_build_load_ptr(irb, &spill_scope->base, symbol_node, payload_ptr); + build_decl_var_and_init(irb, child_scope, symbol_node, payload_var, var_value, buf_ptr(var_symbol), is_comptime); + + ZigList incoming_values = {0}; + ZigList incoming_blocks = {0}; + + if (is_duplicate_label(irb->codegen, child_scope, node, node->data.while_expr.name)) + return irb->codegen->invalid_inst_src; + + ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, child_scope); + loop_scope->break_block = end_block; + loop_scope->continue_block = continue_block; + loop_scope->is_comptime = is_comptime; + loop_scope->incoming_blocks = &incoming_blocks; + loop_scope->incoming_values = &incoming_values; + loop_scope->lval = lval; + loop_scope->peer_parent = peer_parent; + loop_scope->spill_scope = spill_scope; + + // Note the body block of the loop is not the place that lval and result_loc are used - + // it's actually in break statements, handled similarly to return statements. + // That is why we set those values in loop_scope above and not in this ir_gen_node call. + IrInstSrc *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base); + if (body_result == irb->codegen->invalid_inst_src) + return body_result; + + if (loop_scope->name != nullptr && loop_scope->name_used == false) { + add_node_error(irb->codegen, node, buf_sprintf("unused while label")); + } + + if (!instr_is_unreachable(body_result)) { + ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, node->data.while_expr.body, body_result)); + ir_mark_gen(ir_build_br(irb, child_scope, node, continue_block, is_comptime)); + } + + if (continue_expr_node) { + ir_set_cursor_at_end_and_append_block(irb, continue_block); + IrInstSrc *expr_result = ir_gen_node(irb, continue_expr_node, child_scope); + if (expr_result == irb->codegen->invalid_inst_src) + return expr_result; + if (!instr_is_unreachable(expr_result)) { + ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, continue_expr_node, expr_result)); + ir_mark_gen(ir_build_br(irb, child_scope, node, cond_block, is_comptime)); + } + } + + IrInstSrc *else_result = nullptr; + if (else_node) { + ir_set_cursor_at_end_and_append_block(irb, else_block); + + if (peer_parent->peers.length != 0) { + peer_parent->peers.last()->next_bb = else_block; + } + ResultLocPeer *peer_result = create_peer_result(peer_parent); + peer_parent->peers.append(peer_result); + else_result = ir_gen_node_extra(irb, else_node, scope, lval, &peer_result->base); + if (else_result == irb->codegen->invalid_inst_src) + return else_result; + if (!instr_is_unreachable(else_result)) + ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime)); + } + IrBasicBlockSrc *after_else_block = irb->current_basic_block; + ir_set_cursor_at_end_and_append_block(irb, end_block); + if (else_result) { + incoming_blocks.append(after_else_block); + incoming_values.append(else_result); + } else { + incoming_blocks.append(after_cond_block); + incoming_values.append(void_else_result); + } + if (peer_parent->peers.length != 0) { + peer_parent->peers.last()->next_bb = end_block; + } + + IrInstSrc *phi = ir_build_phi(irb, scope, node, incoming_blocks.length, + incoming_blocks.items, incoming_values.items, peer_parent); + return ir_expr_wrap(irb, scope, phi, result_loc); + } else { + ir_set_cursor_at_end_and_append_block(irb, cond_block); + IrInstSrc *cond_val = ir_gen_node(irb, node->data.while_expr.condition, scope); + if (cond_val == irb->codegen->invalid_inst_src) + return cond_val; + IrBasicBlockSrc *after_cond_block = irb->current_basic_block; + IrInstSrc *void_else_result = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, scope, node)); + IrInstSrc *cond_br_inst; + if (!instr_is_unreachable(cond_val)) { + cond_br_inst = ir_build_cond_br(irb, scope, node->data.while_expr.condition, cond_val, + body_block, else_block, is_comptime); + cond_br_inst->is_gen = true; + } else { + // for the purposes of the source instruction to ir_build_result_peers + cond_br_inst = irb->current_basic_block->instruction_list.last(); + } + + ResultLocPeerParent *peer_parent = ir_build_result_peers(irb, cond_br_inst, end_block, result_loc, + is_comptime); + ir_set_cursor_at_end_and_append_block(irb, body_block); + + ZigList incoming_values = {0}; + ZigList incoming_blocks = {0}; + + Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime); + + if (is_duplicate_label(irb->codegen, subexpr_scope, node, node->data.while_expr.name)) + return irb->codegen->invalid_inst_src; + + ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, subexpr_scope); + loop_scope->break_block = end_block; + loop_scope->continue_block = continue_block; + loop_scope->is_comptime = is_comptime; + loop_scope->incoming_blocks = &incoming_blocks; + loop_scope->incoming_values = &incoming_values; + loop_scope->lval = lval; + loop_scope->peer_parent = peer_parent; + + // Note the body block of the loop is not the place that lval and result_loc are used - + // it's actually in break statements, handled similarly to return statements. + // That is why we set those values in loop_scope above and not in this ir_gen_node call. + IrInstSrc *body_result = ir_gen_node(irb, node->data.while_expr.body, &loop_scope->base); + if (body_result == irb->codegen->invalid_inst_src) + return body_result; + + if (loop_scope->name != nullptr && loop_scope->name_used == false) { + add_node_error(irb->codegen, node, buf_sprintf("unused while label")); + } + + if (!instr_is_unreachable(body_result)) { + ir_mark_gen(ir_build_check_statement_is_void(irb, scope, node->data.while_expr.body, body_result)); + ir_mark_gen(ir_build_br(irb, scope, node, continue_block, is_comptime)); + } + + if (continue_expr_node) { + ir_set_cursor_at_end_and_append_block(irb, continue_block); + IrInstSrc *expr_result = ir_gen_node(irb, continue_expr_node, subexpr_scope); + if (expr_result == irb->codegen->invalid_inst_src) + return expr_result; + if (!instr_is_unreachable(expr_result)) { + ir_mark_gen(ir_build_check_statement_is_void(irb, scope, continue_expr_node, expr_result)); + ir_mark_gen(ir_build_br(irb, scope, node, cond_block, is_comptime)); + } + } + + IrInstSrc *else_result = nullptr; + if (else_node) { + ir_set_cursor_at_end_and_append_block(irb, else_block); + + if (peer_parent->peers.length != 0) { + peer_parent->peers.last()->next_bb = else_block; + } + ResultLocPeer *peer_result = create_peer_result(peer_parent); + peer_parent->peers.append(peer_result); + + else_result = ir_gen_node_extra(irb, else_node, subexpr_scope, lval, &peer_result->base); + if (else_result == irb->codegen->invalid_inst_src) + return else_result; + if (!instr_is_unreachable(else_result)) + ir_mark_gen(ir_build_br(irb, scope, node, end_block, is_comptime)); + } + IrBasicBlockSrc *after_else_block = irb->current_basic_block; + ir_set_cursor_at_end_and_append_block(irb, end_block); + if (else_result) { + incoming_blocks.append(after_else_block); + incoming_values.append(else_result); + } else { + incoming_blocks.append(after_cond_block); + incoming_values.append(void_else_result); + } + if (peer_parent->peers.length != 0) { + peer_parent->peers.last()->next_bb = end_block; + } + + IrInstSrc *phi = ir_build_phi(irb, scope, node, incoming_blocks.length, + incoming_blocks.items, incoming_values.items, peer_parent); + return ir_expr_wrap(irb, scope, phi, result_loc); + } +} + +static IrInstSrc *ir_gen_for_expr(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node, LVal lval, + ResultLoc *result_loc) +{ + assert(node->type == NodeTypeForExpr); + + AstNode *array_node = node->data.for_expr.array_expr; + AstNode *elem_node = node->data.for_expr.elem_node; + AstNode *index_node = node->data.for_expr.index_node; + AstNode *body_node = node->data.for_expr.body; + AstNode *else_node = node->data.for_expr.else_node; + + if (!elem_node) { + add_node_error(irb->codegen, node, buf_sprintf("for loop expression missing element parameter")); + return irb->codegen->invalid_inst_src; + } + assert(elem_node->type == NodeTypeSymbol); + + ScopeExpr *spill_scope = create_expr_scope(irb->codegen, node, parent_scope); + + IrInstSrc *array_val_ptr = ir_gen_node_extra(irb, array_node, &spill_scope->base, LValPtr, nullptr); + if (array_val_ptr == irb->codegen->invalid_inst_src) + return array_val_ptr; + + IrInstSrc *is_comptime = ir_build_const_bool(irb, parent_scope, node, + ir_should_inline(irb->exec, parent_scope) || node->data.for_expr.is_inline); + + AstNode *index_var_source_node; + ZigVar *index_var; + const char *index_var_name; + if (index_node) { + index_var_source_node = index_node; + Buf *index_var_name_buf = index_node->data.symbol_expr.symbol; + index_var = ir_create_var(irb, index_node, parent_scope, index_var_name_buf, true, false, false, is_comptime); + index_var_name = buf_ptr(index_var_name_buf); + } else { + index_var_source_node = node; + index_var = ir_create_var(irb, node, parent_scope, nullptr, true, false, true, is_comptime); + index_var_name = "i"; + } + + IrInstSrc *zero = ir_build_const_usize(irb, parent_scope, node, 0); + build_decl_var_and_init(irb, parent_scope, index_var_source_node, index_var, zero, index_var_name, is_comptime); + parent_scope = index_var->child_scope; + + IrInstSrc *one = ir_build_const_usize(irb, parent_scope, node, 1); + IrInstSrc *index_ptr = ir_build_var_ptr(irb, parent_scope, node, index_var); + + + IrBasicBlockSrc *cond_block = ir_create_basic_block(irb, parent_scope, "ForCond"); + IrBasicBlockSrc *body_block = ir_create_basic_block(irb, parent_scope, "ForBody"); + IrBasicBlockSrc *end_block = ir_create_basic_block(irb, parent_scope, "ForEnd"); + IrBasicBlockSrc *else_block = else_node ? ir_create_basic_block(irb, parent_scope, "ForElse") : end_block; + IrBasicBlockSrc *continue_block = ir_create_basic_block(irb, parent_scope, "ForContinue"); + + Buf *len_field_name = buf_create_from_str("len"); + IrInstSrc *len_ref = ir_build_field_ptr(irb, parent_scope, node, array_val_ptr, len_field_name, false); + IrInstSrc *len_val = ir_build_load_ptr(irb, &spill_scope->base, node, len_ref); + ir_build_br(irb, parent_scope, node, cond_block, is_comptime); + + ir_set_cursor_at_end_and_append_block(irb, cond_block); + IrInstSrc *index_val = ir_build_load_ptr(irb, &spill_scope->base, node, index_ptr); + IrInstSrc *cond = ir_build_bin_op(irb, parent_scope, node, IrBinOpCmpLessThan, index_val, len_val, false); + IrBasicBlockSrc *after_cond_block = irb->current_basic_block; + IrInstSrc *void_else_value = else_node ? nullptr : ir_mark_gen(ir_build_const_void(irb, parent_scope, node)); + IrInstSrc *cond_br_inst = ir_mark_gen(ir_build_cond_br(irb, parent_scope, node, cond, + body_block, else_block, is_comptime)); + + ResultLocPeerParent *peer_parent = ir_build_result_peers(irb, cond_br_inst, end_block, result_loc, is_comptime); + + ir_set_cursor_at_end_and_append_block(irb, body_block); + IrInstSrc *elem_ptr = ir_build_elem_ptr(irb, &spill_scope->base, node, array_val_ptr, index_val, + false, PtrLenSingle, nullptr); + // TODO make it an error to write to element variable or i variable. + Buf *elem_var_name = elem_node->data.symbol_expr.symbol; + ZigVar *elem_var = ir_create_var(irb, elem_node, parent_scope, elem_var_name, true, false, false, is_comptime); + Scope *child_scope = elem_var->child_scope; + + IrInstSrc *elem_value = node->data.for_expr.elem_is_ptr ? + elem_ptr : ir_build_load_ptr(irb, &spill_scope->base, elem_node, elem_ptr); + build_decl_var_and_init(irb, parent_scope, elem_node, elem_var, elem_value, buf_ptr(elem_var_name), is_comptime); + + if (is_duplicate_label(irb->codegen, child_scope, node, node->data.for_expr.name)) + return irb->codegen->invalid_inst_src; + + ZigList incoming_values = {0}; + ZigList incoming_blocks = {0}; + ScopeLoop *loop_scope = create_loop_scope(irb->codegen, node, child_scope); + loop_scope->break_block = end_block; + loop_scope->continue_block = continue_block; + loop_scope->is_comptime = is_comptime; + loop_scope->incoming_blocks = &incoming_blocks; + loop_scope->incoming_values = &incoming_values; + loop_scope->lval = LValNone; + loop_scope->peer_parent = peer_parent; + loop_scope->spill_scope = spill_scope; + + // Note the body block of the loop is not the place that lval and result_loc are used - + // it's actually in break statements, handled similarly to return statements. + // That is why we set those values in loop_scope above and not in this ir_gen_node call. + IrInstSrc *body_result = ir_gen_node(irb, body_node, &loop_scope->base); + if (body_result == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + + if (loop_scope->name != nullptr && loop_scope->name_used == false) { + add_node_error(irb->codegen, node, buf_sprintf("unused for label")); + } + + if (!instr_is_unreachable(body_result)) { + ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, node->data.for_expr.body, body_result)); + ir_mark_gen(ir_build_br(irb, child_scope, node, continue_block, is_comptime)); + } + + ir_set_cursor_at_end_and_append_block(irb, continue_block); + IrInstSrc *new_index_val = ir_build_bin_op(irb, child_scope, node, IrBinOpAdd, index_val, one, false); + ir_build_store_ptr(irb, child_scope, node, index_ptr, new_index_val)->allow_write_through_const = true; + ir_build_br(irb, child_scope, node, cond_block, is_comptime); + + IrInstSrc *else_result = nullptr; + if (else_node) { + ir_set_cursor_at_end_and_append_block(irb, else_block); + + if (peer_parent->peers.length != 0) { + peer_parent->peers.last()->next_bb = else_block; + } + ResultLocPeer *peer_result = create_peer_result(peer_parent); + peer_parent->peers.append(peer_result); + else_result = ir_gen_node_extra(irb, else_node, parent_scope, LValNone, &peer_result->base); + if (else_result == irb->codegen->invalid_inst_src) + return else_result; + if (!instr_is_unreachable(else_result)) + ir_mark_gen(ir_build_br(irb, parent_scope, node, end_block, is_comptime)); + } + IrBasicBlockSrc *after_else_block = irb->current_basic_block; + ir_set_cursor_at_end_and_append_block(irb, end_block); + + if (else_result) { + incoming_blocks.append(after_else_block); + incoming_values.append(else_result); + } else { + incoming_blocks.append(after_cond_block); + incoming_values.append(void_else_value); + } + if (peer_parent->peers.length != 0) { + peer_parent->peers.last()->next_bb = end_block; + } + + IrInstSrc *phi = ir_build_phi(irb, parent_scope, node, incoming_blocks.length, + incoming_blocks.items, incoming_values.items, peer_parent); + return ir_lval_wrap(irb, parent_scope, phi, lval, result_loc); +} + +static IrInstSrc *ir_gen_bool_literal(IrBuilderSrc *irb, Scope *scope, AstNode *node) { + assert(node->type == NodeTypeBoolLiteral); + return ir_build_const_bool(irb, scope, node, node->data.bool_literal.value); +} + +static IrInstSrc *ir_gen_enum_literal(IrBuilderSrc *irb, Scope *scope, AstNode *node) { + assert(node->type == NodeTypeEnumLiteral); + Buf *name = &node->data.enum_literal.identifier->data.str_lit.str; + return ir_build_const_enum_literal(irb, scope, node, name); +} + +static IrInstSrc *ir_gen_string_literal(IrBuilderSrc *irb, Scope *scope, AstNode *node) { + assert(node->type == NodeTypeStringLiteral); + + return ir_build_const_str_lit(irb, scope, node, node->data.string_literal.buf); +} + +static IrInstSrc *ir_gen_array_type(IrBuilderSrc *irb, Scope *scope, AstNode *node) { + assert(node->type == NodeTypeArrayType); + + AstNode *size_node = node->data.array_type.size; + AstNode *child_type_node = node->data.array_type.child_type; + bool is_const = node->data.array_type.is_const; + bool is_volatile = node->data.array_type.is_volatile; + bool is_allow_zero = node->data.array_type.allow_zero_token != nullptr; + AstNode *sentinel_expr = node->data.array_type.sentinel; + AstNode *align_expr = node->data.array_type.align_expr; + + Scope *comptime_scope = create_comptime_scope(irb->codegen, node, scope); + + IrInstSrc *sentinel; + if (sentinel_expr != nullptr) { + sentinel = ir_gen_node(irb, sentinel_expr, comptime_scope); + if (sentinel == irb->codegen->invalid_inst_src) + return sentinel; + } else { + sentinel = nullptr; + } + + if (size_node) { + if (is_const) { + add_node_error(irb->codegen, node, buf_create_from_str("const qualifier invalid on array type")); + return irb->codegen->invalid_inst_src; + } + if (is_volatile) { + add_node_error(irb->codegen, node, buf_create_from_str("volatile qualifier invalid on array type")); + return irb->codegen->invalid_inst_src; + } + if (is_allow_zero) { + add_node_error(irb->codegen, node, buf_create_from_str("allowzero qualifier invalid on array type")); + return irb->codegen->invalid_inst_src; + } + if (align_expr != nullptr) { + add_node_error(irb->codegen, node, buf_create_from_str("align qualifier invalid on array type")); + return irb->codegen->invalid_inst_src; + } + + IrInstSrc *size_value = ir_gen_node(irb, size_node, comptime_scope); + if (size_value == irb->codegen->invalid_inst_src) + return size_value; + + IrInstSrc *child_type = ir_gen_node(irb, child_type_node, comptime_scope); + if (child_type == irb->codegen->invalid_inst_src) + return child_type; + + return ir_build_array_type(irb, scope, node, size_value, sentinel, child_type); + } else { + IrInstSrc *align_value; + if (align_expr != nullptr) { + align_value = ir_gen_node(irb, align_expr, comptime_scope); + if (align_value == irb->codegen->invalid_inst_src) + return align_value; + } else { + align_value = nullptr; + } + + IrInstSrc *child_type = ir_gen_node(irb, child_type_node, comptime_scope); + if (child_type == irb->codegen->invalid_inst_src) + return child_type; + + return ir_build_slice_type(irb, scope, node, child_type, is_const, is_volatile, sentinel, + align_value, is_allow_zero); + } +} + +static IrInstSrc *ir_gen_anyframe_type(IrBuilderSrc *irb, Scope *scope, AstNode *node) { + assert(node->type == NodeTypeAnyFrameType); + + AstNode *payload_type_node = node->data.anyframe_type.payload_type; + IrInstSrc *payload_type_value = nullptr; + + if (payload_type_node != nullptr) { + payload_type_value = ir_gen_node(irb, payload_type_node, scope); + if (payload_type_value == irb->codegen->invalid_inst_src) + return payload_type_value; + + } + + return ir_build_anyframe_type(irb, scope, node, payload_type_value); +} + +static IrInstSrc *ir_gen_undefined_literal(IrBuilderSrc *irb, Scope *scope, AstNode *node) { + assert(node->type == NodeTypeUndefinedLiteral); + return ir_build_const_undefined(irb, scope, node); +} + +static Error parse_asm_template(IrAnalyze *ira, AstNode *source_node, Buf *asm_template, + ZigList *tok_list) +{ + // TODO Connect the errors in this function back up to the actual source location + // rather than just the token. https://github.com/ziglang/zig/issues/2080 + enum State { + StateStart, + StatePercent, + StateTemplate, + StateVar, + }; + + assert(tok_list->length == 0); + + AsmToken *cur_tok = nullptr; + + enum State state = StateStart; + + for (size_t i = 0; i < buf_len(asm_template); i += 1) { + uint8_t c = *((uint8_t*)buf_ptr(asm_template) + i); + switch (state) { + case StateStart: + if (c == '%') { + tok_list->add_one(); + cur_tok = &tok_list->last(); + cur_tok->id = AsmTokenIdPercent; + cur_tok->start = i; + state = StatePercent; + } else { + tok_list->add_one(); + cur_tok = &tok_list->last(); + cur_tok->id = AsmTokenIdTemplate; + cur_tok->start = i; + state = StateTemplate; + } + break; + case StatePercent: + if (c == '%') { + cur_tok->end = i; + state = StateStart; + } else if (c == '[') { + cur_tok->id = AsmTokenIdVar; + state = StateVar; + } else if (c == '=') { + cur_tok->id = AsmTokenIdUniqueId; + cur_tok->end = i; + state = StateStart; + } else { + add_node_error(ira->codegen, source_node, + buf_create_from_str("expected a '%' or '['")); + return ErrorSemanticAnalyzeFail; + } + break; + case StateTemplate: + if (c == '%') { + cur_tok->end = i; + i -= 1; + cur_tok = nullptr; + state = StateStart; + } + break; + case StateVar: + if (c == ']') { + cur_tok->end = i; + state = StateStart; + } else if ((c >= 'a' && c <= 'z') || + (c >= '0' && c <= '9') || + (c == '_')) + { + // do nothing + } else { + add_node_error(ira->codegen, source_node, + buf_sprintf("invalid substitution character: '%c'", c)); + return ErrorSemanticAnalyzeFail; + } + break; + } + } + + switch (state) { + case StateStart: + break; + case StatePercent: + case StateVar: + add_node_error(ira->codegen, source_node, buf_sprintf("unexpected end of assembly template")); + return ErrorSemanticAnalyzeFail; + case StateTemplate: + cur_tok->end = buf_len(asm_template); + break; + } + return ErrorNone; +} + +static size_t find_asm_index(CodeGen *g, AstNode *node, AsmToken *tok, Buf *src_template) { + const char *ptr = buf_ptr(src_template) + tok->start + 2; + size_t len = tok->end - tok->start - 2; + size_t result = 0; + for (size_t i = 0; i < node->data.asm_expr.output_list.length; i += 1, result += 1) { + AsmOutput *asm_output = node->data.asm_expr.output_list.at(i); + if (buf_eql_mem(asm_output->asm_symbolic_name, ptr, len)) { + return result; + } + } + for (size_t i = 0; i < node->data.asm_expr.input_list.length; i += 1, result += 1) { + AsmInput *asm_input = node->data.asm_expr.input_list.at(i); + if (buf_eql_mem(asm_input->asm_symbolic_name, ptr, len)) { + return result; + } + } + return SIZE_MAX; +} + +static IrInstSrc *ir_gen_asm_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node) { + assert(node->type == NodeTypeAsmExpr); + AstNodeAsmExpr *asm_expr = &node->data.asm_expr; + + IrInstSrc *asm_template = ir_gen_node(irb, asm_expr->asm_template, scope); + if (asm_template == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + + bool is_volatile = asm_expr->volatile_token != nullptr; + bool in_fn_scope = (scope_fn_entry(scope) != nullptr); + + if (!in_fn_scope) { + if (is_volatile) { + add_token_error(irb->codegen, node->owner, asm_expr->volatile_token, + buf_sprintf("volatile is meaningless on global assembly")); + return irb->codegen->invalid_inst_src; + } + + if (asm_expr->output_list.length != 0 || asm_expr->input_list.length != 0 || + asm_expr->clobber_list.length != 0) + { + add_node_error(irb->codegen, node, + buf_sprintf("global assembly cannot have inputs, outputs, or clobbers")); + return irb->codegen->invalid_inst_src; + } + + return ir_build_asm_src(irb, scope, node, asm_template, nullptr, nullptr, + nullptr, 0, is_volatile, true); + } + + IrInstSrc **input_list = heap::c_allocator.allocate(asm_expr->input_list.length); + IrInstSrc **output_types = heap::c_allocator.allocate(asm_expr->output_list.length); + ZigVar **output_vars = heap::c_allocator.allocate(asm_expr->output_list.length); + size_t return_count = 0; + if (!is_volatile && asm_expr->output_list.length == 0) { + add_node_error(irb->codegen, node, + buf_sprintf("assembly expression with no output must be marked volatile")); + return irb->codegen->invalid_inst_src; + } + for (size_t i = 0; i < asm_expr->output_list.length; i += 1) { + AsmOutput *asm_output = asm_expr->output_list.at(i); + if (asm_output->return_type) { + return_count += 1; + + IrInstSrc *return_type = ir_gen_node(irb, asm_output->return_type, scope); + if (return_type == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + if (return_count > 1) { + add_node_error(irb->codegen, node, + buf_sprintf("inline assembly allows up to one output value")); + return irb->codegen->invalid_inst_src; + } + output_types[i] = return_type; + } else { + Buf *variable_name = asm_output->variable_name; + // TODO there is some duplication here with ir_gen_symbol. I need to do a full audit of how + // inline assembly works. https://github.com/ziglang/zig/issues/215 + ZigVar *var = find_variable(irb->codegen, scope, variable_name, nullptr); + if (var) { + output_vars[i] = var; + } else { + add_node_error(irb->codegen, node, + buf_sprintf("use of undeclared identifier '%s'", buf_ptr(variable_name))); + return irb->codegen->invalid_inst_src; + } + } + + const char modifier = *buf_ptr(asm_output->constraint); + if (modifier != '=') { + add_node_error(irb->codegen, node, + buf_sprintf("invalid modifier starting output constraint for '%s': '%c', only '=' is supported." + " Compiler TODO: see https://github.com/ziglang/zig/issues/215", + buf_ptr(asm_output->asm_symbolic_name), modifier)); + return irb->codegen->invalid_inst_src; + } + } + for (size_t i = 0; i < asm_expr->input_list.length; i += 1) { + AsmInput *asm_input = asm_expr->input_list.at(i); + IrInstSrc *input_value = ir_gen_node(irb, asm_input->expr, scope); + if (input_value == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + + input_list[i] = input_value; + } + + return ir_build_asm_src(irb, scope, node, asm_template, input_list, output_types, + output_vars, return_count, is_volatile, false); +} + +static IrInstSrc *ir_gen_if_optional_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, + ResultLoc *result_loc) +{ + assert(node->type == NodeTypeIfOptional); + + Buf *var_symbol = node->data.test_expr.var_symbol; + AstNode *expr_node = node->data.test_expr.target_node; + AstNode *then_node = node->data.test_expr.then_node; + AstNode *else_node = node->data.test_expr.else_node; + bool var_is_ptr = node->data.test_expr.var_is_ptr; + + ScopeExpr *spill_scope = create_expr_scope(irb->codegen, expr_node, scope); + spill_scope->spill_harder = true; + + IrInstSrc *maybe_val_ptr = ir_gen_node_extra(irb, expr_node, &spill_scope->base, LValPtr, nullptr); + if (maybe_val_ptr == irb->codegen->invalid_inst_src) + return maybe_val_ptr; + + IrInstSrc *maybe_val = ir_build_load_ptr(irb, scope, node, maybe_val_ptr); + IrInstSrc *is_non_null = ir_build_test_non_null_src(irb, scope, node, maybe_val); + + IrBasicBlockSrc *then_block = ir_create_basic_block(irb, scope, "OptionalThen"); + IrBasicBlockSrc *else_block = ir_create_basic_block(irb, scope, "OptionalElse"); + IrBasicBlockSrc *endif_block = ir_create_basic_block(irb, scope, "OptionalEndIf"); + + IrInstSrc *is_comptime; + if (ir_should_inline(irb->exec, scope)) { + is_comptime = ir_build_const_bool(irb, scope, node, true); + } else { + is_comptime = ir_build_test_comptime(irb, scope, node, is_non_null); + } + IrInstSrc *cond_br_inst = ir_build_cond_br(irb, scope, node, is_non_null, + then_block, else_block, is_comptime); + + ResultLocPeerParent *peer_parent = ir_build_binary_result_peers(irb, cond_br_inst, else_block, endif_block, + result_loc, is_comptime); + + ir_set_cursor_at_end_and_append_block(irb, then_block); + + Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, &spill_scope->base, is_comptime); + Scope *var_scope; + if (var_symbol) { + bool is_shadowable = false; + bool is_const = true; + ZigVar *var = ir_create_var(irb, node, subexpr_scope, + var_symbol, is_const, is_const, is_shadowable, is_comptime); + + IrInstSrc *payload_ptr = ir_build_optional_unwrap_ptr(irb, subexpr_scope, node, maybe_val_ptr, false); + IrInstSrc *var_value = var_is_ptr ? + payload_ptr : ir_build_load_ptr(irb, &spill_scope->base, node, payload_ptr); + build_decl_var_and_init(irb, subexpr_scope, node, var, var_value, buf_ptr(var_symbol), is_comptime); + var_scope = var->child_scope; + } else { + var_scope = subexpr_scope; + } + IrInstSrc *then_expr_result = ir_gen_node_extra(irb, then_node, var_scope, lval, + &peer_parent->peers.at(0)->base); + if (then_expr_result == irb->codegen->invalid_inst_src) + return then_expr_result; + IrBasicBlockSrc *after_then_block = irb->current_basic_block; + if (!instr_is_unreachable(then_expr_result)) + ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); + + ir_set_cursor_at_end_and_append_block(irb, else_block); + IrInstSrc *else_expr_result; + if (else_node) { + else_expr_result = ir_gen_node_extra(irb, else_node, subexpr_scope, lval, &peer_parent->peers.at(1)->base); + if (else_expr_result == irb->codegen->invalid_inst_src) + return else_expr_result; + } else { + else_expr_result = ir_build_const_void(irb, scope, node); + ir_build_end_expr(irb, scope, node, else_expr_result, &peer_parent->peers.at(1)->base); + } + IrBasicBlockSrc *after_else_block = irb->current_basic_block; + if (!instr_is_unreachable(else_expr_result)) + ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); + + ir_set_cursor_at_end_and_append_block(irb, endif_block); + IrInstSrc **incoming_values = heap::c_allocator.allocate(2); + incoming_values[0] = then_expr_result; + incoming_values[1] = else_expr_result; + IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate(2); + incoming_blocks[0] = after_then_block; + incoming_blocks[1] = after_else_block; + + IrInstSrc *phi = ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values, peer_parent); + return ir_expr_wrap(irb, scope, phi, result_loc); +} + +static IrInstSrc *ir_gen_if_err_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, + ResultLoc *result_loc) +{ + assert(node->type == NodeTypeIfErrorExpr); + + AstNode *target_node = node->data.if_err_expr.target_node; + AstNode *then_node = node->data.if_err_expr.then_node; + AstNode *else_node = node->data.if_err_expr.else_node; + bool var_is_ptr = node->data.if_err_expr.var_is_ptr; + bool var_is_const = true; + Buf *var_symbol = node->data.if_err_expr.var_symbol; + Buf *err_symbol = node->data.if_err_expr.err_symbol; + + IrInstSrc *err_val_ptr = ir_gen_node_extra(irb, target_node, scope, LValPtr, nullptr); + if (err_val_ptr == irb->codegen->invalid_inst_src) + return err_val_ptr; + + IrInstSrc *err_val = ir_build_load_ptr(irb, scope, node, err_val_ptr); + IrInstSrc *is_err = ir_build_test_err_src(irb, scope, node, err_val_ptr, true, false); + + IrBasicBlockSrc *ok_block = ir_create_basic_block(irb, scope, "TryOk"); + IrBasicBlockSrc *else_block = ir_create_basic_block(irb, scope, "TryElse"); + IrBasicBlockSrc *endif_block = ir_create_basic_block(irb, scope, "TryEnd"); + + bool force_comptime = ir_should_inline(irb->exec, scope); + IrInstSrc *is_comptime = force_comptime ? ir_build_const_bool(irb, scope, node, true) : ir_build_test_comptime(irb, scope, node, is_err); + IrInstSrc *cond_br_inst = ir_build_cond_br(irb, scope, node, is_err, else_block, ok_block, is_comptime); + + ResultLocPeerParent *peer_parent = ir_build_binary_result_peers(irb, cond_br_inst, else_block, endif_block, + result_loc, is_comptime); + + ir_set_cursor_at_end_and_append_block(irb, ok_block); + + Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime); + Scope *var_scope; + if (var_symbol) { + bool is_shadowable = false; + IrInstSrc *var_is_comptime = force_comptime ? ir_build_const_bool(irb, subexpr_scope, node, true) : ir_build_test_comptime(irb, subexpr_scope, node, err_val); + ZigVar *var = ir_create_var(irb, node, subexpr_scope, + var_symbol, var_is_const, var_is_const, is_shadowable, var_is_comptime); + + IrInstSrc *payload_ptr = ir_build_unwrap_err_payload_src(irb, subexpr_scope, node, err_val_ptr, false, false); + IrInstSrc *var_value = var_is_ptr ? + payload_ptr : ir_build_load_ptr(irb, subexpr_scope, node, payload_ptr); + build_decl_var_and_init(irb, subexpr_scope, node, var, var_value, buf_ptr(var_symbol), var_is_comptime); + var_scope = var->child_scope; + } else { + var_scope = subexpr_scope; + } + IrInstSrc *then_expr_result = ir_gen_node_extra(irb, then_node, var_scope, lval, + &peer_parent->peers.at(0)->base); + if (then_expr_result == irb->codegen->invalid_inst_src) + return then_expr_result; + IrBasicBlockSrc *after_then_block = irb->current_basic_block; + if (!instr_is_unreachable(then_expr_result)) + ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); + + ir_set_cursor_at_end_and_append_block(irb, else_block); + + IrInstSrc *else_expr_result; + if (else_node) { + Scope *err_var_scope; + if (err_symbol) { + bool is_shadowable = false; + bool is_const = true; + ZigVar *var = ir_create_var(irb, node, subexpr_scope, + err_symbol, is_const, is_const, is_shadowable, is_comptime); + + IrInstSrc *err_ptr = ir_build_unwrap_err_code_src(irb, subexpr_scope, node, err_val_ptr); + IrInstSrc *err_value = ir_build_load_ptr(irb, subexpr_scope, node, err_ptr); + build_decl_var_and_init(irb, subexpr_scope, node, var, err_value, buf_ptr(err_symbol), is_comptime); + err_var_scope = var->child_scope; + } else { + err_var_scope = subexpr_scope; + } + else_expr_result = ir_gen_node_extra(irb, else_node, err_var_scope, lval, &peer_parent->peers.at(1)->base); + if (else_expr_result == irb->codegen->invalid_inst_src) + return else_expr_result; + } else { + else_expr_result = ir_build_const_void(irb, scope, node); + ir_build_end_expr(irb, scope, node, else_expr_result, &peer_parent->peers.at(1)->base); + } + IrBasicBlockSrc *after_else_block = irb->current_basic_block; + if (!instr_is_unreachable(else_expr_result)) + ir_mark_gen(ir_build_br(irb, scope, node, endif_block, is_comptime)); + + ir_set_cursor_at_end_and_append_block(irb, endif_block); + IrInstSrc **incoming_values = heap::c_allocator.allocate(2); + incoming_values[0] = then_expr_result; + incoming_values[1] = else_expr_result; + IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate(2); + incoming_blocks[0] = after_then_block; + incoming_blocks[1] = after_else_block; + + IrInstSrc *phi = ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values, peer_parent); + return ir_expr_wrap(irb, scope, phi, result_loc); +} + +static bool ir_gen_switch_prong_expr(IrBuilderSrc *irb, Scope *scope, AstNode *switch_node, AstNode *prong_node, + IrBasicBlockSrc *end_block, IrInstSrc *is_comptime, IrInstSrc *var_is_comptime, + IrInstSrc *target_value_ptr, IrInstSrc **prong_values, size_t prong_values_len, + ZigList *incoming_blocks, ZigList *incoming_values, + IrInstSrcSwitchElseVar **out_switch_else_var, LVal lval, ResultLoc *result_loc) +{ + assert(switch_node->type == NodeTypeSwitchExpr); + assert(prong_node->type == NodeTypeSwitchProng); + + AstNode *expr_node = prong_node->data.switch_prong.expr; + AstNode *var_symbol_node = prong_node->data.switch_prong.var_symbol; + Scope *child_scope; + if (var_symbol_node) { + assert(var_symbol_node->type == NodeTypeSymbol); + Buf *var_name = var_symbol_node->data.symbol_expr.symbol; + bool var_is_ptr = prong_node->data.switch_prong.var_is_ptr; + + bool is_shadowable = false; + bool is_const = true; + ZigVar *var = ir_create_var(irb, var_symbol_node, scope, + var_name, is_const, is_const, is_shadowable, var_is_comptime); + child_scope = var->child_scope; + IrInstSrc *var_value; + if (out_switch_else_var != nullptr) { + IrInstSrcSwitchElseVar *switch_else_var = ir_build_switch_else_var(irb, scope, var_symbol_node, + target_value_ptr); + *out_switch_else_var = switch_else_var; + IrInstSrc *payload_ptr = &switch_else_var->base; + var_value = var_is_ptr ? + payload_ptr : ir_build_load_ptr(irb, scope, var_symbol_node, payload_ptr); + } else if (prong_values != nullptr) { + IrInstSrc *payload_ptr = ir_build_switch_var(irb, scope, var_symbol_node, target_value_ptr, + prong_values, prong_values_len); + var_value = var_is_ptr ? + payload_ptr : ir_build_load_ptr(irb, scope, var_symbol_node, payload_ptr); + } else { + var_value = var_is_ptr ? + target_value_ptr : ir_build_load_ptr(irb, scope, var_symbol_node, target_value_ptr); + } + build_decl_var_and_init(irb, scope, var_symbol_node, var, var_value, buf_ptr(var_name), var_is_comptime); + } else { + child_scope = scope; + } + + IrInstSrc *expr_result = ir_gen_node_extra(irb, expr_node, child_scope, lval, result_loc); + if (expr_result == irb->codegen->invalid_inst_src) + return false; + if (!instr_is_unreachable(expr_result)) + ir_mark_gen(ir_build_br(irb, scope, switch_node, end_block, is_comptime)); + incoming_blocks->append(irb->current_basic_block); + incoming_values->append(expr_result); + return true; +} + +static IrInstSrc *ir_gen_switch_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, + ResultLoc *result_loc) +{ + assert(node->type == NodeTypeSwitchExpr); + + AstNode *target_node = node->data.switch_expr.expr; + IrInstSrc *target_value_ptr = ir_gen_node_extra(irb, target_node, scope, LValPtr, nullptr); + if (target_value_ptr == irb->codegen->invalid_inst_src) + return target_value_ptr; + IrInstSrc *target_value = ir_build_switch_target(irb, scope, node, target_value_ptr); + + IrBasicBlockSrc *else_block = ir_create_basic_block(irb, scope, "SwitchElse"); + IrBasicBlockSrc *end_block = ir_create_basic_block(irb, scope, "SwitchEnd"); + + size_t prong_count = node->data.switch_expr.prongs.length; + ZigList cases = {0}; + + IrInstSrc *is_comptime; + IrInstSrc *var_is_comptime; + if (ir_should_inline(irb->exec, scope)) { + is_comptime = ir_build_const_bool(irb, scope, node, true); + var_is_comptime = is_comptime; + } else { + is_comptime = ir_build_test_comptime(irb, scope, node, target_value); + var_is_comptime = ir_build_test_comptime(irb, scope, node, target_value_ptr); + } + + ZigList incoming_values = {0}; + ZigList incoming_blocks = {0}; + ZigList check_ranges = {0}; + + IrInstSrcSwitchElseVar *switch_else_var = nullptr; + + ResultLocPeerParent *peer_parent = heap::c_allocator.create(); + peer_parent->base.id = ResultLocIdPeerParent; + peer_parent->base.allow_write_through_const = result_loc->allow_write_through_const; + peer_parent->end_bb = end_block; + peer_parent->is_comptime = is_comptime; + peer_parent->parent = result_loc; + + ir_build_reset_result(irb, scope, node, &peer_parent->base); + + // First do the else and the ranges + Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, scope, is_comptime); + Scope *comptime_scope = create_comptime_scope(irb->codegen, node, scope); + AstNode *else_prong = nullptr; + AstNode *underscore_prong = nullptr; + for (size_t prong_i = 0; prong_i < prong_count; prong_i += 1) { + AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i); + size_t prong_item_count = prong_node->data.switch_prong.items.length; + if (prong_node->data.switch_prong.any_items_are_range) { + ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent); + + IrInstSrc *ok_bit = nullptr; + AstNode *last_item_node = nullptr; + for (size_t item_i = 0; item_i < prong_item_count; item_i += 1) { + AstNode *item_node = prong_node->data.switch_prong.items.at(item_i); + last_item_node = item_node; + if (item_node->type == NodeTypeSwitchRange) { + AstNode *start_node = item_node->data.switch_range.start; + AstNode *end_node = item_node->data.switch_range.end; + + IrInstSrc *start_value = ir_gen_node(irb, start_node, comptime_scope); + if (start_value == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + + IrInstSrc *end_value = ir_gen_node(irb, end_node, comptime_scope); + if (end_value == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + + IrInstSrcCheckSwitchProngsRange *check_range = check_ranges.add_one(); + check_range->start = start_value; + check_range->end = end_value; + + IrInstSrc *lower_range_ok = ir_build_bin_op(irb, scope, item_node, IrBinOpCmpGreaterOrEq, + target_value, start_value, false); + IrInstSrc *upper_range_ok = ir_build_bin_op(irb, scope, item_node, IrBinOpCmpLessOrEq, + target_value, end_value, false); + IrInstSrc *both_ok = ir_build_bin_op(irb, scope, item_node, IrBinOpBoolAnd, + lower_range_ok, upper_range_ok, false); + if (ok_bit) { + ok_bit = ir_build_bin_op(irb, scope, item_node, IrBinOpBoolOr, both_ok, ok_bit, false); + } else { + ok_bit = both_ok; + } + } else { + IrInstSrc *item_value = ir_gen_node(irb, item_node, comptime_scope); + if (item_value == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + + IrInstSrcCheckSwitchProngsRange *check_range = check_ranges.add_one(); + check_range->start = item_value; + check_range->end = item_value; + + IrInstSrc *cmp_ok = ir_build_bin_op(irb, scope, item_node, IrBinOpCmpEq, + item_value, target_value, false); + if (ok_bit) { + ok_bit = ir_build_bin_op(irb, scope, item_node, IrBinOpBoolOr, cmp_ok, ok_bit, false); + } else { + ok_bit = cmp_ok; + } + } + } + + IrBasicBlockSrc *range_block_yes = ir_create_basic_block(irb, scope, "SwitchRangeYes"); + IrBasicBlockSrc *range_block_no = ir_create_basic_block(irb, scope, "SwitchRangeNo"); + + assert(ok_bit); + assert(last_item_node); + IrInstSrc *br_inst = ir_mark_gen(ir_build_cond_br(irb, scope, last_item_node, ok_bit, + range_block_yes, range_block_no, is_comptime)); + if (peer_parent->base.source_instruction == nullptr) { + peer_parent->base.source_instruction = br_inst; + } + + if (peer_parent->peers.length > 0) { + peer_parent->peers.last()->next_bb = range_block_yes; + } + peer_parent->peers.append(this_peer_result_loc); + ir_set_cursor_at_end_and_append_block(irb, range_block_yes); + if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block, + is_comptime, var_is_comptime, target_value_ptr, nullptr, 0, + &incoming_blocks, &incoming_values, nullptr, LValNone, &this_peer_result_loc->base)) + { + return irb->codegen->invalid_inst_src; + } + + ir_set_cursor_at_end_and_append_block(irb, range_block_no); + } else { + if (prong_item_count == 0) { + if (else_prong) { + ErrorMsg *msg = add_node_error(irb->codegen, prong_node, + buf_sprintf("multiple else prongs in switch expression")); + add_error_note(irb->codegen, msg, else_prong, + buf_sprintf("previous else prong is here")); + return irb->codegen->invalid_inst_src; + } + else_prong = prong_node; + } else if (prong_item_count == 1 && + prong_node->data.switch_prong.items.at(0)->type == NodeTypeSymbol && + buf_eql_str(prong_node->data.switch_prong.items.at(0)->data.symbol_expr.symbol, "_")) { + if (underscore_prong) { + ErrorMsg *msg = add_node_error(irb->codegen, prong_node, + buf_sprintf("multiple '_' prongs in switch expression")); + add_error_note(irb->codegen, msg, underscore_prong, + buf_sprintf("previous '_' prong is here")); + return irb->codegen->invalid_inst_src; + } + underscore_prong = prong_node; + } else { + continue; + } + if (underscore_prong && else_prong) { + ErrorMsg *msg = add_node_error(irb->codegen, prong_node, + buf_sprintf("else and '_' prong in switch expression")); + if (underscore_prong == prong_node) + add_error_note(irb->codegen, msg, else_prong, + buf_sprintf("else prong is here")); + else + add_error_note(irb->codegen, msg, underscore_prong, + buf_sprintf("'_' prong is here")); + return irb->codegen->invalid_inst_src; + } + ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent); + + IrBasicBlockSrc *prev_block = irb->current_basic_block; + if (peer_parent->peers.length > 0) { + peer_parent->peers.last()->next_bb = else_block; + } + peer_parent->peers.append(this_peer_result_loc); + ir_set_cursor_at_end_and_append_block(irb, else_block); + if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block, + is_comptime, var_is_comptime, target_value_ptr, nullptr, 0, &incoming_blocks, &incoming_values, + &switch_else_var, LValNone, &this_peer_result_loc->base)) + { + return irb->codegen->invalid_inst_src; + } + ir_set_cursor_at_end(irb, prev_block); + } + } + + // next do the non-else non-ranges + for (size_t prong_i = 0; prong_i < prong_count; prong_i += 1) { + AstNode *prong_node = node->data.switch_expr.prongs.at(prong_i); + size_t prong_item_count = prong_node->data.switch_prong.items.length; + if (prong_item_count == 0) + continue; + if (prong_node->data.switch_prong.any_items_are_range) + continue; + if (underscore_prong == prong_node) + continue; + + ResultLocPeer *this_peer_result_loc = create_peer_result(peer_parent); + + IrBasicBlockSrc *prong_block = ir_create_basic_block(irb, scope, "SwitchProng"); + IrInstSrc **items = heap::c_allocator.allocate(prong_item_count); + + for (size_t item_i = 0; item_i < prong_item_count; item_i += 1) { + AstNode *item_node = prong_node->data.switch_prong.items.at(item_i); + assert(item_node->type != NodeTypeSwitchRange); + + IrInstSrc *item_value = ir_gen_node(irb, item_node, comptime_scope); + if (item_value == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + + IrInstSrcCheckSwitchProngsRange *check_range = check_ranges.add_one(); + check_range->start = item_value; + check_range->end = item_value; + + IrInstSrcSwitchBrCase *this_case = cases.add_one(); + this_case->value = item_value; + this_case->block = prong_block; + + items[item_i] = item_value; + } + + IrBasicBlockSrc *prev_block = irb->current_basic_block; + if (peer_parent->peers.length > 0) { + peer_parent->peers.last()->next_bb = prong_block; + } + peer_parent->peers.append(this_peer_result_loc); + ir_set_cursor_at_end_and_append_block(irb, prong_block); + if (!ir_gen_switch_prong_expr(irb, subexpr_scope, node, prong_node, end_block, + is_comptime, var_is_comptime, target_value_ptr, items, prong_item_count, + &incoming_blocks, &incoming_values, nullptr, LValNone, &this_peer_result_loc->base)) + { + return irb->codegen->invalid_inst_src; + } + + ir_set_cursor_at_end(irb, prev_block); + + } + + IrInstSrc *switch_prongs_void = ir_build_check_switch_prongs(irb, scope, node, target_value, + check_ranges.items, check_ranges.length, else_prong, underscore_prong != nullptr); + + IrInstSrc *br_instruction; + if (cases.length == 0) { + br_instruction = ir_build_br(irb, scope, node, else_block, is_comptime); + } else { + IrInstSrcSwitchBr *switch_br = ir_build_switch_br_src(irb, scope, node, target_value, else_block, + cases.length, cases.items, is_comptime, switch_prongs_void); + if (switch_else_var != nullptr) { + switch_else_var->switch_br = switch_br; + } + br_instruction = &switch_br->base; + } + if (peer_parent->base.source_instruction == nullptr) { + peer_parent->base.source_instruction = br_instruction; + } + for (size_t i = 0; i < peer_parent->peers.length; i += 1) { + peer_parent->peers.at(i)->base.source_instruction = peer_parent->base.source_instruction; + } + + if (!else_prong && !underscore_prong) { + if (peer_parent->peers.length != 0) { + peer_parent->peers.last()->next_bb = else_block; + } + ir_set_cursor_at_end_and_append_block(irb, else_block); + ir_build_unreachable(irb, scope, node); + } else { + if (peer_parent->peers.length != 0) { + peer_parent->peers.last()->next_bb = end_block; + } + } + + ir_set_cursor_at_end_and_append_block(irb, end_block); + assert(incoming_blocks.length == incoming_values.length); + IrInstSrc *result_instruction; + if (incoming_blocks.length == 0) { + result_instruction = ir_build_const_void(irb, scope, node); + } else { + result_instruction = ir_build_phi(irb, scope, node, incoming_blocks.length, + incoming_blocks.items, incoming_values.items, peer_parent); + } + return ir_lval_wrap(irb, scope, result_instruction, lval, result_loc); +} + +static IrInstSrc *ir_gen_comptime(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node, LVal lval) { + assert(node->type == NodeTypeCompTime); + + Scope *child_scope = create_comptime_scope(irb->codegen, node, parent_scope); + // purposefully pass null for result_loc and let EndExpr handle it + return ir_gen_node_extra(irb, node->data.comptime_expr.expr, child_scope, lval, nullptr); +} + +static IrInstSrc *ir_gen_nosuspend(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node, LVal lval) { + assert(node->type == NodeTypeNoSuspend); + + Scope *child_scope = create_nosuspend_scope(irb->codegen, node, parent_scope); + // purposefully pass null for result_loc and let EndExpr handle it + return ir_gen_node_extra(irb, node->data.comptime_expr.expr, child_scope, lval, nullptr); +} + +static IrInstSrc *ir_gen_return_from_block(IrBuilderSrc *irb, Scope *break_scope, AstNode *node, ScopeBlock *block_scope) { + IrInstSrc *is_comptime; + if (ir_should_inline(irb->exec, break_scope)) { + is_comptime = ir_build_const_bool(irb, break_scope, node, true); + } else { + is_comptime = block_scope->is_comptime; + } + + IrInstSrc *result_value; + if (node->data.break_expr.expr) { + ResultLocPeer *peer_result = create_peer_result(block_scope->peer_parent); + block_scope->peer_parent->peers.append(peer_result); + + result_value = ir_gen_node_extra(irb, node->data.break_expr.expr, break_scope, block_scope->lval, + &peer_result->base); + if (result_value == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + } else { + result_value = ir_build_const_void(irb, break_scope, node); + } + + IrBasicBlockSrc *dest_block = block_scope->end_block; + if (!ir_gen_defers_for_block(irb, break_scope, dest_block->scope, nullptr, nullptr)) + return irb->codegen->invalid_inst_src; + + block_scope->incoming_blocks->append(irb->current_basic_block); + block_scope->incoming_values->append(result_value); + return ir_build_br(irb, break_scope, node, dest_block, is_comptime); +} + +static IrInstSrc *ir_gen_break(IrBuilderSrc *irb, Scope *break_scope, AstNode *node) { + assert(node->type == NodeTypeBreak); + + // Search up the scope. We'll find one of these things first: + // * function definition scope or global scope => error, break outside loop + // * defer expression scope => error, cannot break out of defer expression + // * loop scope => OK + // * (if it's a labeled break) labeled block => OK + + Scope *search_scope = break_scope; + ScopeLoop *loop_scope; + for (;;) { + if (search_scope == nullptr || search_scope->id == ScopeIdFnDef) { + if (node->data.break_expr.name != nullptr) { + add_node_error(irb->codegen, node, buf_sprintf("label not found: '%s'", buf_ptr(node->data.break_expr.name))); + return irb->codegen->invalid_inst_src; + } else { + add_node_error(irb->codegen, node, buf_sprintf("break expression outside loop")); + return irb->codegen->invalid_inst_src; + } + } else if (search_scope->id == ScopeIdDeferExpr) { + add_node_error(irb->codegen, node, buf_sprintf("cannot break out of defer expression")); + return irb->codegen->invalid_inst_src; + } else if (search_scope->id == ScopeIdLoop) { + ScopeLoop *this_loop_scope = (ScopeLoop *)search_scope; + if (node->data.break_expr.name == nullptr || + (this_loop_scope->name != nullptr && buf_eql_buf(node->data.break_expr.name, this_loop_scope->name))) + { + this_loop_scope->name_used = true; + loop_scope = this_loop_scope; + break; + } + } else if (search_scope->id == ScopeIdBlock) { + ScopeBlock *this_block_scope = (ScopeBlock *)search_scope; + if (node->data.break_expr.name != nullptr && + (this_block_scope->name != nullptr && buf_eql_buf(node->data.break_expr.name, this_block_scope->name))) + { + assert(this_block_scope->end_block != nullptr); + this_block_scope->name_used = true; + return ir_gen_return_from_block(irb, break_scope, node, this_block_scope); + } + } else if (search_scope->id == ScopeIdSuspend) { + add_node_error(irb->codegen, node, buf_sprintf("cannot break out of suspend block")); + return irb->codegen->invalid_inst_src; + } + search_scope = search_scope->parent; + } + + IrInstSrc *is_comptime; + if (ir_should_inline(irb->exec, break_scope)) { + is_comptime = ir_build_const_bool(irb, break_scope, node, true); + } else { + is_comptime = loop_scope->is_comptime; + } + + IrInstSrc *result_value; + if (node->data.break_expr.expr) { + ResultLocPeer *peer_result = create_peer_result(loop_scope->peer_parent); + loop_scope->peer_parent->peers.append(peer_result); + + result_value = ir_gen_node_extra(irb, node->data.break_expr.expr, break_scope, + loop_scope->lval, &peer_result->base); + if (result_value == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + } else { + result_value = ir_build_const_void(irb, break_scope, node); + } + + IrBasicBlockSrc *dest_block = loop_scope->break_block; + if (!ir_gen_defers_for_block(irb, break_scope, dest_block->scope, nullptr, nullptr)) + return irb->codegen->invalid_inst_src; + + loop_scope->incoming_blocks->append(irb->current_basic_block); + loop_scope->incoming_values->append(result_value); + return ir_build_br(irb, break_scope, node, dest_block, is_comptime); +} + +static IrInstSrc *ir_gen_continue(IrBuilderSrc *irb, Scope *continue_scope, AstNode *node) { + assert(node->type == NodeTypeContinue); + + // Search up the scope. We'll find one of these things first: + // * function definition scope or global scope => error, break outside loop + // * defer expression scope => error, cannot break out of defer expression + // * loop scope => OK + + ZigList runtime_scopes = {}; + + Scope *search_scope = continue_scope; + ScopeLoop *loop_scope; + for (;;) { + if (search_scope == nullptr || search_scope->id == ScopeIdFnDef) { + if (node->data.continue_expr.name != nullptr) { + add_node_error(irb->codegen, node, buf_sprintf("labeled loop not found: '%s'", buf_ptr(node->data.continue_expr.name))); + return irb->codegen->invalid_inst_src; + } else { + add_node_error(irb->codegen, node, buf_sprintf("continue expression outside loop")); + return irb->codegen->invalid_inst_src; + } + } else if (search_scope->id == ScopeIdDeferExpr) { + add_node_error(irb->codegen, node, buf_sprintf("cannot continue out of defer expression")); + return irb->codegen->invalid_inst_src; + } else if (search_scope->id == ScopeIdLoop) { + ScopeLoop *this_loop_scope = (ScopeLoop *)search_scope; + if (node->data.continue_expr.name == nullptr || + (this_loop_scope->name != nullptr && buf_eql_buf(node->data.continue_expr.name, this_loop_scope->name))) + { + this_loop_scope->name_used = true; + loop_scope = this_loop_scope; + break; + } + } else if (search_scope->id == ScopeIdRuntime) { + ScopeRuntime *scope_runtime = (ScopeRuntime *)search_scope; + runtime_scopes.append(scope_runtime); + } + search_scope = search_scope->parent; + } + + IrInstSrc *is_comptime; + if (ir_should_inline(irb->exec, continue_scope)) { + is_comptime = ir_build_const_bool(irb, continue_scope, node, true); + } else { + is_comptime = loop_scope->is_comptime; + } + + for (size_t i = 0; i < runtime_scopes.length; i += 1) { + ScopeRuntime *scope_runtime = runtime_scopes.at(i); + ir_mark_gen(ir_build_check_runtime_scope(irb, continue_scope, node, scope_runtime->is_comptime, is_comptime)); + } + + IrBasicBlockSrc *dest_block = loop_scope->continue_block; + if (!ir_gen_defers_for_block(irb, continue_scope, dest_block->scope, nullptr, nullptr)) + return irb->codegen->invalid_inst_src; + return ir_mark_gen(ir_build_br(irb, continue_scope, node, dest_block, is_comptime)); +} + +static IrInstSrc *ir_gen_error_type(IrBuilderSrc *irb, Scope *scope, AstNode *node) { + assert(node->type == NodeTypeErrorType); + return ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_global_error_set); +} + +static IrInstSrc *ir_gen_defer(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node) { + assert(node->type == NodeTypeDefer); + + ScopeDefer *defer_child_scope = create_defer_scope(irb->codegen, node, parent_scope); + node->data.defer.child_scope = &defer_child_scope->base; + + ScopeDeferExpr *defer_expr_scope = create_defer_expr_scope(irb->codegen, node, parent_scope); + node->data.defer.expr_scope = &defer_expr_scope->base; + + return ir_build_const_void(irb, parent_scope, node); +} + +static IrInstSrc *ir_gen_slice(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, ResultLoc *result_loc) { + assert(node->type == NodeTypeSliceExpr); + + AstNodeSliceExpr *slice_expr = &node->data.slice_expr; + AstNode *array_node = slice_expr->array_ref_expr; + AstNode *start_node = slice_expr->start; + AstNode *end_node = slice_expr->end; + AstNode *sentinel_node = slice_expr->sentinel; + + IrInstSrc *ptr_value = ir_gen_node_extra(irb, array_node, scope, LValPtr, nullptr); + if (ptr_value == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + + IrInstSrc *start_value = ir_gen_node(irb, start_node, scope); + if (start_value == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + + IrInstSrc *end_value; + if (end_node) { + end_value = ir_gen_node(irb, end_node, scope); + if (end_value == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + } else { + end_value = nullptr; + } + + IrInstSrc *sentinel_value; + if (sentinel_node) { + sentinel_value = ir_gen_node(irb, sentinel_node, scope); + if (sentinel_value == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + } else { + sentinel_value = nullptr; + } + + IrInstSrc *slice = ir_build_slice_src(irb, scope, node, ptr_value, start_value, end_value, + sentinel_value, true, result_loc); + return ir_lval_wrap(irb, scope, slice, lval, result_loc); +} + +static IrInstSrc *ir_gen_catch(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node, LVal lval, + ResultLoc *result_loc) +{ + assert(node->type == NodeTypeCatchExpr); + + AstNode *op1_node = node->data.unwrap_err_expr.op1; + AstNode *op2_node = node->data.unwrap_err_expr.op2; + AstNode *var_node = node->data.unwrap_err_expr.symbol; + + if (op2_node->type == NodeTypeUnreachable) { + if (var_node != nullptr) { + assert(var_node->type == NodeTypeSymbol); + Buf *var_name = var_node->data.symbol_expr.symbol; + add_node_error(irb->codegen, var_node, buf_sprintf("unused variable: '%s'", buf_ptr(var_name))); + return irb->codegen->invalid_inst_src; + } + return ir_gen_catch_unreachable(irb, parent_scope, node, op1_node, lval, result_loc); + } + + + ScopeExpr *spill_scope = create_expr_scope(irb->codegen, op1_node, parent_scope); + spill_scope->spill_harder = true; + + IrInstSrc *err_union_ptr = ir_gen_node_extra(irb, op1_node, &spill_scope->base, LValPtr, nullptr); + if (err_union_ptr == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + + IrInstSrc *is_err = ir_build_test_err_src(irb, parent_scope, node, err_union_ptr, true, false); + + IrInstSrc *is_comptime; + if (ir_should_inline(irb->exec, parent_scope)) { + is_comptime = ir_build_const_bool(irb, parent_scope, node, true); + } else { + is_comptime = ir_build_test_comptime(irb, parent_scope, node, is_err); + } + + IrBasicBlockSrc *ok_block = ir_create_basic_block(irb, parent_scope, "UnwrapErrOk"); + IrBasicBlockSrc *err_block = ir_create_basic_block(irb, parent_scope, "UnwrapErrError"); + IrBasicBlockSrc *end_block = ir_create_basic_block(irb, parent_scope, "UnwrapErrEnd"); + IrInstSrc *cond_br_inst = ir_build_cond_br(irb, parent_scope, node, is_err, err_block, ok_block, is_comptime); + + ResultLocPeerParent *peer_parent = ir_build_binary_result_peers(irb, cond_br_inst, ok_block, end_block, result_loc, + is_comptime); + + ir_set_cursor_at_end_and_append_block(irb, err_block); + Scope *subexpr_scope = create_runtime_scope(irb->codegen, node, &spill_scope->base, is_comptime); + Scope *err_scope; + if (var_node) { + assert(var_node->type == NodeTypeSymbol); + Buf *var_name = var_node->data.symbol_expr.symbol; + bool is_const = true; + bool is_shadowable = false; + ZigVar *var = ir_create_var(irb, node, subexpr_scope, var_name, + is_const, is_const, is_shadowable, is_comptime); + err_scope = var->child_scope; + IrInstSrc *err_ptr = ir_build_unwrap_err_code_src(irb, err_scope, node, err_union_ptr); + IrInstSrc *err_value = ir_build_load_ptr(irb, err_scope, var_node, err_ptr); + build_decl_var_and_init(irb, err_scope, var_node, var, err_value, buf_ptr(var_name), is_comptime); + } else { + err_scope = subexpr_scope; + } + IrInstSrc *err_result = ir_gen_node_extra(irb, op2_node, err_scope, LValNone, &peer_parent->peers.at(0)->base); + if (err_result == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + IrBasicBlockSrc *after_err_block = irb->current_basic_block; + if (!instr_is_unreachable(err_result)) + ir_mark_gen(ir_build_br(irb, parent_scope, node, end_block, is_comptime)); + + ir_set_cursor_at_end_and_append_block(irb, ok_block); + IrInstSrc *unwrapped_ptr = ir_build_unwrap_err_payload_src(irb, parent_scope, node, err_union_ptr, false, false); + IrInstSrc *unwrapped_payload = ir_build_load_ptr(irb, parent_scope, node, unwrapped_ptr); + ir_build_end_expr(irb, parent_scope, node, unwrapped_payload, &peer_parent->peers.at(1)->base); + IrBasicBlockSrc *after_ok_block = irb->current_basic_block; + ir_build_br(irb, parent_scope, node, end_block, is_comptime); + + ir_set_cursor_at_end_and_append_block(irb, end_block); + IrInstSrc **incoming_values = heap::c_allocator.allocate(2); + incoming_values[0] = err_result; + incoming_values[1] = unwrapped_payload; + IrBasicBlockSrc **incoming_blocks = heap::c_allocator.allocate(2); + incoming_blocks[0] = after_err_block; + incoming_blocks[1] = after_ok_block; + IrInstSrc *phi = ir_build_phi(irb, parent_scope, node, 2, incoming_blocks, incoming_values, peer_parent); + return ir_lval_wrap(irb, parent_scope, phi, lval, result_loc); +} + +static bool render_instance_name_recursive(CodeGen *codegen, Buf *name, Scope *outer_scope, Scope *inner_scope) { + if (inner_scope == nullptr || inner_scope == outer_scope) return false; + bool need_comma = render_instance_name_recursive(codegen, name, outer_scope, inner_scope->parent); + if (inner_scope->id != ScopeIdVarDecl) + return need_comma; + + ScopeVarDecl *var_scope = (ScopeVarDecl *)inner_scope; + if (need_comma) + buf_append_char(name, ','); + // TODO: const ptr reinterpret here to make the var type agree with the value? + render_const_value(codegen, name, var_scope->var->const_value); + return true; +} + +static Buf *get_anon_type_name(CodeGen *codegen, IrExecutableSrc *exec, const char *kind_name, + Scope *scope, AstNode *source_node, Buf *out_bare_name) +{ + if (exec != nullptr && exec->name) { + ZigType *import = get_scope_import(scope); + Buf *namespace_name = buf_alloc(); + append_namespace_qualification(codegen, namespace_name, import); + buf_append_buf(namespace_name, exec->name); + buf_init_from_buf(out_bare_name, exec->name); + return namespace_name; + } else if (exec != nullptr && exec->name_fn != nullptr) { + Buf *name = buf_alloc(); + buf_append_buf(name, &exec->name_fn->symbol_name); + buf_appendf(name, "("); + render_instance_name_recursive(codegen, name, &exec->name_fn->fndef_scope->base, exec->begin_scope); + buf_appendf(name, ")"); + buf_init_from_buf(out_bare_name, name); + return name; + } else { + ZigType *import = get_scope_import(scope); + Buf *namespace_name = buf_alloc(); + append_namespace_qualification(codegen, namespace_name, import); + buf_appendf(namespace_name, "%s:%" ZIG_PRI_usize ":%" ZIG_PRI_usize, kind_name, + source_node->line + 1, source_node->column + 1); + buf_init_from_buf(out_bare_name, namespace_name); + return namespace_name; + } +} + +static IrInstSrc *ir_gen_container_decl(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node) { + assert(node->type == NodeTypeContainerDecl); + + ContainerKind kind = node->data.container_decl.kind; + Buf *bare_name = buf_alloc(); + Buf *name = get_anon_type_name(irb->codegen, irb->exec, container_string(kind), parent_scope, node, bare_name); + + ContainerLayout layout = node->data.container_decl.layout; + ZigType *container_type = get_partial_container_type(irb->codegen, parent_scope, + kind, node, buf_ptr(name), bare_name, layout); + ScopeDecls *child_scope = get_container_scope(container_type); + + for (size_t i = 0; i < node->data.container_decl.decls.length; i += 1) { + AstNode *child_node = node->data.container_decl.decls.at(i); + scan_decls(irb->codegen, child_scope, child_node); + } + + TldContainer *tld_container = heap::c_allocator.create(); + init_tld(&tld_container->base, TldIdContainer, bare_name, VisibModPub, node, parent_scope); + tld_container->type_entry = container_type; + tld_container->decls_scope = child_scope; + irb->codegen->resolve_queue.append(&tld_container->base); + + // Add this to the list to mark as invalid if analyzing this exec fails. + irb->exec->tld_list.append(&tld_container->base); + + return ir_build_const_type(irb, parent_scope, node, container_type); +} + +// errors should be populated with set1's values +static ZigType *get_error_set_union(CodeGen *g, ErrorTableEntry **errors, ZigType *set1, ZigType *set2, + Buf *type_name) +{ + assert(set1->id == ZigTypeIdErrorSet); + assert(set2->id == ZigTypeIdErrorSet); + + ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet); + 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; + if (type_name == nullptr) { + buf_resize(&err_set_type->name, 0); + buf_appendf(&err_set_type->name, "error{"); + } else { + buf_init_from_buf(&err_set_type->name, type_name); + } + + for (uint32_t i = 0, count = set1->data.error_set.err_count; i < count; i += 1) { + assert(errors[set1->data.error_set.errors[i]->value] == set1->data.error_set.errors[i]); + } + + uint32_t count = set1->data.error_set.err_count; + for (uint32_t i = 0; i < set2->data.error_set.err_count; i += 1) { + ErrorTableEntry *error_entry = set2->data.error_set.errors[i]; + if (errors[error_entry->value] == nullptr) { + count += 1; + } + } + + err_set_type->data.error_set.err_count = count; + err_set_type->data.error_set.errors = heap::c_allocator.allocate(count); + + bool need_comma = false; + for (uint32_t i = 0; i < set1->data.error_set.err_count; i += 1) { + ErrorTableEntry *error_entry = set1->data.error_set.errors[i]; + if (type_name == nullptr) { + const char *comma = need_comma ? "," : ""; + need_comma = true; + buf_appendf(&err_set_type->name, "%s%s", comma, buf_ptr(&error_entry->name)); + } + err_set_type->data.error_set.errors[i] = error_entry; + } + + uint32_t index = set1->data.error_set.err_count; + for (uint32_t i = 0; i < set2->data.error_set.err_count; i += 1) { + ErrorTableEntry *error_entry = set2->data.error_set.errors[i]; + if (errors[error_entry->value] == nullptr) { + errors[error_entry->value] = error_entry; + if (type_name == nullptr) { + const char *comma = need_comma ? "," : ""; + need_comma = true; + buf_appendf(&err_set_type->name, "%s%s", comma, buf_ptr(&error_entry->name)); + } + err_set_type->data.error_set.errors[index] = error_entry; + index += 1; + } + } + assert(index == count); + + if (type_name == nullptr) { + buf_appendf(&err_set_type->name, "}"); + } + + return err_set_type; + +} + +static ZigType *make_err_set_with_one_item(CodeGen *g, Scope *parent_scope, AstNode *node, + ErrorTableEntry *err_entry) +{ + ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet); + buf_resize(&err_set_type->name, 0); + buf_appendf(&err_set_type->name, "error{%s}", buf_ptr(&err_entry->name)); + 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; + err_set_type->data.error_set.err_count = 1; + err_set_type->data.error_set.errors = heap::c_allocator.create(); + + err_set_type->data.error_set.errors[0] = err_entry; + + return err_set_type; +} + +static AstNode *ast_field_to_symbol_node(AstNode *err_set_field_node) { + if (err_set_field_node->type == NodeTypeSymbol) { + return err_set_field_node; + } else if (err_set_field_node->type == NodeTypeErrorSetField) { + assert(err_set_field_node->data.err_set_field.field_name->type == NodeTypeSymbol); + return err_set_field_node->data.err_set_field.field_name; + } else { + return err_set_field_node; + } +} + +static IrInstSrc *ir_gen_err_set_decl(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node) { + assert(node->type == NodeTypeErrorSetDecl); + + uint32_t err_count = node->data.err_set_decl.decls.length; + + Buf bare_name = BUF_INIT; + Buf *type_name = get_anon_type_name(irb->codegen, irb->exec, "error", parent_scope, node, &bare_name); + ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet); + buf_init_from_buf(&err_set_type->name, type_name); + err_set_type->data.error_set.err_count = err_count; + err_set_type->size_in_bits = irb->codegen->builtin_types.entry_global_error_set->size_in_bits; + err_set_type->abi_align = irb->codegen->builtin_types.entry_global_error_set->abi_align; + err_set_type->abi_size = irb->codegen->builtin_types.entry_global_error_set->abi_size; + err_set_type->data.error_set.errors = heap::c_allocator.allocate(err_count); + + size_t errors_count = irb->codegen->errors_by_index.length + err_count; + ErrorTableEntry **errors = heap::c_allocator.allocate(errors_count); + + for (uint32_t i = 0; i < err_count; i += 1) { + AstNode *field_node = node->data.err_set_decl.decls.at(i); + AstNode *symbol_node = ast_field_to_symbol_node(field_node); + Buf *err_name = symbol_node->data.symbol_expr.symbol; + ErrorTableEntry *err = heap::c_allocator.create(); + err->decl_node = field_node; + buf_init_from_buf(&err->name, err_name); + + auto existing_entry = irb->codegen->error_table.put_unique(err_name, err); + if (existing_entry) { + err->value = existing_entry->value->value; + } else { + size_t error_value_count = irb->codegen->errors_by_index.length; + assert((uint32_t)error_value_count < (((uint32_t)1) << (uint32_t)irb->codegen->err_tag_type->data.integral.bit_count)); + err->value = error_value_count; + irb->codegen->errors_by_index.append(err); + } + err_set_type->data.error_set.errors[i] = err; + + ErrorTableEntry *prev_err = errors[err->value]; + if (prev_err != nullptr) { + ErrorMsg *msg = add_node_error(irb->codegen, ast_field_to_symbol_node(err->decl_node), + buf_sprintf("duplicate error: '%s'", buf_ptr(&err->name))); + add_error_note(irb->codegen, msg, ast_field_to_symbol_node(prev_err->decl_node), + buf_sprintf("other error here")); + return irb->codegen->invalid_inst_src; + } + errors[err->value] = err; + } + heap::c_allocator.deallocate(errors, errors_count); + return ir_build_const_type(irb, parent_scope, node, err_set_type); +} + +static IrInstSrc *ir_gen_fn_proto(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node) { + assert(node->type == NodeTypeFnProto); + + size_t param_count = node->data.fn_proto.params.length; + IrInstSrc **param_types = heap::c_allocator.allocate(param_count); + + bool is_var_args = false; + for (size_t i = 0; i < param_count; i += 1) { + AstNode *param_node = node->data.fn_proto.params.at(i); + if (param_node->data.param_decl.is_var_args) { + is_var_args = true; + break; + } + if (param_node->data.param_decl.anytype_token == nullptr) { + AstNode *type_node = param_node->data.param_decl.type; + IrInstSrc *type_value = ir_gen_node(irb, type_node, parent_scope); + if (type_value == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + param_types[i] = type_value; + } else { + param_types[i] = nullptr; + } + } + + IrInstSrc *align_value = nullptr; + if (node->data.fn_proto.align_expr != nullptr) { + align_value = ir_gen_node(irb, node->data.fn_proto.align_expr, parent_scope); + if (align_value == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + } + + IrInstSrc *callconv_value = nullptr; + if (node->data.fn_proto.callconv_expr != nullptr) { + callconv_value = ir_gen_node(irb, node->data.fn_proto.callconv_expr, parent_scope); + if (callconv_value == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + } + + IrInstSrc *return_type; + if (node->data.fn_proto.return_anytype_token == nullptr) { + if (node->data.fn_proto.return_type == nullptr) { + return_type = ir_build_const_type(irb, parent_scope, node, irb->codegen->builtin_types.entry_void); + } else { + return_type = ir_gen_node(irb, node->data.fn_proto.return_type, parent_scope); + if (return_type == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + } + } else { + add_node_error(irb->codegen, node, + buf_sprintf("TODO implement inferred return types https://github.com/ziglang/zig/issues/447")); + return irb->codegen->invalid_inst_src; + //return_type = nullptr; + } + + return ir_build_fn_proto(irb, parent_scope, node, param_types, align_value, callconv_value, return_type, is_var_args); +} + +static IrInstSrc *ir_gen_resume(IrBuilderSrc *irb, Scope *scope, AstNode *node) { + assert(node->type == NodeTypeResume); + if (get_scope_nosuspend(scope) != nullptr) { + add_node_error(irb->codegen, node, buf_sprintf("resume in nosuspend scope")); + return irb->codegen->invalid_inst_src; + } + + IrInstSrc *target_inst = ir_gen_node_extra(irb, node->data.resume_expr.expr, scope, LValPtr, nullptr); + if (target_inst == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + + return ir_build_resume_src(irb, scope, node, target_inst); +} + +static IrInstSrc *ir_gen_await_expr(IrBuilderSrc *irb, Scope *scope, AstNode *node, LVal lval, + ResultLoc *result_loc) +{ + assert(node->type == NodeTypeAwaitExpr); + + bool is_nosuspend = get_scope_nosuspend(scope) != nullptr; + + AstNode *expr_node = node->data.await_expr.expr; + if (expr_node->type == NodeTypeFnCallExpr && expr_node->data.fn_call_expr.modifier == CallModifierBuiltin) { + AstNode *fn_ref_expr = expr_node->data.fn_call_expr.fn_ref_expr; + Buf *name = fn_ref_expr->data.symbol_expr.symbol; + auto entry = irb->codegen->builtin_fn_table.maybe_get(name); + if (entry != nullptr) { + BuiltinFnEntry *builtin_fn = entry->value; + if (builtin_fn->id == BuiltinFnIdAsyncCall) { + return ir_gen_async_call(irb, scope, node, expr_node, lval, result_loc); + } + } + } + + ZigFn *fn_entry = exec_fn_entry(irb->exec); + if (!fn_entry) { + add_node_error(irb->codegen, node, buf_sprintf("await outside function definition")); + return irb->codegen->invalid_inst_src; + } + ScopeSuspend *existing_suspend_scope = get_scope_suspend(scope); + if (existing_suspend_scope) { + if (!existing_suspend_scope->reported_err) { + ErrorMsg *msg = add_node_error(irb->codegen, node, buf_sprintf("cannot await inside suspend block")); + add_error_note(irb->codegen, msg, existing_suspend_scope->base.source_node, buf_sprintf("suspend block here")); + existing_suspend_scope->reported_err = true; + } + return irb->codegen->invalid_inst_src; + } + + IrInstSrc *target_inst = ir_gen_node_extra(irb, expr_node, scope, LValPtr, nullptr); + if (target_inst == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + + IrInstSrc *await_inst = ir_build_await_src(irb, scope, node, target_inst, result_loc, is_nosuspend); + return ir_lval_wrap(irb, scope, await_inst, lval, result_loc); +} + +static IrInstSrc *ir_gen_suspend(IrBuilderSrc *irb, Scope *parent_scope, AstNode *node) { + assert(node->type == NodeTypeSuspend); + + ZigFn *fn_entry = exec_fn_entry(irb->exec); + if (!fn_entry) { + add_node_error(irb->codegen, node, buf_sprintf("suspend outside function definition")); + return irb->codegen->invalid_inst_src; + } + if (get_scope_nosuspend(parent_scope) != nullptr) { + add_node_error(irb->codegen, node, buf_sprintf("suspend in nosuspend scope")); + return irb->codegen->invalid_inst_src; + } + + ScopeSuspend *existing_suspend_scope = get_scope_suspend(parent_scope); + if (existing_suspend_scope) { + if (!existing_suspend_scope->reported_err) { + ErrorMsg *msg = add_node_error(irb->codegen, node, buf_sprintf("cannot suspend inside suspend block")); + add_error_note(irb->codegen, msg, existing_suspend_scope->base.source_node, buf_sprintf("other suspend block here")); + existing_suspend_scope->reported_err = true; + } + return irb->codegen->invalid_inst_src; + } + + IrInstSrcSuspendBegin *begin = ir_build_suspend_begin_src(irb, parent_scope, node); + if (node->data.suspend.block != nullptr) { + ScopeSuspend *suspend_scope = create_suspend_scope(irb->codegen, node, parent_scope); + Scope *child_scope = &suspend_scope->base; + IrInstSrc *susp_res = ir_gen_node(irb, node->data.suspend.block, child_scope); + if (susp_res == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + ir_mark_gen(ir_build_check_statement_is_void(irb, child_scope, node->data.suspend.block, susp_res)); + } + + return ir_mark_gen(ir_build_suspend_finish_src(irb, parent_scope, node, begin)); +} + +static IrInstSrc *ir_gen_node_raw(IrBuilderSrc *irb, AstNode *node, Scope *scope, + LVal lval, ResultLoc *result_loc) +{ + assert(scope); + switch (node->type) { + case NodeTypeStructValueField: + case NodeTypeParamDecl: + case NodeTypeUsingNamespace: + case NodeTypeSwitchProng: + case NodeTypeSwitchRange: + case NodeTypeStructField: + case NodeTypeErrorSetField: + case NodeTypeFnDef: + case NodeTypeTestDecl: + zig_unreachable(); + case NodeTypeBlock: + return ir_gen_block(irb, scope, node, lval, result_loc); + case NodeTypeGroupedExpr: + return ir_gen_node_raw(irb, node->data.grouped_expr, scope, lval, result_loc); + case NodeTypeBinOpExpr: + return ir_gen_bin_op(irb, scope, node, lval, result_loc); + case NodeTypeIntLiteral: + return ir_lval_wrap(irb, scope, ir_gen_int_lit(irb, scope, node), lval, result_loc); + case NodeTypeFloatLiteral: + return ir_lval_wrap(irb, scope, ir_gen_float_lit(irb, scope, node), lval, result_loc); + case NodeTypeCharLiteral: + return ir_lval_wrap(irb, scope, ir_gen_char_lit(irb, scope, node), lval, result_loc); + case NodeTypeSymbol: + return ir_gen_symbol(irb, scope, node, lval, result_loc); + case NodeTypeFnCallExpr: + return ir_gen_fn_call(irb, scope, node, lval, result_loc); + case NodeTypeIfBoolExpr: + return ir_gen_if_bool_expr(irb, scope, node, lval, result_loc); + case NodeTypePrefixOpExpr: + return ir_gen_prefix_op_expr(irb, scope, node, lval, result_loc); + case NodeTypeContainerInitExpr: + return ir_gen_container_init_expr(irb, scope, node, lval, result_loc); + case NodeTypeVariableDeclaration: + return ir_gen_var_decl(irb, scope, node); + case NodeTypeWhileExpr: + return ir_gen_while_expr(irb, scope, node, lval, result_loc); + case NodeTypeForExpr: + return ir_gen_for_expr(irb, scope, node, lval, result_loc); + case NodeTypeArrayAccessExpr: + return ir_gen_array_access(irb, scope, node, lval, result_loc); + case NodeTypeReturnExpr: + return ir_gen_return(irb, scope, node, lval, result_loc); + case NodeTypeFieldAccessExpr: + { + IrInstSrc *ptr_instruction = ir_gen_field_access(irb, scope, node); + if (ptr_instruction == irb->codegen->invalid_inst_src) + return ptr_instruction; + if (lval == LValPtr || lval == LValAssign) + return ptr_instruction; + + IrInstSrc *load_ptr = ir_build_load_ptr(irb, scope, node, ptr_instruction); + return ir_expr_wrap(irb, scope, load_ptr, result_loc); + } + case NodeTypePtrDeref: { + AstNode *expr_node = node->data.ptr_deref_expr.target; + + LVal child_lval = lval; + if (child_lval == LValAssign) + child_lval = LValPtr; + + IrInstSrc *value = ir_gen_node_extra(irb, expr_node, scope, child_lval, nullptr); + if (value == irb->codegen->invalid_inst_src) + return value; + + // We essentially just converted any lvalue from &(x.*) to (&x).*; + // this inhibits checking that x is a pointer later, so we directly + // record whether the pointer check is needed + IrInstSrc *un_op = ir_build_un_op_lval(irb, scope, node, IrUnOpDereference, value, lval, result_loc); + return ir_expr_wrap(irb, scope, un_op, result_loc); + } + case NodeTypeUnwrapOptional: { + AstNode *expr_node = node->data.unwrap_optional.expr; + + IrInstSrc *maybe_ptr = ir_gen_node_extra(irb, expr_node, scope, LValPtr, nullptr); + if (maybe_ptr == irb->codegen->invalid_inst_src) + return irb->codegen->invalid_inst_src; + + IrInstSrc *unwrapped_ptr = ir_build_optional_unwrap_ptr(irb, scope, node, maybe_ptr, true ); + if (lval == LValPtr || lval == LValAssign) + return unwrapped_ptr; + + IrInstSrc *load_ptr = ir_build_load_ptr(irb, scope, node, unwrapped_ptr); + return ir_expr_wrap(irb, scope, load_ptr, result_loc); + } + case NodeTypeBoolLiteral: + return ir_lval_wrap(irb, scope, ir_gen_bool_literal(irb, scope, node), lval, result_loc); + case NodeTypeArrayType: + return ir_lval_wrap(irb, scope, ir_gen_array_type(irb, scope, node), lval, result_loc); + case NodeTypePointerType: + return ir_lval_wrap(irb, scope, ir_gen_pointer_type(irb, scope, node), lval, result_loc); + case NodeTypeAnyFrameType: + return ir_lval_wrap(irb, scope, ir_gen_anyframe_type(irb, scope, node), lval, result_loc); + case NodeTypeStringLiteral: + return ir_lval_wrap(irb, scope, ir_gen_string_literal(irb, scope, node), lval, result_loc); + case NodeTypeUndefinedLiteral: + return ir_lval_wrap(irb, scope, ir_gen_undefined_literal(irb, scope, node), lval, result_loc); + case NodeTypeAsmExpr: + return ir_lval_wrap(irb, scope, ir_gen_asm_expr(irb, scope, node), lval, result_loc); + case NodeTypeNullLiteral: + return ir_lval_wrap(irb, scope, ir_gen_null_literal(irb, scope, node), lval, result_loc); + case NodeTypeIfErrorExpr: + return ir_gen_if_err_expr(irb, scope, node, lval, result_loc); + case NodeTypeIfOptional: + return ir_gen_if_optional_expr(irb, scope, node, lval, result_loc); + case NodeTypeSwitchExpr: + return ir_gen_switch_expr(irb, scope, node, lval, result_loc); + case NodeTypeCompTime: + return ir_expr_wrap(irb, scope, ir_gen_comptime(irb, scope, node, lval), result_loc); + case NodeTypeNoSuspend: + return ir_expr_wrap(irb, scope, ir_gen_nosuspend(irb, scope, node, lval), result_loc); + case NodeTypeErrorType: + return ir_lval_wrap(irb, scope, ir_gen_error_type(irb, scope, node), lval, result_loc); + case NodeTypeBreak: + return ir_lval_wrap(irb, scope, ir_gen_break(irb, scope, node), lval, result_loc); + case NodeTypeContinue: + return ir_lval_wrap(irb, scope, ir_gen_continue(irb, scope, node), lval, result_loc); + case NodeTypeUnreachable: + return ir_build_unreachable(irb, scope, node); + case NodeTypeDefer: + return ir_lval_wrap(irb, scope, ir_gen_defer(irb, scope, node), lval, result_loc); + case NodeTypeSliceExpr: + return ir_gen_slice(irb, scope, node, lval, result_loc); + case NodeTypeCatchExpr: + return ir_gen_catch(irb, scope, node, lval, result_loc); + case NodeTypeContainerDecl: + return ir_lval_wrap(irb, scope, ir_gen_container_decl(irb, scope, node), lval, result_loc); + case NodeTypeFnProto: + return ir_lval_wrap(irb, scope, ir_gen_fn_proto(irb, scope, node), lval, result_loc); + case NodeTypeErrorSetDecl: + return ir_lval_wrap(irb, scope, ir_gen_err_set_decl(irb, scope, node), lval, result_loc); + case NodeTypeResume: + return ir_lval_wrap(irb, scope, ir_gen_resume(irb, scope, node), lval, result_loc); + case NodeTypeAwaitExpr: + return ir_gen_await_expr(irb, scope, node, lval, result_loc); + case NodeTypeSuspend: + return ir_lval_wrap(irb, scope, ir_gen_suspend(irb, scope, node), lval, result_loc); + case NodeTypeEnumLiteral: + return ir_lval_wrap(irb, scope, ir_gen_enum_literal(irb, scope, node), lval, result_loc); + case NodeTypeInferredArrayType: + add_node_error(irb->codegen, node, + buf_sprintf("inferred array size invalid here")); + return irb->codegen->invalid_inst_src; + case NodeTypeAnyTypeField: + return ir_lval_wrap(irb, scope, + ir_build_const_type(irb, scope, node, irb->codegen->builtin_types.entry_anytype), lval, result_loc); + } + zig_unreachable(); +} + +static ResultLoc *no_result_loc(void) { + ResultLocNone *result_loc_none = heap::c_allocator.create(); + result_loc_none->base.id = ResultLocIdNone; + return &result_loc_none->base; +} + +static IrInstSrc *ir_gen_node_extra(IrBuilderSrc *irb, AstNode *node, Scope *scope, LVal lval, + ResultLoc *result_loc) +{ + if (lval == LValAssign) { + switch (node->type) { + case NodeTypeStructValueField: + case NodeTypeParamDecl: + case NodeTypeUsingNamespace: + case NodeTypeSwitchProng: + case NodeTypeSwitchRange: + case NodeTypeStructField: + case NodeTypeErrorSetField: + case NodeTypeFnDef: + case NodeTypeTestDecl: + zig_unreachable(); + + // cannot be assigned to + case NodeTypeBlock: + case NodeTypeGroupedExpr: + case NodeTypeBinOpExpr: + case NodeTypeIntLiteral: + case NodeTypeFloatLiteral: + case NodeTypeCharLiteral: + case NodeTypeIfBoolExpr: + case NodeTypeContainerInitExpr: + case NodeTypeVariableDeclaration: + case NodeTypeWhileExpr: + case NodeTypeForExpr: + case NodeTypeReturnExpr: + case NodeTypeBoolLiteral: + case NodeTypeArrayType: + case NodeTypePointerType: + case NodeTypeAnyFrameType: + case NodeTypeStringLiteral: + case NodeTypeUndefinedLiteral: + case NodeTypeAsmExpr: + case NodeTypeNullLiteral: + case NodeTypeIfErrorExpr: + case NodeTypeIfOptional: + case NodeTypeSwitchExpr: + case NodeTypeCompTime: + case NodeTypeNoSuspend: + case NodeTypeErrorType: + case NodeTypeBreak: + case NodeTypeContinue: + case NodeTypeUnreachable: + case NodeTypeDefer: + case NodeTypeSliceExpr: + case NodeTypeCatchExpr: + case NodeTypeContainerDecl: + case NodeTypeFnProto: + case NodeTypeErrorSetDecl: + case NodeTypeResume: + case NodeTypeAwaitExpr: + case NodeTypeSuspend: + case NodeTypeEnumLiteral: + case NodeTypeInferredArrayType: + case NodeTypeAnyTypeField: + case NodeTypePrefixOpExpr: + add_node_error(irb->codegen, node, + buf_sprintf("invalid left-hand side to assignment")); + return irb->codegen->invalid_inst_src; + + // @field can be assigned to + case NodeTypeFnCallExpr: + if (node->data.fn_call_expr.modifier == CallModifierBuiltin) { + AstNode *fn_ref_expr = node->data.fn_call_expr.fn_ref_expr; + Buf *name = fn_ref_expr->data.symbol_expr.symbol; + auto entry = irb->codegen->builtin_fn_table.maybe_get(name); + + if (!entry) { + add_node_error(irb->codegen, node, + buf_sprintf("invalid builtin function: '%s'", buf_ptr(name))); + return irb->codegen->invalid_inst_src; + } + + if (entry->value->id == BuiltinFnIdField) { + break; + } + } + add_node_error(irb->codegen, node, + buf_sprintf("invalid left-hand side to assignment")); + return irb->codegen->invalid_inst_src; + + + // can be assigned to + case NodeTypeUnwrapOptional: + case NodeTypePtrDeref: + case NodeTypeFieldAccessExpr: + case NodeTypeArrayAccessExpr: + case NodeTypeSymbol: + break; + } + } + if (result_loc == nullptr) { + // Create a result location indicating there is none - but if one gets created + // it will be properly distributed. + result_loc = no_result_loc(); + ir_build_reset_result(irb, scope, node, result_loc); + } + Scope *child_scope; + if (irb->exec->is_inline || + (irb->exec->fn_entry != nullptr && irb->exec->fn_entry->child_scope == scope)) + { + child_scope = scope; + } else { + child_scope = &create_expr_scope(irb->codegen, node, scope)->base; + } + IrInstSrc *result = ir_gen_node_raw(irb, node, child_scope, lval, result_loc); + if (result == irb->codegen->invalid_inst_src) { + if (irb->exec->first_err_trace_msg == nullptr) { + irb->exec->first_err_trace_msg = irb->codegen->trace_err; + } + } + return result; +} + +static IrInstSrc *ir_gen_node(IrBuilderSrc *irb, AstNode *node, Scope *scope) { + return ir_gen_node_extra(irb, node, scope, LValNone, nullptr); +} + +static void invalidate_exec(IrExecutableSrc *exec, ErrorMsg *msg) { + if (exec->first_err_trace_msg != nullptr) + return; + + exec->first_err_trace_msg = msg; + + for (size_t i = 0; i < exec->tld_list.length; i += 1) { + exec->tld_list.items[i]->resolution = TldResolutionInvalid; + } +} + +static void invalidate_exec_gen(IrExecutableGen *exec, ErrorMsg *msg) { + if (exec->first_err_trace_msg != nullptr) + return; + + exec->first_err_trace_msg = msg; + + for (size_t i = 0; i < exec->tld_list.length; i += 1) { + exec->tld_list.items[i]->resolution = TldResolutionInvalid; + } + + if (exec->source_exec != nullptr) + invalidate_exec(exec->source_exec, msg); +} + + +bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutableSrc *ir_executable) { + assert(node->owner); + + IrBuilderSrc ir_builder = {0}; + IrBuilderSrc *irb = &ir_builder; + + irb->codegen = codegen; + irb->exec = ir_executable; + irb->main_block_node = node; + + IrBasicBlockSrc *entry_block = ir_create_basic_block(irb, scope, "Entry"); + ir_set_cursor_at_end_and_append_block(irb, entry_block); + // Entry block gets a reference because we enter it to begin. + ir_ref_bb(irb->current_basic_block); + + IrInstSrc *result = ir_gen_node_extra(irb, node, scope, LValNone, nullptr); + + if (result == irb->codegen->invalid_inst_src) + return false; + + if (irb->exec->first_err_trace_msg != nullptr) { + codegen->trace_err = irb->exec->first_err_trace_msg; + return false; + } + + if (!instr_is_unreachable(result)) { + ir_mark_gen(ir_build_add_implicit_return_type(irb, scope, result->base.source_node, result, nullptr)); + // no need for save_err_ret_addr because this cannot return error + ResultLocReturn *result_loc_ret = heap::c_allocator.create(); + result_loc_ret->base.id = ResultLocIdReturn; + ir_build_reset_result(irb, scope, node, &result_loc_ret->base); + ir_mark_gen(ir_build_end_expr(irb, scope, node, result, &result_loc_ret->base)); + ir_mark_gen(ir_build_return_src(irb, scope, result->base.source_node, result)); + } + + return true; +} + +bool ir_gen_fn(CodeGen *codegen, ZigFn *fn_entry) { + assert(fn_entry); + + IrExecutableSrc *ir_executable = fn_entry->ir_executable; + AstNode *body_node = fn_entry->body_node; + + assert(fn_entry->child_scope); + + return ir_gen(codegen, body_node, fn_entry->child_scope, ir_executable); +} + +static void ir_add_call_stack_errors_gen(CodeGen *codegen, IrExecutableGen *exec, ErrorMsg *err_msg, int limit) { + if (!exec || !exec->source_node || limit < 0) return; + add_error_note(codegen, err_msg, exec->source_node, buf_sprintf("called from here")); + + ir_add_call_stack_errors_gen(codegen, exec->parent_exec, err_msg, limit - 1); +} + +static void ir_add_call_stack_errors(CodeGen *codegen, IrExecutableSrc *exec, ErrorMsg *err_msg, int limit) { + if (!exec || !exec->source_node || limit < 0) return; + add_error_note(codegen, err_msg, exec->source_node, buf_sprintf("called from here")); + + ir_add_call_stack_errors_gen(codegen, exec->parent_exec, err_msg, limit - 1); +} + +static ErrorMsg *exec_add_error_node(CodeGen *codegen, IrExecutableSrc *exec, AstNode *source_node, Buf *msg) { + ErrorMsg *err_msg = add_node_error(codegen, source_node, msg); + invalidate_exec(exec, err_msg); + if (exec->parent_exec) { + ir_add_call_stack_errors(codegen, exec, err_msg, 10); + } + return err_msg; +} + +static ErrorMsg *exec_add_error_node_gen(CodeGen *codegen, IrExecutableGen *exec, AstNode *source_node, Buf *msg) { + ErrorMsg *err_msg = add_node_error(codegen, source_node, msg); + invalidate_exec_gen(exec, err_msg); + if (exec->parent_exec) { + ir_add_call_stack_errors_gen(codegen, exec, err_msg, 10); + } + return err_msg; +} + +static ErrorMsg *ir_add_error_node(IrAnalyze *ira, AstNode *source_node, Buf *msg) { + return exec_add_error_node_gen(ira->codegen, ira->new_irb.exec, source_node, msg); +} + +static ErrorMsg *opt_ir_add_error_node(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, Buf *msg) { + if (ira != nullptr) + return exec_add_error_node_gen(codegen, ira->new_irb.exec, source_node, msg); + else + return add_node_error(codegen, source_node, msg); +} + +static ErrorMsg *ir_add_error(IrAnalyze *ira, IrInst *source_instruction, Buf *msg) { + return ir_add_error_node(ira, source_instruction->source_node, msg); +} + +static void ir_assert_impl(bool ok, IrInst *source_instruction, char const *file, unsigned int line) { + if (ok) return; + src_assert_impl(ok, source_instruction->source_node, file, line); +} + +static void ir_assert_gen_impl(bool ok, IrInstGen *source_instruction, char const *file, unsigned int line) { + if (ok) return; + src_assert_impl(ok, source_instruction->base.source_node, file, line); +} + +// This function takes a comptime ptr and makes the child const value conform to the type +// described by the pointer. +static Error eval_comptime_ptr_reinterpret(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, + ZigValue *ptr_val) +{ + Error err; + assert(ptr_val->type->id == ZigTypeIdPointer); + assert(ptr_val->special == ConstValSpecialStatic); + ZigValue tmp = {}; + tmp.special = ConstValSpecialStatic; + tmp.type = ptr_val->type->data.pointer.child_type; + if ((err = ir_read_const_ptr(ira, codegen, source_node, &tmp, ptr_val))) + return err; + ZigValue *child_val = const_ptr_pointee_unchecked(codegen, ptr_val); + copy_const_val(codegen, child_val, &tmp); + return ErrorNone; +} + +ZigValue *const_ptr_pointee(IrAnalyze *ira, CodeGen *codegen, ZigValue *const_val, + AstNode *source_node) +{ + Error err; + ZigValue *val = const_ptr_pointee_unchecked(codegen, const_val); + if (val == nullptr) return nullptr; + assert(const_val->type->id == ZigTypeIdPointer); + ZigType *expected_type = const_val->type->data.pointer.child_type; + if (expected_type == codegen->builtin_types.entry_anytype) { + return val; + } + switch (type_has_one_possible_value(codegen, expected_type)) { + case OnePossibleValueInvalid: + return nullptr; + case OnePossibleValueNo: + break; + case OnePossibleValueYes: + return get_the_one_possible_value(codegen, expected_type); + } + if (!types_have_same_zig_comptime_repr(codegen, expected_type, val->type)) { + if ((err = eval_comptime_ptr_reinterpret(ira, codegen, source_node, const_val))) + return nullptr; + return const_ptr_pointee_unchecked(codegen, const_val); + } + return val; +} + +static Error ir_exec_scan_for_side_effects(CodeGen *codegen, IrExecutableGen *exec) { + IrBasicBlockGen *bb = exec->basic_block_list.at(0); + for (size_t i = 0; i < bb->instruction_list.length; i += 1) { + IrInstGen *instruction = bb->instruction_list.at(i); + if (instruction->id == IrInstGenIdReturn) { + return ErrorNone; + } else if (ir_inst_gen_has_side_effects(instruction)) { + if (instr_is_comptime(instruction)) { + switch (instruction->id) { + case IrInstGenIdUnwrapErrPayload: + case IrInstGenIdOptionalUnwrapPtr: + case IrInstGenIdUnionFieldPtr: + continue; + default: + break; + } + } + if (get_scope_typeof(instruction->base.scope) != nullptr) { + // doesn't count, it's inside a @TypeOf() + continue; + } + exec_add_error_node_gen(codegen, exec, instruction->base.source_node, + buf_sprintf("unable to evaluate constant expression")); + return ErrorSemanticAnalyzeFail; + } + } + zig_unreachable(); +} + +static bool ir_emit_global_runtime_side_effect(IrAnalyze *ira, IrInst* source_instruction) { + if (ir_should_inline(ira->old_irb.exec, source_instruction->scope)) { + ir_add_error(ira, source_instruction, buf_sprintf("unable to evaluate constant expression")); + return false; + } + return true; +} + +static bool const_val_fits_in_num_lit(ZigValue *const_val, ZigType *num_lit_type) { + return ((num_lit_type->id == ZigTypeIdComptimeFloat && + (const_val->type->id == ZigTypeIdFloat || const_val->type->id == ZigTypeIdComptimeFloat)) || + (num_lit_type->id == ZigTypeIdComptimeInt && + (const_val->type->id == ZigTypeIdInt || const_val->type->id == ZigTypeIdComptimeInt))); +} + +static bool float_has_fraction(ZigValue *const_val) { + if (const_val->type->id == ZigTypeIdComptimeFloat) { + return bigfloat_has_fraction(&const_val->data.x_bigfloat); + } else if (const_val->type->id == ZigTypeIdFloat) { + switch (const_val->type->data.floating.bit_count) { + case 16: + { + float16_t floored = f16_roundToInt(const_val->data.x_f16, softfloat_round_minMag, false); + return !f16_eq(floored, const_val->data.x_f16); + } + case 32: + return floorf(const_val->data.x_f32) != const_val->data.x_f32; + case 64: + return floor(const_val->data.x_f64) != const_val->data.x_f64; + case 128: + { + float128_t floored; + f128M_roundToInt(&const_val->data.x_f128, softfloat_round_minMag, false, &floored); + return !f128M_eq(&floored, &const_val->data.x_f128); + } + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +static void float_append_buf(Buf *buf, ZigValue *const_val) { + if (const_val->type->id == ZigTypeIdComptimeFloat) { + bigfloat_append_buf(buf, &const_val->data.x_bigfloat); + } else if (const_val->type->id == ZigTypeIdFloat) { + switch (const_val->type->data.floating.bit_count) { + case 16: + buf_appendf(buf, "%f", zig_f16_to_double(const_val->data.x_f16)); + break; + case 32: + buf_appendf(buf, "%f", const_val->data.x_f32); + break; + case 64: + buf_appendf(buf, "%f", const_val->data.x_f64); + break; + case 128: + { + // TODO actual implementation + 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)); + + int len = snprintf(buf_ptr(buf) + old_len, extra_len, "%f", double_value); + assert(len > 0); + buf_resize(buf, old_len + len); + break; + } + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +static void float_init_bigint(BigInt *bigint, ZigValue *const_val) { + if (const_val->type->id == ZigTypeIdComptimeFloat) { + bigint_init_bigfloat(bigint, &const_val->data.x_bigfloat); + } else if (const_val->type->id == ZigTypeIdFloat) { + switch (const_val->type->data.floating.bit_count) { + case 16: + { + double x = zig_f16_to_double(const_val->data.x_f16); + if (x >= 0) { + bigint_init_unsigned(bigint, (uint64_t)x); + } else { + bigint_init_unsigned(bigint, (uint64_t)-x); + bigint->is_negative = true; + } + break; + } + case 32: + if (const_val->data.x_f32 >= 0) { + bigint_init_unsigned(bigint, (uint64_t)(const_val->data.x_f32)); + } else { + bigint_init_unsigned(bigint, (uint64_t)(-const_val->data.x_f32)); + bigint->is_negative = true; + } + break; + case 64: + if (const_val->data.x_f64 >= 0) { + bigint_init_unsigned(bigint, (uint64_t)(const_val->data.x_f64)); + } else { + bigint_init_unsigned(bigint, (uint64_t)(-const_val->data.x_f64)); + bigint->is_negative = true; + } + break; + case 128: + { + BigFloat tmp_float; + bigfloat_init_128(&tmp_float, const_val->data.x_f128); + bigint_init_bigfloat(bigint, &tmp_float); + } + break; + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +static void float_init_bigfloat(ZigValue *dest_val, BigFloat *bigfloat) { + if (dest_val->type->id == ZigTypeIdComptimeFloat) { + bigfloat_init_bigfloat(&dest_val->data.x_bigfloat, bigfloat); + } else if (dest_val->type->id == ZigTypeIdFloat) { + switch (dest_val->type->data.floating.bit_count) { + case 16: + dest_val->data.x_f16 = bigfloat_to_f16(bigfloat); + break; + case 32: + dest_val->data.x_f32 = bigfloat_to_f32(bigfloat); + break; + case 64: + dest_val->data.x_f64 = bigfloat_to_f64(bigfloat); + break; + case 80: + zig_panic("TODO"); + case 128: + dest_val->data.x_f128 = bigfloat_to_f128(bigfloat); + break; + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +static void float_init_f16(ZigValue *dest_val, float16_t x) { + if (dest_val->type->id == ZigTypeIdComptimeFloat) { + bigfloat_init_16(&dest_val->data.x_bigfloat, x); + } else if (dest_val->type->id == ZigTypeIdFloat) { + switch (dest_val->type->data.floating.bit_count) { + case 16: + dest_val->data.x_f16 = x; + break; + case 32: + dest_val->data.x_f32 = zig_f16_to_double(x); + break; + case 64: + dest_val->data.x_f64 = zig_f16_to_double(x); + break; + case 128: + f16_to_f128M(x, &dest_val->data.x_f128); + break; + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +static void float_init_f32(ZigValue *dest_val, float x) { + if (dest_val->type->id == ZigTypeIdComptimeFloat) { + bigfloat_init_32(&dest_val->data.x_bigfloat, x); + } else if (dest_val->type->id == ZigTypeIdFloat) { + switch (dest_val->type->data.floating.bit_count) { + case 16: + dest_val->data.x_f16 = zig_double_to_f16(x); + break; + case 32: + dest_val->data.x_f32 = x; + break; + case 64: + dest_val->data.x_f64 = x; + break; + case 128: + { + float32_t x_f32; + memcpy(&x_f32, &x, sizeof(float)); + f32_to_f128M(x_f32, &dest_val->data.x_f128); + break; + } + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +static void float_init_f64(ZigValue *dest_val, double x) { + if (dest_val->type->id == ZigTypeIdComptimeFloat) { + bigfloat_init_64(&dest_val->data.x_bigfloat, x); + } else if (dest_val->type->id == ZigTypeIdFloat) { + switch (dest_val->type->data.floating.bit_count) { + case 16: + dest_val->data.x_f16 = zig_double_to_f16(x); + break; + case 32: + dest_val->data.x_f32 = x; + break; + case 64: + dest_val->data.x_f64 = x; + break; + case 128: + { + float64_t x_f64; + memcpy(&x_f64, &x, sizeof(double)); + f64_to_f128M(x_f64, &dest_val->data.x_f128); + break; + } + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +static void float_init_f128(ZigValue *dest_val, float128_t x) { + if (dest_val->type->id == ZigTypeIdComptimeFloat) { + bigfloat_init_128(&dest_val->data.x_bigfloat, x); + } else if (dest_val->type->id == ZigTypeIdFloat) { + switch (dest_val->type->data.floating.bit_count) { + case 16: + dest_val->data.x_f16 = f128M_to_f16(&x); + break; + case 32: + { + float32_t f32_val = f128M_to_f32(&x); + memcpy(&dest_val->data.x_f32, &f32_val, sizeof(float)); + break; + } + case 64: + { + float64_t f64_val = f128M_to_f64(&x); + memcpy(&dest_val->data.x_f64, &f64_val, sizeof(double)); + break; + } + case 128: + { + memcpy(&dest_val->data.x_f128, &x, sizeof(float128_t)); + break; + } + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +static void float_init_float(ZigValue *dest_val, ZigValue *src_val) { + if (src_val->type->id == ZigTypeIdComptimeFloat) { + float_init_bigfloat(dest_val, &src_val->data.x_bigfloat); + } else if (src_val->type->id == ZigTypeIdFloat) { + switch (src_val->type->data.floating.bit_count) { + case 16: + float_init_f16(dest_val, src_val->data.x_f16); + break; + case 32: + float_init_f32(dest_val, src_val->data.x_f32); + break; + case 64: + float_init_f64(dest_val, src_val->data.x_f64); + break; + case 128: + float_init_f128(dest_val, src_val->data.x_f128); + break; + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +static bool float_is_nan(ZigValue *op) { + if (op->type->id == ZigTypeIdComptimeFloat) { + return bigfloat_is_nan(&op->data.x_bigfloat); + } else if (op->type->id == ZigTypeIdFloat) { + switch (op->type->data.floating.bit_count) { + case 16: + return f16_isSignalingNaN(op->data.x_f16); + case 32: + return op->data.x_f32 != op->data.x_f32; + case 64: + return op->data.x_f64 != op->data.x_f64; + case 128: + return f128M_isSignalingNaN(&op->data.x_f128); + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +static Cmp float_cmp(ZigValue *op1, ZigValue *op2) { + if (op1->type == op2->type) { + if (op1->type->id == ZigTypeIdComptimeFloat) { + return bigfloat_cmp(&op1->data.x_bigfloat, &op2->data.x_bigfloat); + } else if (op1->type->id == ZigTypeIdFloat) { + switch (op1->type->data.floating.bit_count) { + case 16: + if (f16_lt(op1->data.x_f16, op2->data.x_f16)) { + return CmpLT; + } else if (f16_lt(op2->data.x_f16, op1->data.x_f16)) { + return CmpGT; + } else { + return CmpEQ; + } + case 32: + if (op1->data.x_f32 > op2->data.x_f32) { + return CmpGT; + } else if (op1->data.x_f32 < op2->data.x_f32) { + return CmpLT; + } else { + return CmpEQ; + } + case 64: + if (op1->data.x_f64 > op2->data.x_f64) { + return CmpGT; + } else if (op1->data.x_f64 < op2->data.x_f64) { + return CmpLT; + } else { + return CmpEQ; + } + case 128: + if (f128M_lt(&op1->data.x_f128, &op2->data.x_f128)) { + return CmpLT; + } else if (f128M_eq(&op1->data.x_f128, &op2->data.x_f128)) { + return CmpEQ; + } else { + return CmpGT; + } + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } + } + BigFloat op1_big; + BigFloat op2_big; + value_to_bigfloat(&op1_big, op1); + value_to_bigfloat(&op2_big, op2); + return bigfloat_cmp(&op1_big, &op2_big); +} + +// This function cannot handle NaN +static Cmp float_cmp_zero(ZigValue *op) { + if (op->type->id == ZigTypeIdComptimeFloat) { + return bigfloat_cmp_zero(&op->data.x_bigfloat); + } else if (op->type->id == ZigTypeIdFloat) { + switch (op->type->data.floating.bit_count) { + case 16: + { + const float16_t zero = zig_double_to_f16(0); + if (f16_lt(op->data.x_f16, zero)) { + return CmpLT; + } else if (f16_lt(zero, op->data.x_f16)) { + return CmpGT; + } else { + return CmpEQ; + } + } + case 32: + if (op->data.x_f32 < 0.0) { + return CmpLT; + } else if (op->data.x_f32 > 0.0) { + return CmpGT; + } else { + return CmpEQ; + } + case 64: + if (op->data.x_f64 < 0.0) { + return CmpLT; + } else if (op->data.x_f64 > 0.0) { + return CmpGT; + } else { + return CmpEQ; + } + case 128: + float128_t zero_float; + ui32_to_f128M(0, &zero_float); + if (f128M_lt(&op->data.x_f128, &zero_float)) { + return CmpLT; + } else if (f128M_eq(&op->data.x_f128, &zero_float)) { + return CmpEQ; + } else { + return CmpGT; + } + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +static void float_add(ZigValue *out_val, ZigValue *op1, ZigValue *op2) { + assert(op1->type == op2->type); + out_val->type = op1->type; + if (op1->type->id == ZigTypeIdComptimeFloat) { + bigfloat_add(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); + } else if (op1->type->id == ZigTypeIdFloat) { + switch (op1->type->data.floating.bit_count) { + case 16: + out_val->data.x_f16 = f16_add(op1->data.x_f16, op2->data.x_f16); + return; + case 32: + out_val->data.x_f32 = op1->data.x_f32 + op2->data.x_f32; + return; + case 64: + out_val->data.x_f64 = op1->data.x_f64 + op2->data.x_f64; + return; + case 128: + f128M_add(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); + return; + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +static void float_sub(ZigValue *out_val, ZigValue *op1, ZigValue *op2) { + assert(op1->type == op2->type); + out_val->type = op1->type; + if (op1->type->id == ZigTypeIdComptimeFloat) { + bigfloat_sub(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); + } else if (op1->type->id == ZigTypeIdFloat) { + switch (op1->type->data.floating.bit_count) { + case 16: + out_val->data.x_f16 = f16_sub(op1->data.x_f16, op2->data.x_f16); + return; + case 32: + out_val->data.x_f32 = op1->data.x_f32 - op2->data.x_f32; + return; + case 64: + out_val->data.x_f64 = op1->data.x_f64 - op2->data.x_f64; + return; + case 128: + f128M_sub(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); + return; + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +static void float_mul(ZigValue *out_val, ZigValue *op1, ZigValue *op2) { + assert(op1->type == op2->type); + out_val->type = op1->type; + if (op1->type->id == ZigTypeIdComptimeFloat) { + bigfloat_mul(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); + } else if (op1->type->id == ZigTypeIdFloat) { + switch (op1->type->data.floating.bit_count) { + case 16: + out_val->data.x_f16 = f16_mul(op1->data.x_f16, op2->data.x_f16); + return; + case 32: + out_val->data.x_f32 = op1->data.x_f32 * op2->data.x_f32; + return; + case 64: + out_val->data.x_f64 = op1->data.x_f64 * op2->data.x_f64; + return; + case 128: + f128M_mul(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); + return; + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +static void float_div(ZigValue *out_val, ZigValue *op1, ZigValue *op2) { + assert(op1->type == op2->type); + out_val->type = op1->type; + if (op1->type->id == ZigTypeIdComptimeFloat) { + bigfloat_div(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); + } else if (op1->type->id == ZigTypeIdFloat) { + switch (op1->type->data.floating.bit_count) { + case 16: + out_val->data.x_f16 = f16_div(op1->data.x_f16, op2->data.x_f16); + return; + case 32: + out_val->data.x_f32 = op1->data.x_f32 / op2->data.x_f32; + return; + case 64: + out_val->data.x_f64 = op1->data.x_f64 / op2->data.x_f64; + return; + case 128: + f128M_div(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); + return; + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +static void float_div_trunc(ZigValue *out_val, ZigValue *op1, ZigValue *op2) { + assert(op1->type == op2->type); + out_val->type = op1->type; + if (op1->type->id == ZigTypeIdComptimeFloat) { + bigfloat_div_trunc(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); + } else if (op1->type->id == ZigTypeIdFloat) { + switch (op1->type->data.floating.bit_count) { + case 16: + out_val->data.x_f16 = f16_div(op1->data.x_f16, op2->data.x_f16); + out_val->data.x_f16 = f16_roundToInt(out_val->data.x_f16, softfloat_round_minMag, false); + return; + case 32: + out_val->data.x_f32 = truncf(op1->data.x_f32 / op2->data.x_f32); + return; + case 64: + out_val->data.x_f64 = trunc(op1->data.x_f64 / op2->data.x_f64); + return; + case 128: + f128M_div(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); + f128M_roundToInt(&out_val->data.x_f128, softfloat_round_minMag, false, &out_val->data.x_f128); + return; + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +static void float_div_floor(ZigValue *out_val, ZigValue *op1, ZigValue *op2) { + assert(op1->type == op2->type); + out_val->type = op1->type; + if (op1->type->id == ZigTypeIdComptimeFloat) { + bigfloat_div_floor(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); + } else if (op1->type->id == ZigTypeIdFloat) { + switch (op1->type->data.floating.bit_count) { + case 16: + out_val->data.x_f16 = f16_div(op1->data.x_f16, op2->data.x_f16); + out_val->data.x_f16 = f16_roundToInt(out_val->data.x_f16, softfloat_round_min, false); + return; + case 32: + out_val->data.x_f32 = floorf(op1->data.x_f32 / op2->data.x_f32); + return; + case 64: + out_val->data.x_f64 = floor(op1->data.x_f64 / op2->data.x_f64); + return; + case 128: + f128M_div(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); + f128M_roundToInt(&out_val->data.x_f128, softfloat_round_min, false, &out_val->data.x_f128); + return; + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +static void float_rem(ZigValue *out_val, ZigValue *op1, ZigValue *op2) { + assert(op1->type == op2->type); + out_val->type = op1->type; + if (op1->type->id == ZigTypeIdComptimeFloat) { + bigfloat_rem(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); + } else if (op1->type->id == ZigTypeIdFloat) { + switch (op1->type->data.floating.bit_count) { + case 16: + out_val->data.x_f16 = f16_rem(op1->data.x_f16, op2->data.x_f16); + return; + case 32: + out_val->data.x_f32 = fmodf(op1->data.x_f32, op2->data.x_f32); + return; + case 64: + out_val->data.x_f64 = fmod(op1->data.x_f64, op2->data.x_f64); + return; + case 128: + f128M_rem(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); + return; + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +// c = a - b * trunc(a / b) +static float16_t zig_f16_mod(float16_t a, float16_t b) { + float16_t c; + c = f16_div(a, b); + c = f16_roundToInt(c, softfloat_round_min, true); + c = f16_mul(b, c); + c = f16_sub(a, c); + return c; +} + +// c = a - b * trunc(a / b) +static void zig_f128M_mod(const float128_t* a, const float128_t* b, float128_t* c) { + f128M_div(a, b, c); + f128M_roundToInt(c, softfloat_round_min, true, c); + f128M_mul(b, c, c); + f128M_sub(a, c, c); +} + +static void float_mod(ZigValue *out_val, ZigValue *op1, ZigValue *op2) { + assert(op1->type == op2->type); + out_val->type = op1->type; + if (op1->type->id == ZigTypeIdComptimeFloat) { + bigfloat_mod(&out_val->data.x_bigfloat, &op1->data.x_bigfloat, &op2->data.x_bigfloat); + } else if (op1->type->id == ZigTypeIdFloat) { + switch (op1->type->data.floating.bit_count) { + case 16: + out_val->data.x_f16 = zig_f16_mod(op1->data.x_f16, op2->data.x_f16); + return; + case 32: + out_val->data.x_f32 = fmodf(fmodf(op1->data.x_f32, op2->data.x_f32) + op2->data.x_f32, op2->data.x_f32); + return; + case 64: + out_val->data.x_f64 = fmod(fmod(op1->data.x_f64, op2->data.x_f64) + op2->data.x_f64, op2->data.x_f64); + return; + case 128: + zig_f128M_mod(&op1->data.x_f128, &op2->data.x_f128, &out_val->data.x_f128); + return; + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +static void float_negate(ZigValue *out_val, ZigValue *op) { + out_val->type = op->type; + if (op->type->id == ZigTypeIdComptimeFloat) { + bigfloat_negate(&out_val->data.x_bigfloat, &op->data.x_bigfloat); + } else if (op->type->id == ZigTypeIdFloat) { + switch (op->type->data.floating.bit_count) { + case 16: + { + const float16_t zero = zig_double_to_f16(0); + out_val->data.x_f16 = f16_sub(zero, op->data.x_f16); + return; + } + case 32: + out_val->data.x_f32 = -op->data.x_f32; + return; + case 64: + out_val->data.x_f64 = -op->data.x_f64; + return; + case 128: + float128_t zero_f128; + ui32_to_f128M(0, &zero_f128); + f128M_sub(&zero_f128, &op->data.x_f128, &out_val->data.x_f128); + return; + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +void float_write_ieee597(ZigValue *op, uint8_t *buf, bool is_big_endian) { + if (op->type->id != ZigTypeIdFloat) + zig_unreachable(); + + const unsigned n = op->type->data.floating.bit_count / 8; + assert(n <= 16); + + switch (op->type->data.floating.bit_count) { + case 16: + memcpy(buf, &op->data.x_f16, 2); + break; + case 32: + memcpy(buf, &op->data.x_f32, 4); + break; + case 64: + memcpy(buf, &op->data.x_f64, 8); + break; + case 128: + memcpy(buf, &op->data.x_f128, 16); + break; + default: + zig_unreachable(); + } + + if (is_big_endian) { + // Byteswap in place if needed + for (size_t i = 0; i < n / 2; i++) { + uint8_t u = buf[i]; + buf[i] = buf[n - 1 - i]; + buf[n - 1 - i] = u; + } + } +} + +void float_read_ieee597(ZigValue *val, uint8_t *buf, bool is_big_endian) { + if (val->type->id != ZigTypeIdFloat) + zig_unreachable(); + + const unsigned n = val->type->data.floating.bit_count / 8; + assert(n <= 16); + + uint8_t tmp[16]; + uint8_t *ptr = buf; + + if (is_big_endian) { + memcpy(tmp, buf, n); + + // Byteswap if needed + for (size_t i = 0; i < n / 2; i++) { + uint8_t u = tmp[i]; + tmp[i] = tmp[n - 1 - i]; + tmp[n - 1 - i] = u; + } + + ptr = tmp; + } + + switch (val->type->data.floating.bit_count) { + case 16: + memcpy(&val->data.x_f16, ptr, 2); + return; + case 32: + memcpy(&val->data.x_f32, ptr, 4); + return; + case 64: + memcpy(&val->data.x_f64, ptr, 8); + return; + case 128: + memcpy(&val->data.x_f128, ptr, 16); + return; + default: + zig_unreachable(); + } +} + +static void value_to_bigfloat(BigFloat *out, ZigValue *val) { + switch (val->type->id) { + case ZigTypeIdInt: + case ZigTypeIdComptimeInt: + bigfloat_init_bigint(out, &val->data.x_bigint); + return; + case ZigTypeIdComptimeFloat: + *out = val->data.x_bigfloat; + return; + case ZigTypeIdFloat: switch (val->type->data.floating.bit_count) { + case 16: + bigfloat_init_16(out, val->data.x_f16); + return; + case 32: + bigfloat_init_32(out, val->data.x_f32); + return; + case 64: + bigfloat_init_64(out, val->data.x_f64); + return; + case 80: + zig_panic("TODO"); + case 128: + bigfloat_init_128(out, val->data.x_f128); + return; + default: + zig_unreachable(); + } + default: + zig_unreachable(); + } +} + +static bool ir_num_lit_fits_in_other_type(IrAnalyze *ira, IrInstGen *instruction, ZigType *other_type, + bool explicit_cast) +{ + if (type_is_invalid(other_type)) { + return false; + } + + ZigValue *const_val = ir_resolve_const(ira, instruction, LazyOkNoUndef); + if (const_val == nullptr) + return false; + + if (const_val->special == ConstValSpecialLazy) { + switch (const_val->data.x_lazy->id) { + case LazyValueIdAlignOf: { + // This is guaranteed to fit into a u29 + if (other_type->id == ZigTypeIdComptimeInt) + return true; + size_t align_bits = get_align_amt_type(ira->codegen)->data.integral.bit_count; + if (other_type->id == ZigTypeIdInt && !other_type->data.integral.is_signed && + other_type->data.integral.bit_count >= align_bits) + { + return true; + } + break; + } + case LazyValueIdSizeOf: { + // This is guaranteed to fit into a usize + if (other_type->id == ZigTypeIdComptimeInt) + return true; + size_t usize_bits = ira->codegen->builtin_types.entry_usize->data.integral.bit_count; + if (other_type->id == ZigTypeIdInt && !other_type->data.integral.is_signed && + other_type->data.integral.bit_count >= usize_bits) + { + return true; + } + break; + } + default: + break; + } + } + + const_val = ir_resolve_const(ira, instruction, UndefBad); + if (const_val == nullptr) + return false; + + bool const_val_is_int = (const_val->type->id == ZigTypeIdInt || const_val->type->id == ZigTypeIdComptimeInt); + bool const_val_is_float = (const_val->type->id == ZigTypeIdFloat || const_val->type->id == ZigTypeIdComptimeFloat); + assert(const_val_is_int || const_val_is_float); + + if (const_val_is_int && other_type->id == ZigTypeIdComptimeFloat) { + return true; + } + if (other_type->id == ZigTypeIdFloat) { + if (const_val->type->id == ZigTypeIdComptimeInt || const_val->type->id == ZigTypeIdComptimeFloat) { + return true; + } + if (const_val->type->id == ZigTypeIdInt) { + BigFloat tmp_bf; + bigfloat_init_bigint(&tmp_bf, &const_val->data.x_bigint); + BigFloat orig_bf; + switch (other_type->data.floating.bit_count) { + case 16: { + float16_t tmp = bigfloat_to_f16(&tmp_bf); + bigfloat_init_16(&orig_bf, tmp); + break; + } + case 32: { + float tmp = bigfloat_to_f32(&tmp_bf); + bigfloat_init_32(&orig_bf, tmp); + break; + } + case 64: { + double tmp = bigfloat_to_f64(&tmp_bf); + bigfloat_init_64(&orig_bf, tmp); + break; + } + case 80: + zig_panic("TODO"); + case 128: { + float128_t tmp = bigfloat_to_f128(&tmp_bf); + bigfloat_init_128(&orig_bf, tmp); + break; + } + default: + zig_unreachable(); + } + BigInt orig_bi; + bigint_init_bigfloat(&orig_bi, &orig_bf); + if (bigint_cmp(&orig_bi, &const_val->data.x_bigint) == CmpEQ) { + return true; + } + Buf *val_buf = buf_alloc(); + bigint_append_buf(val_buf, &const_val->data.x_bigint, 10); + ir_add_error_node(ira, instruction->base.source_node, + buf_sprintf("type %s cannot represent integer value %s", + buf_ptr(&other_type->name), + buf_ptr(val_buf))); + return false; + } + if (other_type->data.floating.bit_count >= const_val->type->data.floating.bit_count) { + return true; + } + switch (other_type->data.floating.bit_count) { + case 16: + switch (const_val->type->data.floating.bit_count) { + case 32: { + float16_t tmp = zig_double_to_f16(const_val->data.x_f32); + float orig = zig_f16_to_double(tmp); + if (const_val->data.x_f32 == orig) { + return true; + } + break; + } + case 64: { + float16_t tmp = zig_double_to_f16(const_val->data.x_f64); + double orig = zig_f16_to_double(tmp); + if (const_val->data.x_f64 == orig) { + return true; + } + break; + } + case 80: + zig_panic("TODO"); + case 128: { + float16_t tmp = f128M_to_f16(&const_val->data.x_f128); + float128_t orig; + f16_to_f128M(tmp, &orig); + if (f128M_eq(&orig, &const_val->data.x_f128)) { + return true; + } + break; + } + default: + zig_unreachable(); + } + break; + case 32: + switch (const_val->type->data.floating.bit_count) { + case 64: { + float tmp = const_val->data.x_f64; + double orig = tmp; + if (const_val->data.x_f64 == orig) { + return true; + } + break; + } + case 80: + zig_panic("TODO"); + case 128: { + float32_t tmp = f128M_to_f32(&const_val->data.x_f128); + float128_t orig; + f32_to_f128M(tmp, &orig); + if (f128M_eq(&orig, &const_val->data.x_f128)) { + return true; + } + break; + } + default: + zig_unreachable(); + } + break; + case 64: + switch (const_val->type->data.floating.bit_count) { + case 80: + zig_panic("TODO"); + case 128: { + float64_t tmp = f128M_to_f64(&const_val->data.x_f128); + float128_t orig; + f64_to_f128M(tmp, &orig); + if (f128M_eq(&orig, &const_val->data.x_f128)) { + return true; + } + break; + } + default: + zig_unreachable(); + } + break; + case 80: + assert(const_val->type->data.floating.bit_count == 128); + zig_panic("TODO"); + case 128: + return true; + default: + zig_unreachable(); + } + Buf *val_buf = buf_alloc(); + float_append_buf(val_buf, const_val); + ir_add_error_node(ira, instruction->base.source_node, + buf_sprintf("cast of value %s to type '%s' loses information", + buf_ptr(val_buf), + buf_ptr(&other_type->name))); + return false; + } else if (other_type->id == ZigTypeIdInt && const_val_is_int) { + if (!other_type->data.integral.is_signed && const_val->data.x_bigint.is_negative) { + Buf *val_buf = buf_alloc(); + bigint_append_buf(val_buf, &const_val->data.x_bigint, 10); + ir_add_error_node(ira, instruction->base.source_node, + buf_sprintf("cannot cast negative value %s to unsigned integer type '%s'", + buf_ptr(val_buf), + buf_ptr(&other_type->name))); + return false; + } + if (bigint_fits_in_bits(&const_val->data.x_bigint, other_type->data.integral.bit_count, + other_type->data.integral.is_signed)) + { + return true; + } + } else if (const_val_fits_in_num_lit(const_val, other_type)) { + return true; + } else if (other_type->id == ZigTypeIdOptional) { + ZigType *child_type = other_type->data.maybe.child_type; + if (const_val_fits_in_num_lit(const_val, child_type)) { + return true; + } else if (child_type->id == ZigTypeIdInt && const_val_is_int) { + if (!child_type->data.integral.is_signed && const_val->data.x_bigint.is_negative) { + Buf *val_buf = buf_alloc(); + bigint_append_buf(val_buf, &const_val->data.x_bigint, 10); + ir_add_error_node(ira, instruction->base.source_node, + buf_sprintf("cannot cast negative value %s to unsigned integer type '%s'", + buf_ptr(val_buf), + buf_ptr(&child_type->name))); + return false; + } + if (bigint_fits_in_bits(&const_val->data.x_bigint, + child_type->data.integral.bit_count, + child_type->data.integral.is_signed)) + { + return true; + } + } else if (child_type->id == ZigTypeIdFloat && const_val_is_float) { + return true; + } + } + if (explicit_cast && (other_type->id == ZigTypeIdInt || other_type->id == ZigTypeIdComptimeInt) && + const_val_is_float) + { + if (float_has_fraction(const_val)) { + Buf *val_buf = buf_alloc(); + float_append_buf(val_buf, const_val); + + ir_add_error_node(ira, instruction->base.source_node, + buf_sprintf("fractional component prevents float value %s from being casted to type '%s'", + buf_ptr(val_buf), + buf_ptr(&other_type->name))); + return false; + } else { + if (other_type->id == ZigTypeIdComptimeInt) { + return true; + } else { + BigInt bigint; + float_init_bigint(&bigint, const_val); + if (bigint_fits_in_bits(&bigint, other_type->data.integral.bit_count, + other_type->data.integral.is_signed)) + { + return true; + } + } + } + } + + const char *num_lit_str; + Buf *val_buf = buf_alloc(); + if (const_val_is_float) { + num_lit_str = "float"; + float_append_buf(val_buf, const_val); + } else { + num_lit_str = "integer"; + bigint_append_buf(val_buf, &const_val->data.x_bigint, 10); + } + + ir_add_error_node(ira, instruction->base.source_node, + buf_sprintf("%s value %s cannot be coerced to type '%s'", + num_lit_str, + buf_ptr(val_buf), + buf_ptr(&other_type->name))); + return false; +} + +static bool is_tagged_union(ZigType *type) { + if (type->id != ZigTypeIdUnion) + return false; + return (type->data.unionation.decl_node->data.container_decl.auto_enum || + type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr); +} + +static void populate_error_set_table(ErrorTableEntry **errors, ZigType *set) { + assert(set->id == ZigTypeIdErrorSet); + for (uint32_t i = 0; i < set->data.error_set.err_count; i += 1) { + ErrorTableEntry *error_entry = set->data.error_set.errors[i]; + assert(errors[error_entry->value] == nullptr); + errors[error_entry->value] = error_entry; + } +} + +static ErrorTableEntry *better_documented_error(ErrorTableEntry *preferred, ErrorTableEntry *other) { + if (preferred->decl_node->type == NodeTypeErrorSetField) + return preferred; + if (other->decl_node->type == NodeTypeErrorSetField) + return other; + return preferred; +} + +static ZigType *get_error_set_intersection(IrAnalyze *ira, ZigType *set1, ZigType *set2, + AstNode *source_node) +{ + assert(set1->id == ZigTypeIdErrorSet); + assert(set2->id == ZigTypeIdErrorSet); + + if (!resolve_inferred_error_set(ira->codegen, set1, source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } + if (!resolve_inferred_error_set(ira->codegen, set2, source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } + if (type_is_global_error_set(set1)) { + return set2; + } + if (type_is_global_error_set(set2)) { + return set1; + } + size_t errors_count = ira->codegen->errors_by_index.length; + ErrorTableEntry **errors = heap::c_allocator.allocate(errors_count); + populate_error_set_table(errors, set1); + ZigList intersection_list = {}; + + ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet); + buf_resize(&err_set_type->name, 0); + buf_appendf(&err_set_type->name, "error{"); + + bool need_comma = false; + for (uint32_t i = 0; i < set2->data.error_set.err_count; i += 1) { + ErrorTableEntry *error_entry = set2->data.error_set.errors[i]; + ErrorTableEntry *existing_entry = errors[error_entry->value]; + if (existing_entry != nullptr) { + // prefer the one with docs + const char *comma = need_comma ? "," : ""; + need_comma = true; + ErrorTableEntry *existing_entry_with_docs = better_documented_error(existing_entry, error_entry); + intersection_list.append(existing_entry_with_docs); + buf_appendf(&err_set_type->name, "%s%s", comma, buf_ptr(&existing_entry_with_docs->name)); + } + } + heap::c_allocator.deallocate(errors, errors_count); + + err_set_type->data.error_set.err_count = intersection_list.length; + err_set_type->data.error_set.errors = intersection_list.items; + err_set_type->size_in_bits = ira->codegen->builtin_types.entry_global_error_set->size_in_bits; + err_set_type->abi_align = ira->codegen->builtin_types.entry_global_error_set->abi_align; + err_set_type->abi_size = ira->codegen->builtin_types.entry_global_error_set->abi_size; + + buf_appendf(&err_set_type->name, "}"); + + return err_set_type; +} + +static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted_type, + ZigType *actual_type, AstNode *source_node, bool wanted_is_mutable) +{ + CodeGen *g = ira->codegen; + ConstCastOnly result = {}; + result.id = ConstCastResultIdOk; + + Error err; + + if (wanted_type == actual_type) + return result; + + // If pointers have the same representation in memory, they can be "const-casted". + // `const` attribute can be gained + // `volatile` attribute can be gained + // `allowzero` attribute can be gained (whether from explicit attribute, C pointer, or optional pointer) + // but only if !wanted_is_mutable + // alignment can be decreased + // bit offset attributes must match exactly + // PtrLenSingle/PtrLenUnknown must match exactly, but PtrLenC matches either one + // sentinel-terminated pointers can coerce into PtrLenUnknown + ZigType *wanted_ptr_type = get_src_ptr_type(wanted_type); + ZigType *actual_ptr_type = get_src_ptr_type(actual_type); + bool wanted_allows_zero = ptr_allows_addr_zero(wanted_type); + bool actual_allows_zero = ptr_allows_addr_zero(actual_type); + bool wanted_is_c_ptr = wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.ptr_len == PtrLenC; + bool actual_is_c_ptr = actual_type->id == ZigTypeIdPointer && actual_type->data.pointer.ptr_len == PtrLenC; + bool wanted_opt_or_ptr = wanted_ptr_type != nullptr && wanted_ptr_type->id == ZigTypeIdPointer; + bool actual_opt_or_ptr = actual_ptr_type != nullptr && actual_ptr_type->id == ZigTypeIdPointer; + if (wanted_opt_or_ptr && actual_opt_or_ptr) { + bool ok_null_term_ptrs = + wanted_ptr_type->data.pointer.sentinel == nullptr || + (actual_ptr_type->data.pointer.sentinel != nullptr && + const_values_equal(ira->codegen, wanted_ptr_type->data.pointer.sentinel, + actual_ptr_type->data.pointer.sentinel)) || + actual_ptr_type->data.pointer.ptr_len == PtrLenC; + if (!ok_null_term_ptrs) { + result.id = ConstCastResultIdPtrSentinel; + result.data.bad_ptr_sentinel = heap::c_allocator.allocate_nonzero(1); + result.data.bad_ptr_sentinel->wanted_type = wanted_ptr_type; + result.data.bad_ptr_sentinel->actual_type = actual_ptr_type; + return result; + } + bool ptr_lens_equal = actual_ptr_type->data.pointer.ptr_len == wanted_ptr_type->data.pointer.ptr_len; + if (!(ptr_lens_equal || wanted_is_c_ptr || actual_is_c_ptr)) { + result.id = ConstCastResultIdPtrLens; + return result; + } + + bool ok_cv_qualifiers = + (!actual_ptr_type->data.pointer.is_const || wanted_ptr_type->data.pointer.is_const) && + (!actual_ptr_type->data.pointer.is_volatile || wanted_ptr_type->data.pointer.is_volatile); + if (!ok_cv_qualifiers) { + result.id = ConstCastResultIdCV; + result.data.bad_cv = heap::c_allocator.allocate_nonzero(1); + result.data.bad_cv->wanted_type = wanted_ptr_type; + result.data.bad_cv->actual_type = actual_ptr_type; + return result; + } + + ConstCastOnly child = types_match_const_cast_only(ira, wanted_ptr_type->data.pointer.child_type, + actual_ptr_type->data.pointer.child_type, source_node, !wanted_ptr_type->data.pointer.is_const); + if (child.id == ConstCastResultIdInvalid) + return child; + if (child.id != ConstCastResultIdOk) { + result.id = ConstCastResultIdPointerChild; + result.data.pointer_mismatch = heap::c_allocator.allocate_nonzero(1); + result.data.pointer_mismatch->child = child; + result.data.pointer_mismatch->wanted_child = wanted_ptr_type->data.pointer.child_type; + result.data.pointer_mismatch->actual_child = actual_ptr_type->data.pointer.child_type; + return result; + } + bool ok_allows_zero = (wanted_allows_zero && + (actual_allows_zero || !wanted_is_mutable)) || + (!wanted_allows_zero && !actual_allows_zero); + if (!ok_allows_zero) { + result.id = ConstCastResultIdBadAllowsZero; + result.data.bad_allows_zero = heap::c_allocator.allocate_nonzero(1); + result.data.bad_allows_zero->wanted_type = wanted_type; + result.data.bad_allows_zero->actual_type = actual_type; + return result; + } + if ((err = type_resolve(g, actual_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) { + result.id = ConstCastResultIdInvalid; + return result; + } + if ((err = type_resolve(g, wanted_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) { + result.id = ConstCastResultIdInvalid; + return result; + } + if ((err = type_resolve(g, wanted_type, ResolveStatusZeroBitsKnown))) { + result.id = ConstCastResultIdInvalid; + return result; + } + if ((err = type_resolve(g, actual_type, ResolveStatusZeroBitsKnown))) { + result.id = ConstCastResultIdInvalid; + return result; + } + if (type_has_bits(g, wanted_type) == type_has_bits(g, actual_type) && + actual_ptr_type->data.pointer.bit_offset_in_host == wanted_ptr_type->data.pointer.bit_offset_in_host && + actual_ptr_type->data.pointer.host_int_bytes == wanted_ptr_type->data.pointer.host_int_bytes && + get_ptr_align(ira->codegen, actual_ptr_type) >= get_ptr_align(ira->codegen, wanted_ptr_type)) + { + return result; + } + } + + // arrays + if (wanted_type->id == ZigTypeIdArray && actual_type->id == ZigTypeIdArray && + wanted_type->data.array.len == actual_type->data.array.len) + { + ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.array.child_type, + actual_type->data.array.child_type, source_node, wanted_is_mutable); + if (child.id == ConstCastResultIdInvalid) + return child; + if (child.id != ConstCastResultIdOk) { + result.id = ConstCastResultIdArrayChild; + result.data.array_mismatch = heap::c_allocator.allocate_nonzero(1); + result.data.array_mismatch->child = child; + result.data.array_mismatch->wanted_child = wanted_type->data.array.child_type; + result.data.array_mismatch->actual_child = actual_type->data.array.child_type; + return result; + } + bool ok_null_terminated = (wanted_type->data.array.sentinel == nullptr) || + (actual_type->data.array.sentinel != nullptr && + const_values_equal(ira->codegen, wanted_type->data.array.sentinel, actual_type->data.array.sentinel)); + if (!ok_null_terminated) { + result.id = ConstCastResultIdSentinelArrays; + result.data.sentinel_arrays = heap::c_allocator.allocate_nonzero(1); + result.data.sentinel_arrays->child = child; + result.data.sentinel_arrays->wanted_type = wanted_type; + result.data.sentinel_arrays->actual_type = actual_type; + return result; + } + return result; + } + + // slice const + if (is_slice(wanted_type) && is_slice(actual_type)) { + ZigType *actual_ptr_type = actual_type->data.structure.fields[slice_ptr_index]->type_entry; + ZigType *wanted_ptr_type = wanted_type->data.structure.fields[slice_ptr_index]->type_entry; + if ((err = type_resolve(g, actual_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) { + result.id = ConstCastResultIdInvalid; + return result; + } + if ((err = type_resolve(g, wanted_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) { + result.id = ConstCastResultIdInvalid; + return result; + } + bool ok_sentinels = + wanted_ptr_type->data.pointer.sentinel == nullptr || + (actual_ptr_type->data.pointer.sentinel != nullptr && + const_values_equal(ira->codegen, wanted_ptr_type->data.pointer.sentinel, + actual_ptr_type->data.pointer.sentinel)); + if (!ok_sentinels) { + result.id = ConstCastResultIdPtrSentinel; + result.data.bad_ptr_sentinel = heap::c_allocator.allocate_nonzero(1); + result.data.bad_ptr_sentinel->wanted_type = wanted_ptr_type; + result.data.bad_ptr_sentinel->actual_type = actual_ptr_type; + return result; + } + if ((!actual_ptr_type->data.pointer.is_const || wanted_ptr_type->data.pointer.is_const) && + (!actual_ptr_type->data.pointer.is_volatile || wanted_ptr_type->data.pointer.is_volatile) && + actual_ptr_type->data.pointer.bit_offset_in_host == wanted_ptr_type->data.pointer.bit_offset_in_host && + actual_ptr_type->data.pointer.host_int_bytes == wanted_ptr_type->data.pointer.host_int_bytes && + get_ptr_align(g, actual_ptr_type) >= get_ptr_align(g, wanted_ptr_type)) + { + ConstCastOnly child = types_match_const_cast_only(ira, wanted_ptr_type->data.pointer.child_type, + actual_ptr_type->data.pointer.child_type, source_node, !wanted_ptr_type->data.pointer.is_const); + if (child.id == ConstCastResultIdInvalid) + return child; + if (child.id != ConstCastResultIdOk) { + result.id = ConstCastResultIdSliceChild; + result.data.slice_mismatch = heap::c_allocator.allocate_nonzero(1); + result.data.slice_mismatch->child = child; + result.data.slice_mismatch->actual_child = actual_ptr_type->data.pointer.child_type; + result.data.slice_mismatch->wanted_child = wanted_ptr_type->data.pointer.child_type; + } + return result; + } + } + + // optional types + if (wanted_type->id == ZigTypeIdOptional && actual_type->id == ZigTypeIdOptional) { + // Consider the case where the wanted type is ??[*]T and the actual one + // is ?[*]T, we cannot turn the former into the latter even though the + // child types are compatible (?[*]T and [*]T are both represented as a + // pointer). The extra level of indirection in ??[*]T means it's + // represented as a regular, fat, optional type and, as a consequence, + // has a different shape than the one of ?[*]T. + if ((wanted_ptr_type != nullptr) != (actual_ptr_type != nullptr)) { + // The use of type_mismatch is intentional + result.id = ConstCastResultIdOptionalShape; + result.data.type_mismatch = heap::c_allocator.allocate_nonzero(1); + result.data.type_mismatch->wanted_type = wanted_type; + result.data.type_mismatch->actual_type = actual_type; + return result; + } + ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.maybe.child_type, + actual_type->data.maybe.child_type, source_node, wanted_is_mutable); + if (child.id == ConstCastResultIdInvalid) + return child; + if (child.id != ConstCastResultIdOk) { + result.id = ConstCastResultIdOptionalChild; + result.data.optional = heap::c_allocator.allocate_nonzero(1); + result.data.optional->child = child; + result.data.optional->wanted_child = wanted_type->data.maybe.child_type; + result.data.optional->actual_child = actual_type->data.maybe.child_type; + } + return result; + } + + // error union + if (wanted_type->id == ZigTypeIdErrorUnion && actual_type->id == ZigTypeIdErrorUnion) { + ConstCastOnly payload_child = types_match_const_cast_only(ira, wanted_type->data.error_union.payload_type, + actual_type->data.error_union.payload_type, source_node, wanted_is_mutable); + if (payload_child.id == ConstCastResultIdInvalid) + return payload_child; + if (payload_child.id != ConstCastResultIdOk) { + result.id = ConstCastResultIdErrorUnionPayload; + result.data.error_union_payload = heap::c_allocator.allocate_nonzero(1); + result.data.error_union_payload->child = payload_child; + result.data.error_union_payload->wanted_payload = wanted_type->data.error_union.payload_type; + result.data.error_union_payload->actual_payload = actual_type->data.error_union.payload_type; + return result; + } + ConstCastOnly error_set_child = types_match_const_cast_only(ira, wanted_type->data.error_union.err_set_type, + actual_type->data.error_union.err_set_type, source_node, wanted_is_mutable); + if (error_set_child.id == ConstCastResultIdInvalid) + return error_set_child; + if (error_set_child.id != ConstCastResultIdOk) { + result.id = ConstCastResultIdErrorUnionErrorSet; + result.data.error_union_error_set = heap::c_allocator.allocate_nonzero(1); + result.data.error_union_error_set->child = error_set_child; + result.data.error_union_error_set->wanted_err_set = wanted_type->data.error_union.err_set_type; + result.data.error_union_error_set->actual_err_set = actual_type->data.error_union.err_set_type; + return result; + } + return result; + } + + // error set + if (wanted_type->id == ZigTypeIdErrorSet && actual_type->id == ZigTypeIdErrorSet) { + ZigType *contained_set = actual_type; + ZigType *container_set = wanted_type; + + // if the container set is inferred, then this will always work. + if (container_set->data.error_set.infer_fn != nullptr && container_set->data.error_set.incomplete) { + return result; + } + // if the container set is the global one, it will always work. + if (type_is_global_error_set(container_set)) { + return result; + } + + if (!resolve_inferred_error_set(ira->codegen, contained_set, source_node)) { + result.id = ConstCastResultIdUnresolvedInferredErrSet; + return result; + } + + if (type_is_global_error_set(contained_set)) { + result.id = ConstCastResultIdErrSetGlobal; + return result; + } + + size_t errors_count = g->errors_by_index.length; + ErrorTableEntry **errors = heap::c_allocator.allocate(errors_count); + for (uint32_t i = 0; i < container_set->data.error_set.err_count; i += 1) { + ErrorTableEntry *error_entry = container_set->data.error_set.errors[i]; + assert(errors[error_entry->value] == nullptr); + errors[error_entry->value] = error_entry; + } + for (uint32_t i = 0; i < contained_set->data.error_set.err_count; i += 1) { + ErrorTableEntry *contained_error_entry = contained_set->data.error_set.errors[i]; + ErrorTableEntry *error_entry = errors[contained_error_entry->value]; + if (error_entry == nullptr) { + if (result.id == ConstCastResultIdOk) { + result.id = ConstCastResultIdErrSet; + result.data.error_set_mismatch = heap::c_allocator.create(); + } + result.data.error_set_mismatch->missing_errors.append(contained_error_entry); + } + } + heap::c_allocator.deallocate(errors, errors_count); + return result; + } + + // fn + if (wanted_type->id == ZigTypeIdFn && + actual_type->id == ZigTypeIdFn) + { + if (wanted_type->data.fn.fn_type_id.alignment > actual_type->data.fn.fn_type_id.alignment) { + result.id = ConstCastResultIdFnAlign; + return result; + } + if (wanted_type->data.fn.fn_type_id.is_var_args != actual_type->data.fn.fn_type_id.is_var_args) { + result.id = ConstCastResultIdFnVarArgs; + return result; + } + if (wanted_type->data.fn.is_generic != actual_type->data.fn.is_generic) { + result.id = ConstCastResultIdFnIsGeneric; + return result; + } + if (!wanted_type->data.fn.is_generic && + actual_type->data.fn.fn_type_id.return_type->id != ZigTypeIdUnreachable) + { + ConstCastOnly child = types_match_const_cast_only(ira, wanted_type->data.fn.fn_type_id.return_type, + actual_type->data.fn.fn_type_id.return_type, source_node, false); + if (child.id == ConstCastResultIdInvalid) + return child; + if (child.id != ConstCastResultIdOk) { + result.id = ConstCastResultIdFnReturnType; + result.data.return_type = heap::c_allocator.allocate_nonzero(1); + *result.data.return_type = child; + return result; + } + } + if (wanted_type->data.fn.fn_type_id.param_count != actual_type->data.fn.fn_type_id.param_count) { + result.id = ConstCastResultIdFnArgCount; + return result; + } + if (wanted_type->data.fn.fn_type_id.next_param_index != actual_type->data.fn.fn_type_id.next_param_index) { + result.id = ConstCastResultIdFnGenericArgCount; + return result; + } + assert(wanted_type->data.fn.is_generic || + wanted_type->data.fn.fn_type_id.next_param_index == wanted_type->data.fn.fn_type_id.param_count); + for (size_t i = 0; i < wanted_type->data.fn.fn_type_id.param_count; i += 1) { + // note it's reversed for parameters + FnTypeParamInfo *actual_param_info = &actual_type->data.fn.fn_type_id.param_info[i]; + FnTypeParamInfo *expected_param_info = &wanted_type->data.fn.fn_type_id.param_info[i]; + + ConstCastOnly arg_child = types_match_const_cast_only(ira, actual_param_info->type, + expected_param_info->type, source_node, false); + if (arg_child.id == ConstCastResultIdInvalid) + return arg_child; + if (arg_child.id != ConstCastResultIdOk) { + result.id = ConstCastResultIdFnArg; + result.data.fn_arg.arg_index = i; + result.data.fn_arg.actual_param_type = actual_param_info->type; + result.data.fn_arg.expected_param_type = expected_param_info->type; + result.data.fn_arg.child = heap::c_allocator.allocate_nonzero(1); + *result.data.fn_arg.child = arg_child; + return result; + } + + if (expected_param_info->is_noalias != actual_param_info->is_noalias) { + result.id = ConstCastResultIdFnArgNoAlias; + result.data.arg_no_alias.arg_index = i; + return result; + } + } + if (wanted_type->data.fn.fn_type_id.cc != actual_type->data.fn.fn_type_id.cc) { + // ConstCastResultIdFnCC is guaranteed to be the last one reported, meaning everything else is ok. + result.id = ConstCastResultIdFnCC; + return result; + } + return result; + } + + if (wanted_type->id == ZigTypeIdInt && actual_type->id == ZigTypeIdInt) { + if (wanted_type->data.integral.is_signed != actual_type->data.integral.is_signed || + wanted_type->data.integral.bit_count != actual_type->data.integral.bit_count) + { + result.id = ConstCastResultIdIntShorten; + result.data.int_shorten = heap::c_allocator.allocate_nonzero(1); + result.data.int_shorten->wanted_type = wanted_type; + result.data.int_shorten->actual_type = actual_type; + return result; + } + return result; + } + + result.id = ConstCastResultIdType; + result.data.type_mismatch = heap::c_allocator.allocate_nonzero(1); + result.data.type_mismatch->wanted_type = wanted_type; + result.data.type_mismatch->actual_type = actual_type; + return result; +} + +static void update_errors_helper(CodeGen *g, ErrorTableEntry ***errors, size_t *errors_count) { + size_t old_errors_count = *errors_count; + *errors_count = g->errors_by_index.length; + *errors = heap::c_allocator.reallocate(*errors, old_errors_count, *errors_count); +} + +static ZigType *ir_resolve_peer_types(IrAnalyze *ira, AstNode *source_node, ZigType *expected_type, + IrInstGen **instructions, size_t instruction_count) +{ + Error err; + assert(instruction_count >= 1); + IrInstGen *prev_inst; + size_t i = 0; + for (;;) { + prev_inst = instructions[i]; + if (type_is_invalid(prev_inst->value->type)) { + return ira->codegen->builtin_types.entry_invalid; + } + if (prev_inst->value->type->id == ZigTypeIdUnreachable) { + i += 1; + if (i == instruction_count) { + return prev_inst->value->type; + } + continue; + } + break; + } + ErrorTableEntry **errors = nullptr; + size_t errors_count = 0; + ZigType *err_set_type = nullptr; + if (prev_inst->value->type->id == ZigTypeIdErrorSet) { + if (!resolve_inferred_error_set(ira->codegen, prev_inst->value->type, prev_inst->base.source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } + if (type_is_global_error_set(prev_inst->value->type)) { + err_set_type = ira->codegen->builtin_types.entry_global_error_set; + } else { + err_set_type = prev_inst->value->type; + update_errors_helper(ira->codegen, &errors, &errors_count); + + for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { + ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i]; + assert(errors[error_entry->value] == nullptr); + errors[error_entry->value] = error_entry; + } + } + } + + bool any_are_null = (prev_inst->value->type->id == ZigTypeIdNull); + bool convert_to_const_slice = false; + bool make_the_slice_const = false; + bool make_the_pointer_const = false; + for (; i < instruction_count; i += 1) { + IrInstGen *cur_inst = instructions[i]; + ZigType *cur_type = cur_inst->value->type; + ZigType *prev_type = prev_inst->value->type; + + if (type_is_invalid(cur_type)) { + return cur_type; + } + + if (prev_type == cur_type) { + continue; + } + + if (prev_type->id == ZigTypeIdUnreachable) { + prev_inst = cur_inst; + continue; + } + + if (cur_type->id == ZigTypeIdUnreachable) { + continue; + } + + if (prev_type->id == ZigTypeIdErrorSet) { + ir_assert_gen(err_set_type != nullptr, prev_inst); + if (cur_type->id == ZigTypeIdErrorSet) { + if (type_is_global_error_set(err_set_type)) { + continue; + } + bool allow_infer = cur_type->data.error_set.infer_fn != nullptr && + cur_type->data.error_set.infer_fn == ira->new_irb.exec->fn_entry; + if (!allow_infer && !resolve_inferred_error_set(ira->codegen, cur_type, cur_inst->base.source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } + if (!allow_infer && type_is_global_error_set(cur_type)) { + err_set_type = ira->codegen->builtin_types.entry_global_error_set; + prev_inst = cur_inst; + continue; + } + + // number of declared errors might have increased now + update_errors_helper(ira->codegen, &errors, &errors_count); + + // if err_set_type is a superset of cur_type, keep err_set_type. + // if cur_type is a superset of err_set_type, switch err_set_type to cur_type + bool prev_is_superset = true; + for (uint32_t i = 0; i < cur_type->data.error_set.err_count; i += 1) { + ErrorTableEntry *contained_error_entry = cur_type->data.error_set.errors[i]; + ErrorTableEntry *error_entry = errors[contained_error_entry->value]; + if (error_entry == nullptr) { + prev_is_superset = false; + break; + } + } + if (prev_is_superset) { + continue; + } + + // unset everything in errors + for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { + ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i]; + errors[error_entry->value] = nullptr; + } + for (uint32_t i = 0, count = ira->codegen->errors_by_index.length; i < count; i += 1) { + assert(errors[i] == nullptr); + } + for (uint32_t i = 0; i < cur_type->data.error_set.err_count; i += 1) { + ErrorTableEntry *error_entry = cur_type->data.error_set.errors[i]; + assert(errors[error_entry->value] == nullptr); + errors[error_entry->value] = error_entry; + } + bool cur_is_superset = true; + for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { + ErrorTableEntry *contained_error_entry = err_set_type->data.error_set.errors[i]; + ErrorTableEntry *error_entry = errors[contained_error_entry->value]; + if (error_entry == nullptr) { + cur_is_superset = false; + break; + } + } + if (cur_is_superset) { + err_set_type = cur_type; + prev_inst = cur_inst; + assert(errors != nullptr); + continue; + } + + // neither of them are supersets. so we invent a new error set type that is a union of both of them + err_set_type = get_error_set_union(ira->codegen, errors, cur_type, err_set_type, nullptr); + assert(errors != nullptr); + continue; + } else if (cur_type->id == ZigTypeIdErrorUnion) { + if (type_is_global_error_set(err_set_type)) { + prev_inst = cur_inst; + continue; + } + ZigType *cur_err_set_type = cur_type->data.error_union.err_set_type; + bool allow_infer = cur_err_set_type->data.error_set.infer_fn != nullptr && + cur_err_set_type->data.error_set.infer_fn == ira->new_irb.exec->fn_entry; + if (!allow_infer && !resolve_inferred_error_set(ira->codegen, cur_err_set_type, cur_inst->base.source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } + if (!allow_infer && type_is_global_error_set(cur_err_set_type)) { + err_set_type = ira->codegen->builtin_types.entry_global_error_set; + prev_inst = cur_inst; + continue; + } + + update_errors_helper(ira->codegen, &errors, &errors_count); + + // test if err_set_type is a subset of cur_type's error set + // unset everything in errors + for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { + ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i]; + errors[error_entry->value] = nullptr; + } + for (uint32_t i = 0, count = ira->codegen->errors_by_index.length; i < count; i += 1) { + assert(errors[i] == nullptr); + } + for (uint32_t i = 0; i < cur_err_set_type->data.error_set.err_count; i += 1) { + ErrorTableEntry *error_entry = cur_err_set_type->data.error_set.errors[i]; + assert(errors[error_entry->value] == nullptr); + errors[error_entry->value] = error_entry; + } + bool cur_is_superset = true; + for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { + ErrorTableEntry *contained_error_entry = err_set_type->data.error_set.errors[i]; + ErrorTableEntry *error_entry = errors[contained_error_entry->value]; + if (error_entry == nullptr) { + cur_is_superset = false; + break; + } + } + if (cur_is_superset) { + err_set_type = cur_err_set_type; + prev_inst = cur_inst; + assert(errors != nullptr); + continue; + } + + // not a subset. invent new error set type, union of both of them + err_set_type = get_error_set_union(ira->codegen, errors, cur_err_set_type, err_set_type, nullptr); + prev_inst = cur_inst; + assert(errors != nullptr); + continue; + } else { + prev_inst = cur_inst; + continue; + } + } + + if (cur_type->id == ZigTypeIdErrorSet) { + bool allow_infer = cur_type->data.error_set.infer_fn != nullptr && + cur_type->data.error_set.infer_fn == ira->new_irb.exec->fn_entry; + if (!allow_infer && !resolve_inferred_error_set(ira->codegen, cur_type, cur_inst->base.source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } + if (!allow_infer && type_is_global_error_set(cur_type)) { + err_set_type = ira->codegen->builtin_types.entry_global_error_set; + continue; + } + if (err_set_type != nullptr && type_is_global_error_set(err_set_type)) { + continue; + } + + update_errors_helper(ira->codegen, &errors, &errors_count); + + if (err_set_type == nullptr) { + bool allow_infer = false; + if (prev_type->id == ZigTypeIdErrorUnion) { + err_set_type = prev_type->data.error_union.err_set_type; + allow_infer = err_set_type->data.error_set.infer_fn != nullptr && + err_set_type->data.error_set.infer_fn == ira->new_irb.exec->fn_entry; + } else { + err_set_type = cur_type; + } + + if (!allow_infer && !resolve_inferred_error_set(ira->codegen, err_set_type, cur_inst->base.source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } + + if (!allow_infer && type_is_global_error_set(err_set_type)) { + err_set_type = ira->codegen->builtin_types.entry_global_error_set; + continue; + } + + update_errors_helper(ira->codegen, &errors, &errors_count); + + for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { + ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i]; + assert(errors[error_entry->value] == nullptr); + errors[error_entry->value] = error_entry; + } + if (err_set_type == cur_type) { + continue; + } + } + // check if the cur type error set is a subset + bool prev_is_superset = true; + for (uint32_t i = 0; i < cur_type->data.error_set.err_count; i += 1) { + ErrorTableEntry *contained_error_entry = cur_type->data.error_set.errors[i]; + ErrorTableEntry *error_entry = errors[contained_error_entry->value]; + if (error_entry == nullptr) { + prev_is_superset = false; + break; + } + } + if (prev_is_superset) { + continue; + } + // not a subset. invent new error set type, union of both of them + err_set_type = get_error_set_union(ira->codegen, errors, err_set_type, cur_type, nullptr); + assert(errors != nullptr); + continue; + } + + if (prev_type->id == ZigTypeIdErrorUnion && cur_type->id == ZigTypeIdErrorUnion) { + ZigType *prev_payload_type = prev_type->data.error_union.payload_type; + ZigType *cur_payload_type = cur_type->data.error_union.payload_type; + + bool const_cast_prev = types_match_const_cast_only(ira, prev_payload_type, cur_payload_type, + source_node, false).id == ConstCastResultIdOk; + bool const_cast_cur = types_match_const_cast_only(ira, cur_payload_type, prev_payload_type, + source_node, false).id == ConstCastResultIdOk; + + if (const_cast_prev || const_cast_cur) { + if (const_cast_cur) { + prev_inst = cur_inst; + } + + ZigType *prev_err_set_type = (err_set_type == nullptr) ? prev_type->data.error_union.err_set_type : err_set_type; + ZigType *cur_err_set_type = cur_type->data.error_union.err_set_type; + if (prev_err_set_type == cur_err_set_type) + continue; + + bool allow_infer_prev = prev_err_set_type->data.error_set.infer_fn != nullptr && + prev_err_set_type->data.error_set.infer_fn == ira->new_irb.exec->fn_entry; + bool allow_infer_cur = cur_err_set_type->data.error_set.infer_fn != nullptr && + cur_err_set_type->data.error_set.infer_fn == ira->new_irb.exec->fn_entry; + + if (!allow_infer_prev && !resolve_inferred_error_set(ira->codegen, prev_err_set_type, cur_inst->base.source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } + + if (!allow_infer_cur && !resolve_inferred_error_set(ira->codegen, cur_err_set_type, cur_inst->base.source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } + + if ((!allow_infer_prev && type_is_global_error_set(prev_err_set_type)) || + (!allow_infer_cur && type_is_global_error_set(cur_err_set_type))) + { + err_set_type = ira->codegen->builtin_types.entry_global_error_set; + continue; + } + + update_errors_helper(ira->codegen, &errors, &errors_count); + + if (err_set_type == nullptr) { + err_set_type = prev_err_set_type; + for (uint32_t i = 0; i < prev_err_set_type->data.error_set.err_count; i += 1) { + ErrorTableEntry *error_entry = prev_err_set_type->data.error_set.errors[i]; + assert(errors[error_entry->value] == nullptr); + errors[error_entry->value] = error_entry; + } + } + bool prev_is_superset = true; + for (uint32_t i = 0; i < cur_err_set_type->data.error_set.err_count; i += 1) { + ErrorTableEntry *contained_error_entry = cur_err_set_type->data.error_set.errors[i]; + ErrorTableEntry *error_entry = errors[contained_error_entry->value]; + if (error_entry == nullptr) { + prev_is_superset = false; + break; + } + } + if (prev_is_superset) { + continue; + } + // unset all the errors + for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { + ErrorTableEntry *error_entry = err_set_type->data.error_set.errors[i]; + errors[error_entry->value] = nullptr; + } + for (uint32_t i = 0, count = ira->codegen->errors_by_index.length; i < count; i += 1) { + assert(errors[i] == nullptr); + } + for (uint32_t i = 0; i < cur_err_set_type->data.error_set.err_count; i += 1) { + ErrorTableEntry *error_entry = cur_err_set_type->data.error_set.errors[i]; + assert(errors[error_entry->value] == nullptr); + errors[error_entry->value] = error_entry; + } + bool cur_is_superset = true; + for (uint32_t i = 0; i < prev_err_set_type->data.error_set.err_count; i += 1) { + ErrorTableEntry *contained_error_entry = prev_err_set_type->data.error_set.errors[i]; + ErrorTableEntry *error_entry = errors[contained_error_entry->value]; + if (error_entry == nullptr) { + cur_is_superset = false; + break; + } + } + if (cur_is_superset) { + err_set_type = cur_err_set_type; + continue; + } + + err_set_type = get_error_set_union(ira->codegen, errors, cur_err_set_type, prev_err_set_type, nullptr); + continue; + } + } + + if (prev_type->id == ZigTypeIdNull) { + prev_inst = cur_inst; + any_are_null = true; + continue; + } + + if (cur_type->id == ZigTypeIdNull) { + any_are_null = true; + continue; + } + + if (prev_type->id == ZigTypeIdEnum && cur_type->id == ZigTypeIdEnumLiteral) { + TypeEnumField *field = find_enum_type_field(prev_type, cur_inst->value->data.x_enum_literal); + if (field != nullptr) { + continue; + } + } + if (is_tagged_union(prev_type) && cur_type->id == ZigTypeIdEnumLiteral) { + TypeUnionField *field = find_union_type_field(prev_type, cur_inst->value->data.x_enum_literal); + if (field != nullptr) { + continue; + } + } + + if (cur_type->id == ZigTypeIdEnum && prev_type->id == ZigTypeIdEnumLiteral) { + TypeEnumField *field = find_enum_type_field(cur_type, prev_inst->value->data.x_enum_literal); + if (field != nullptr) { + prev_inst = cur_inst; + continue; + } + } + + if (is_tagged_union(cur_type) && prev_type->id == ZigTypeIdEnumLiteral) { + TypeUnionField *field = find_union_type_field(cur_type, prev_inst->value->data.x_enum_literal); + if (field != nullptr) { + prev_inst = cur_inst; + continue; + } + } + + if (prev_type->id == ZigTypeIdPointer && prev_type->data.pointer.ptr_len == PtrLenC && + (cur_type->id == ZigTypeIdComptimeInt || cur_type->id == ZigTypeIdInt)) + { + continue; + } + + if (cur_type->id == ZigTypeIdPointer && cur_type->data.pointer.ptr_len == PtrLenC && + (prev_type->id == ZigTypeIdComptimeInt || prev_type->id == ZigTypeIdInt)) + { + prev_inst = cur_inst; + continue; + } + + if (prev_type->id == ZigTypeIdPointer && cur_type->id == ZigTypeIdPointer) { + if (prev_type->data.pointer.ptr_len == PtrLenC && + types_match_const_cast_only(ira, prev_type->data.pointer.child_type, + cur_type->data.pointer.child_type, source_node, + !prev_type->data.pointer.is_const).id == ConstCastResultIdOk) + { + continue; + } + if (cur_type->data.pointer.ptr_len == PtrLenC && + types_match_const_cast_only(ira, cur_type->data.pointer.child_type, + prev_type->data.pointer.child_type, source_node, + !cur_type->data.pointer.is_const).id == ConstCastResultIdOk) + { + prev_inst = cur_inst; + continue; + } + } + + if (types_match_const_cast_only(ira, prev_type, cur_type, source_node, false).id == ConstCastResultIdOk) { + continue; + } + + if (types_match_const_cast_only(ira, cur_type, prev_type, source_node, false).id == ConstCastResultIdOk) { + prev_inst = cur_inst; + continue; + } + + if (prev_type->id == ZigTypeIdInt && + cur_type->id == ZigTypeIdInt && + prev_type->data.integral.is_signed == cur_type->data.integral.is_signed) + { + if (cur_type->data.integral.bit_count > prev_type->data.integral.bit_count) { + prev_inst = cur_inst; + } + continue; + } + + if (prev_type->id == ZigTypeIdFloat && cur_type->id == ZigTypeIdFloat) { + if (cur_type->data.floating.bit_count > prev_type->data.floating.bit_count) { + prev_inst = cur_inst; + } + continue; + } + + if (prev_type->id == ZigTypeIdErrorUnion && + types_match_const_cast_only(ira, prev_type->data.error_union.payload_type, cur_type, + source_node, false).id == ConstCastResultIdOk) + { + continue; + } + + if (cur_type->id == ZigTypeIdErrorUnion && + types_match_const_cast_only(ira, cur_type->data.error_union.payload_type, prev_type, + source_node, false).id == ConstCastResultIdOk) + { + if (err_set_type != nullptr) { + ZigType *cur_err_set_type = cur_type->data.error_union.err_set_type; + bool allow_infer = cur_err_set_type->data.error_set.infer_fn != nullptr && + cur_err_set_type->data.error_set.infer_fn == ira->new_irb.exec->fn_entry; + if (!allow_infer && !resolve_inferred_error_set(ira->codegen, cur_err_set_type, cur_inst->base.source_node)) { + return ira->codegen->builtin_types.entry_invalid; + } + if ((!allow_infer && type_is_global_error_set(cur_err_set_type)) || + type_is_global_error_set(err_set_type)) + { + err_set_type = ira->codegen->builtin_types.entry_global_error_set; + prev_inst = cur_inst; + continue; + } + + update_errors_helper(ira->codegen, &errors, &errors_count); + + err_set_type = get_error_set_union(ira->codegen, errors, err_set_type, cur_err_set_type, nullptr); + } + prev_inst = cur_inst; + continue; + } + + if (prev_type->id == ZigTypeIdOptional && + types_match_const_cast_only(ira, prev_type->data.maybe.child_type, cur_type, + source_node, false).id == ConstCastResultIdOk) + { + continue; + } + + if (cur_type->id == ZigTypeIdOptional && + types_match_const_cast_only(ira, cur_type->data.maybe.child_type, prev_type, + source_node, false).id == ConstCastResultIdOk) + { + prev_inst = cur_inst; + continue; + } + + if (prev_type->id == ZigTypeIdOptional && + types_match_const_cast_only(ira, cur_type, prev_type->data.maybe.child_type, + source_node, false).id == ConstCastResultIdOk) + { + prev_inst = cur_inst; + any_are_null = true; + continue; + } + + if (cur_type->id == ZigTypeIdOptional && + types_match_const_cast_only(ira, prev_type, cur_type->data.maybe.child_type, + source_node, false).id == ConstCastResultIdOk) + { + any_are_null = true; + continue; + } + + if (cur_type->id == ZigTypeIdUndefined) { + continue; + } + + if (prev_type->id == ZigTypeIdUndefined) { + prev_inst = cur_inst; + continue; + } + + if (prev_type->id == ZigTypeIdComptimeInt || + prev_type->id == ZigTypeIdComptimeFloat) + { + if (ir_num_lit_fits_in_other_type(ira, prev_inst, cur_type, false)) { + prev_inst = cur_inst; + continue; + } else { + return ira->codegen->builtin_types.entry_invalid; + } + } + + if (cur_type->id == ZigTypeIdComptimeInt || + cur_type->id == ZigTypeIdComptimeFloat) + { + if (ir_num_lit_fits_in_other_type(ira, cur_inst, prev_type, false)) { + continue; + } else { + return ira->codegen->builtin_types.entry_invalid; + } + } + + // *[N]T to [*]T + if (prev_type->id == ZigTypeIdPointer && + prev_type->data.pointer.ptr_len == PtrLenSingle && + prev_type->data.pointer.child_type->id == ZigTypeIdArray && + ((cur_type->id == ZigTypeIdPointer && cur_type->data.pointer.ptr_len == PtrLenUnknown))) + { + convert_to_const_slice = false; + prev_inst = cur_inst; + + if (prev_type->data.pointer.is_const && !cur_type->data.pointer.is_const) { + // const array pointer and non-const unknown pointer + make_the_pointer_const = true; + } + continue; + } + + // *[N]T to [*]T + if (cur_type->id == ZigTypeIdPointer && + cur_type->data.pointer.ptr_len == PtrLenSingle && + cur_type->data.pointer.child_type->id == ZigTypeIdArray && + ((prev_type->id == ZigTypeIdPointer && prev_type->data.pointer.ptr_len == PtrLenUnknown))) + { + if (cur_type->data.pointer.is_const && !prev_type->data.pointer.is_const) { + // const array pointer and non-const unknown pointer + make_the_pointer_const = true; + } + continue; + } + + // *[N]T to []T + // *[N]T to E![]T + if (cur_type->id == ZigTypeIdPointer && + cur_type->data.pointer.ptr_len == PtrLenSingle && + cur_type->data.pointer.child_type->id == ZigTypeIdArray && + ((prev_type->id == ZigTypeIdErrorUnion && is_slice(prev_type->data.error_union.payload_type)) || + is_slice(prev_type))) + { + ZigType *array_type = cur_type->data.pointer.child_type; + ZigType *slice_type = (prev_type->id == ZigTypeIdErrorUnion) ? + prev_type->data.error_union.payload_type : prev_type; + ZigType *slice_ptr_type = slice_type->data.structure.fields[slice_ptr_index]->type_entry; + if (types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type, + array_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk) + { + bool const_ok = (slice_ptr_type->data.pointer.is_const || array_type->data.array.len == 0 || + !cur_type->data.pointer.is_const); + if (!const_ok) make_the_slice_const = true; + convert_to_const_slice = false; + continue; + } + } + + // *[N]T to []T + // *[N]T to E![]T + if (prev_type->id == ZigTypeIdPointer && + prev_type->data.pointer.child_type->id == ZigTypeIdArray && + prev_type->data.pointer.ptr_len == PtrLenSingle && + ((cur_type->id == ZigTypeIdErrorUnion && is_slice(cur_type->data.error_union.payload_type)) || + (cur_type->id == ZigTypeIdOptional && is_slice(cur_type->data.maybe.child_type)) || + is_slice(cur_type))) + { + ZigType *array_type = prev_type->data.pointer.child_type; + ZigType *slice_type; + switch (cur_type->id) { + case ZigTypeIdErrorUnion: + slice_type = cur_type->data.error_union.payload_type; + break; + case ZigTypeIdOptional: + slice_type = cur_type->data.maybe.child_type; + break; + default: + slice_type = cur_type; + break; + } + ZigType *slice_ptr_type = slice_type->data.structure.fields[slice_ptr_index]->type_entry; + if (types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type, + array_type->data.array.child_type, source_node, false).id == ConstCastResultIdOk) + { + bool const_ok = (slice_ptr_type->data.pointer.is_const || array_type->data.array.len == 0 || + !prev_type->data.pointer.is_const); + if (!const_ok) make_the_slice_const = true; + prev_inst = cur_inst; + convert_to_const_slice = false; + continue; + } + } + + // *[N]T and *[M]T + if (cur_type->id == ZigTypeIdPointer && cur_type->data.pointer.ptr_len == PtrLenSingle && + cur_type->data.pointer.child_type->id == ZigTypeIdArray && + prev_type->id == ZigTypeIdPointer && prev_type->data.pointer.ptr_len == PtrLenSingle && + prev_type->data.pointer.child_type->id == ZigTypeIdArray && + ( + prev_type->data.pointer.child_type->data.array.sentinel == nullptr || + (cur_type->data.pointer.child_type->data.array.sentinel != nullptr && + const_values_equal(ira->codegen, prev_type->data.pointer.child_type->data.array.sentinel, + cur_type->data.pointer.child_type->data.array.sentinel)) + ) && + types_match_const_cast_only(ira, + cur_type->data.pointer.child_type->data.array.child_type, + prev_type->data.pointer.child_type->data.array.child_type, + source_node, !cur_type->data.pointer.is_const).id == ConstCastResultIdOk) + { + bool const_ok = (cur_type->data.pointer.is_const || !prev_type->data.pointer.is_const || + prev_type->data.pointer.child_type->data.array.len == 0); + if (!const_ok) make_the_slice_const = true; + prev_inst = cur_inst; + convert_to_const_slice = true; + continue; + } + if (prev_type->id == ZigTypeIdPointer && prev_type->data.pointer.ptr_len == PtrLenSingle && + prev_type->data.pointer.child_type->id == ZigTypeIdArray && + cur_type->id == ZigTypeIdPointer && cur_type->data.pointer.ptr_len == PtrLenSingle && + cur_type->data.pointer.child_type->id == ZigTypeIdArray && + ( + cur_type->data.pointer.child_type->data.array.sentinel == nullptr || + (prev_type->data.pointer.child_type->data.array.sentinel != nullptr && + const_values_equal(ira->codegen, cur_type->data.pointer.child_type->data.array.sentinel, + prev_type->data.pointer.child_type->data.array.sentinel)) + ) && + types_match_const_cast_only(ira, + prev_type->data.pointer.child_type->data.array.child_type, + cur_type->data.pointer.child_type->data.array.child_type, + source_node, !prev_type->data.pointer.is_const).id == ConstCastResultIdOk) + { + bool const_ok = (prev_type->data.pointer.is_const || !cur_type->data.pointer.is_const || + cur_type->data.pointer.child_type->data.array.len == 0); + if (!const_ok) make_the_slice_const = true; + convert_to_const_slice = true; + continue; + } + + if (prev_type->id == ZigTypeIdEnum && cur_type->id == ZigTypeIdUnion && + (cur_type->data.unionation.decl_node->data.container_decl.auto_enum || cur_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr)) + { + if ((err = type_resolve(ira->codegen, cur_type, ResolveStatusZeroBitsKnown))) + return ira->codegen->builtin_types.entry_invalid; + if (cur_type->data.unionation.tag_type == prev_type) { + continue; + } + } + + if (cur_type->id == ZigTypeIdEnum && prev_type->id == ZigTypeIdUnion && + (prev_type->data.unionation.decl_node->data.container_decl.auto_enum || prev_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr)) + { + if ((err = type_resolve(ira->codegen, prev_type, ResolveStatusZeroBitsKnown))) + return ira->codegen->builtin_types.entry_invalid; + if (prev_type->data.unionation.tag_type == cur_type) { + prev_inst = cur_inst; + continue; + } + } + + ErrorMsg *msg = ir_add_error_node(ira, source_node, + buf_sprintf("incompatible types: '%s' and '%s'", + buf_ptr(&prev_type->name), buf_ptr(&cur_type->name))); + add_error_note(ira->codegen, msg, prev_inst->base.source_node, + buf_sprintf("type '%s' here", buf_ptr(&prev_type->name))); + add_error_note(ira->codegen, msg, cur_inst->base.source_node, + buf_sprintf("type '%s' here", buf_ptr(&cur_type->name))); + + return ira->codegen->builtin_types.entry_invalid; + } + + heap::c_allocator.deallocate(errors, errors_count); + + if (convert_to_const_slice) { + if (prev_inst->value->type->id == ZigTypeIdPointer) { + ZigType *array_type = prev_inst->value->type->data.pointer.child_type; + src_assert(array_type->id == ZigTypeIdArray, source_node); + ZigType *ptr_type = get_pointer_to_type_extra2( + ira->codegen, array_type->data.array.child_type, + prev_inst->value->type->data.pointer.is_const || make_the_slice_const, false, + PtrLenUnknown, + 0, 0, 0, false, + VECTOR_INDEX_NONE, nullptr, array_type->data.array.sentinel); + ZigType *slice_type = get_slice_type(ira->codegen, ptr_type); + if (err_set_type != nullptr) { + return get_error_union_type(ira->codegen, err_set_type, slice_type); + } else { + return slice_type; + } + } else { + zig_unreachable(); + } + } else if (err_set_type != nullptr) { + if (prev_inst->value->type->id == ZigTypeIdErrorSet) { + return err_set_type; + } else if (prev_inst->value->type->id == ZigTypeIdErrorUnion) { + ZigType *payload_type = prev_inst->value->type->data.error_union.payload_type; + if ((err = type_resolve(ira->codegen, payload_type, ResolveStatusSizeKnown))) + return ira->codegen->builtin_types.entry_invalid; + return get_error_union_type(ira->codegen, err_set_type, payload_type); + } else if (expected_type != nullptr && expected_type->id == ZigTypeIdErrorUnion) { + ZigType *payload_type = expected_type->data.error_union.payload_type; + if ((err = type_resolve(ira->codegen, payload_type, ResolveStatusSizeKnown))) + return ira->codegen->builtin_types.entry_invalid; + return get_error_union_type(ira->codegen, err_set_type, payload_type); + } else { + if (prev_inst->value->type->id == ZigTypeIdComptimeInt || + prev_inst->value->type->id == ZigTypeIdComptimeFloat) + { + ir_add_error_node(ira, source_node, + buf_sprintf("unable to make error union out of number literal")); + return ira->codegen->builtin_types.entry_invalid; + } else if (prev_inst->value->type->id == ZigTypeIdNull) { + ir_add_error_node(ira, source_node, + buf_sprintf("unable to make error union out of null literal")); + return ira->codegen->builtin_types.entry_invalid; + } else { + if ((err = type_resolve(ira->codegen, prev_inst->value->type, ResolveStatusSizeKnown))) + return ira->codegen->builtin_types.entry_invalid; + return get_error_union_type(ira->codegen, err_set_type, prev_inst->value->type); + } + } + } else if (any_are_null && prev_inst->value->type->id != ZigTypeIdNull) { + if (prev_inst->value->type->id == ZigTypeIdOptional) { + return prev_inst->value->type; + } else { + if ((err = type_resolve(ira->codegen, prev_inst->value->type, ResolveStatusSizeKnown))) + return ira->codegen->builtin_types.entry_invalid; + return get_optional_type(ira->codegen, prev_inst->value->type); + } + } else if (make_the_slice_const) { + ZigType *slice_type; + if (prev_inst->value->type->id == ZigTypeIdErrorUnion) { + slice_type = prev_inst->value->type->data.error_union.payload_type; + } else if (is_slice(prev_inst->value->type)) { + slice_type = prev_inst->value->type; + } else { + zig_unreachable(); + } + ZigType *slice_ptr_type = slice_type->data.structure.fields[slice_ptr_index]->type_entry; + ZigType *adjusted_ptr_type = adjust_ptr_const(ira->codegen, slice_ptr_type, make_the_slice_const); + ZigType *adjusted_slice_type = get_slice_type(ira->codegen, adjusted_ptr_type); + if (prev_inst->value->type->id == ZigTypeIdErrorUnion) { + return get_error_union_type(ira->codegen, prev_inst->value->type->data.error_union.err_set_type, + adjusted_slice_type); + } else if (is_slice(prev_inst->value->type)) { + return adjusted_slice_type; + } else { + zig_unreachable(); + } + } else if (make_the_pointer_const) { + return adjust_ptr_const(ira->codegen, prev_inst->value->type, make_the_pointer_const); + } else { + return prev_inst->value->type; + } +} + +static bool eval_const_expr_implicit_cast(IrAnalyze *ira, IrInst *source_instr, + CastOp cast_op, + ZigValue *other_val, ZigType *other_type, + ZigValue *const_val, ZigType *new_type) +{ + const_val->special = other_val->special; + + assert(other_val != const_val); + switch (cast_op) { + case CastOpNoCast: + zig_unreachable(); + case CastOpErrSet: + case CastOpBitCast: + zig_panic("TODO"); + case CastOpNoop: { + copy_const_val(ira->codegen, const_val, other_val); + const_val->type = new_type; + break; + } + case CastOpNumLitToConcrete: + if (other_val->type->id == ZigTypeIdComptimeFloat) { + assert(new_type->id == ZigTypeIdFloat); + switch (new_type->data.floating.bit_count) { + case 16: + const_val->data.x_f16 = bigfloat_to_f16(&other_val->data.x_bigfloat); + break; + case 32: + const_val->data.x_f32 = bigfloat_to_f32(&other_val->data.x_bigfloat); + break; + case 64: + const_val->data.x_f64 = bigfloat_to_f64(&other_val->data.x_bigfloat); + break; + case 80: + zig_panic("TODO"); + case 128: + const_val->data.x_f128 = bigfloat_to_f128(&other_val->data.x_bigfloat); + break; + default: + zig_unreachable(); + } + } else if (other_val->type->id == ZigTypeIdComptimeInt) { + bigint_init_bigint(&const_val->data.x_bigint, &other_val->data.x_bigint); + } else { + zig_unreachable(); + } + const_val->type = new_type; + break; + case CastOpIntToFloat: + if (new_type->id == ZigTypeIdFloat) { + BigFloat bigfloat; + bigfloat_init_bigint(&bigfloat, &other_val->data.x_bigint); + switch (new_type->data.floating.bit_count) { + case 16: + const_val->data.x_f16 = bigfloat_to_f16(&bigfloat); + break; + case 32: + const_val->data.x_f32 = bigfloat_to_f32(&bigfloat); + break; + case 64: + const_val->data.x_f64 = bigfloat_to_f64(&bigfloat); + break; + case 80: + zig_panic("TODO"); + case 128: + const_val->data.x_f128 = bigfloat_to_f128(&bigfloat); + break; + default: + zig_unreachable(); + } + } else if (new_type->id == ZigTypeIdComptimeFloat) { + bigfloat_init_bigint(&const_val->data.x_bigfloat, &other_val->data.x_bigint); + } else { + zig_unreachable(); + } + const_val->special = ConstValSpecialStatic; + break; + case CastOpFloatToInt: + float_init_bigint(&const_val->data.x_bigint, other_val); + if (new_type->id == ZigTypeIdInt) { + if (!bigint_fits_in_bits(&const_val->data.x_bigint, new_type->data.integral.bit_count, + new_type->data.integral.is_signed)) + { + Buf *int_buf = buf_alloc(); + bigint_append_buf(int_buf, &const_val->data.x_bigint, 10); + + ir_add_error(ira, source_instr, + buf_sprintf("integer value '%s' cannot be stored in type '%s'", + buf_ptr(int_buf), buf_ptr(&new_type->name))); + return false; + } + } + + const_val->special = ConstValSpecialStatic; + break; + case CastOpBoolToInt: + bigint_init_unsigned(&const_val->data.x_bigint, other_val->data.x_bool ? 1 : 0); + const_val->special = ConstValSpecialStatic; + break; + } + return true; +} + +static IrInstGen *ir_const(IrAnalyze *ira, IrInst *inst, ZigType *ty) { + IrInstGenConst *const_instruction = ir_create_inst_gen(&ira->new_irb, + inst->scope, inst->source_node); + IrInstGen *new_instruction = &const_instruction->base; + new_instruction->value->type = ty; + new_instruction->value->special = ConstValSpecialStatic; + ira->new_irb.constants.append(&heap::c_allocator, const_instruction); + return new_instruction; +} + +static IrInstGen *ir_const_noval(IrAnalyze *ira, IrInst *old_instruction) { + IrInstGenConst *const_instruction = ir_create_inst_noval(&ira->new_irb, + old_instruction->scope, old_instruction->source_node); + ira->new_irb.constants.append(&heap::c_allocator, const_instruction); + return &const_instruction->base; +} + +// This function initializes the new IrInstGen with the provided ZigValue, +// rather than creating a new one. +static IrInstGen *ir_const_move(IrAnalyze *ira, IrInst *old_instruction, ZigValue *val) { + IrInstGen *result = ir_const_noval(ira, old_instruction); + result->value = val; + return result; +} + +static IrInstGen *ir_resolve_cast(IrAnalyze *ira, IrInst *source_instr, IrInstGen *value, + ZigType *wanted_type, CastOp cast_op) +{ + if (instr_is_comptime(value) || !type_has_bits(ira->codegen, wanted_type)) { + IrInstGen *result = ir_const(ira, source_instr, wanted_type); + ZigValue *val = ir_resolve_const(ira, value, UndefBad); + if (val == nullptr) + return ira->codegen->invalid_inst_gen; + + if (!eval_const_expr_implicit_cast(ira, source_instr, cast_op, val, val->type, + result->value, wanted_type)) + { + return ira->codegen->invalid_inst_gen; + } + return result; + } else { + return ir_build_cast(ira, source_instr, wanted_type, value, cast_op); + } +} + +static IrInstGen *ir_resolve_ptr_of_array_to_unknown_len_ptr(IrAnalyze *ira, IrInst* source_instr, + IrInstGen *value, ZigType *wanted_type) +{ + ir_assert(value->value->type->id == ZigTypeIdPointer, source_instr); + + Error err; + + if ((err = type_resolve(ira->codegen, value->value->type->data.pointer.child_type, + ResolveStatusAlignmentKnown))) + { + return ira->codegen->invalid_inst_gen; + } + + wanted_type = adjust_ptr_align(ira->codegen, wanted_type, get_ptr_align(ira->codegen, value->value->type)); + + if (instr_is_comptime(value)) { + ZigValue *val = ir_resolve_const(ira, value, UndefOk); + if (val == nullptr) + return ira->codegen->invalid_inst_gen; + if (val->special == ConstValSpecialUndef) + return ir_const_undef(ira, source_instr, wanted_type); + + ZigValue *pointee = const_ptr_pointee(ira, ira->codegen, val, source_instr->source_node); + if (pointee == nullptr) + return ira->codegen->invalid_inst_gen; + if (pointee->special != ConstValSpecialRuntime) { + IrInstGen *result = ir_const(ira, source_instr, wanted_type); + result->value->data.x_ptr.special = ConstPtrSpecialBaseArray; + result->value->data.x_ptr.mut = val->data.x_ptr.mut; + result->value->data.x_ptr.data.base_array.array_val = pointee; + result->value->data.x_ptr.data.base_array.elem_index = 0; + return result; + } + } + + return ir_build_cast(ira, source_instr, wanted_type, value, CastOpBitCast); +} + +static IrInstGen *ir_resolve_ptr_of_array_to_slice(IrAnalyze *ira, IrInst* source_instr, + IrInstGen *array_ptr, ZigType *wanted_type, ResultLoc *result_loc) +{ + Error err; + + assert(array_ptr->value->type->id == ZigTypeIdPointer); + assert(array_ptr->value->type->data.pointer.child_type->id == ZigTypeIdArray); + + ZigType *array_type = array_ptr->value->type->data.pointer.child_type; + size_t array_len = array_type->data.array.len; + + // A zero-sized array can be casted regardless of the destination alignment, or + // whether the pointer is undefined, and the result is always comptime known. + // TODO However, this is exposing a result location bug that I failed to solve on the first try. + // If you want to try to fix the bug, uncomment this block and get the tests passing. + //if (array_len == 0 && array_type->data.array.sentinel == nullptr) { + // ZigValue *undef_array = ira->codegen->pass1_arena->create(); + // undef_array->special = ConstValSpecialUndef; + // undef_array->type = array_type; + + // IrInstGen *result = ir_const(ira, source_instr, wanted_type); + // init_const_slice(ira->codegen, result->value, undef_array, 0, 0, false); + // result->value->data.x_struct.fields[slice_ptr_index]->data.x_ptr.mut = ConstPtrMutComptimeConst; + // result->value->type = wanted_type; + // return result; + //} + + if ((err = type_resolve(ira->codegen, array_ptr->value->type, ResolveStatusAlignmentKnown))) { + return ira->codegen->invalid_inst_gen; + } + + if (array_len != 0) { + wanted_type = adjust_slice_align(ira->codegen, wanted_type, + get_ptr_align(ira->codegen, array_ptr->value->type)); + } + + if (instr_is_comptime(array_ptr)) { + UndefAllowed undef_allowed = (array_len == 0) ? UndefOk : UndefBad; + ZigValue *array_ptr_val = ir_resolve_const(ira, array_ptr, undef_allowed); + if (array_ptr_val == nullptr) + return ira->codegen->invalid_inst_gen; + ir_assert(is_slice(wanted_type), source_instr); + if (array_ptr_val->special == ConstValSpecialUndef) { + ZigValue *undef_array = ira->codegen->pass1_arena->create(); + undef_array->special = ConstValSpecialUndef; + undef_array->type = array_type; + + IrInstGen *result = ir_const(ira, source_instr, wanted_type); + init_const_slice(ira->codegen, result->value, undef_array, 0, 0, false); + result->value->data.x_struct.fields[slice_ptr_index]->data.x_ptr.mut = ConstPtrMutComptimeConst; + result->value->type = wanted_type; + return result; + } + bool wanted_const = wanted_type->data.structure.fields[slice_ptr_index]->type_entry->data.pointer.is_const; + // Optimization to avoid creating unnecessary ZigValue in const_ptr_pointee + if (array_ptr_val->data.x_ptr.special == ConstPtrSpecialSubArray) { + ZigValue *array_val = array_ptr_val->data.x_ptr.data.base_array.array_val; + if (array_val->special != ConstValSpecialRuntime) { + IrInstGen *result = ir_const(ira, source_instr, wanted_type); + init_const_slice(ira->codegen, result->value, array_val, + array_ptr_val->data.x_ptr.data.base_array.elem_index, + array_type->data.array.len, wanted_const); + result->value->data.x_struct.fields[slice_ptr_index]->data.x_ptr.mut = array_ptr_val->data.x_ptr.mut; + result->value->type = wanted_type; + return result; + } + } else if (array_ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { + ZigValue *pointee = const_ptr_pointee(ira, ira->codegen, array_ptr_val, source_instr->source_node); + if (pointee == nullptr) + return ira->codegen->invalid_inst_gen; + if (pointee->special != ConstValSpecialRuntime) { + assert(array_ptr_val->type->id == ZigTypeIdPointer); + + IrInstGen *result = ir_const(ira, source_instr, wanted_type); + init_const_slice(ira->codegen, result->value, pointee, 0, array_type->data.array.len, wanted_const); + result->value->data.x_struct.fields[slice_ptr_index]->data.x_ptr.mut = array_ptr_val->data.x_ptr.mut; + result->value->type = wanted_type; + return result; + } + } + } + + if (result_loc == nullptr) result_loc = no_result_loc(); + IrInstGen *result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, wanted_type, nullptr, true, true); + if (type_is_invalid(result_loc_inst->value->type) || + result_loc_inst->value->type->id == ZigTypeIdUnreachable) + { + return result_loc_inst; + } + return ir_build_ptr_of_array_to_slice(ira, source_instr, wanted_type, array_ptr, result_loc_inst); +} + +static IrBasicBlockGen *ir_get_new_bb(IrAnalyze *ira, IrBasicBlockSrc *old_bb, IrInst *ref_old_instruction) { + assert(old_bb); + + if (old_bb->child) { + if (ref_old_instruction == nullptr || old_bb->child->ref_instruction != ref_old_instruction) { + return old_bb->child; + } + } + + IrBasicBlockGen *new_bb = ir_build_bb_from(ira, old_bb); + new_bb->ref_instruction = ref_old_instruction; + + return new_bb; +} + +static IrBasicBlockGen *ir_get_new_bb_runtime(IrAnalyze *ira, IrBasicBlockSrc *old_bb, IrInst *ref_old_instruction) { + assert(ref_old_instruction != nullptr); + IrBasicBlockGen *new_bb = ir_get_new_bb(ira, old_bb, ref_old_instruction); + if (new_bb->must_be_comptime_source_instr) { + ErrorMsg *msg = ir_add_error(ira, ref_old_instruction, + buf_sprintf("control flow attempts to use compile-time variable at runtime")); + add_error_note(ira->codegen, msg, new_bb->must_be_comptime_source_instr->source_node, + buf_sprintf("compile-time variable assigned here")); + return nullptr; + } + return new_bb; +} + +static void ir_start_bb(IrAnalyze *ira, IrBasicBlockSrc *old_bb, IrBasicBlockSrc *const_predecessor_bb) { + ir_assert(!old_bb->suspended, (old_bb->instruction_list.length != 0) ? &old_bb->instruction_list.at(0)->base : nullptr); + ira->instruction_index = 0; + ira->old_irb.current_basic_block = old_bb; + ira->const_predecessor_bb = const_predecessor_bb; + ira->old_bb_index = old_bb->index; +} + +static IrInstGen *ira_suspend(IrAnalyze *ira, IrInst *old_instruction, IrBasicBlockSrc *next_bb, + IrSuspendPosition *suspend_pos) +{ + if (ira->codegen->verbose_ir) { + fprintf(stderr, "suspend %s_%" PRIu32 " %s_%" PRIu32 " #%" PRIu32 " (%zu,%zu)\n", + ira->old_irb.current_basic_block->name_hint, + ira->old_irb.current_basic_block->debug_id, + ira->old_irb.exec->basic_block_list.at(ira->old_bb_index)->name_hint, + ira->old_irb.exec->basic_block_list.at(ira->old_bb_index)->debug_id, + ira->old_irb.current_basic_block->instruction_list.at(ira->instruction_index)->base.debug_id, + ira->old_bb_index, ira->instruction_index); + } + suspend_pos->basic_block_index = ira->old_bb_index; + suspend_pos->instruction_index = ira->instruction_index; + + ira->old_irb.current_basic_block->suspended = true; + + // null next_bb means that the caller plans to call ira_resume before returning + if (next_bb != nullptr) { + ira->old_bb_index = next_bb->index; + ira->old_irb.current_basic_block = ira->old_irb.exec->basic_block_list.at(ira->old_bb_index); + assert(ira->old_irb.current_basic_block == next_bb); + ira->instruction_index = 0; + ira->const_predecessor_bb = nullptr; + next_bb->child = ir_get_new_bb_runtime(ira, next_bb, old_instruction); + ira->new_irb.current_basic_block = next_bb->child; + } + return ira->codegen->unreach_instruction; +} + +static IrInstGen *ira_resume(IrAnalyze *ira) { + IrSuspendPosition pos = ira->resume_stack.pop(); + if (ira->codegen->verbose_ir) { + fprintf(stderr, "resume (%zu,%zu) ", pos.basic_block_index, pos.instruction_index); + } + ira->old_bb_index = pos.basic_block_index; + ira->old_irb.current_basic_block = ira->old_irb.exec->basic_block_list.at(ira->old_bb_index); + assert(ira->old_irb.current_basic_block->in_resume_stack); + ira->old_irb.current_basic_block->in_resume_stack = false; + ira->old_irb.current_basic_block->suspended = false; + ira->instruction_index = pos.instruction_index; + assert(pos.instruction_index < ira->old_irb.current_basic_block->instruction_list.length); + if (ira->codegen->verbose_ir) { + fprintf(stderr, "%s_%" PRIu32 " #%" PRIu32 "\n", ira->old_irb.current_basic_block->name_hint, + ira->old_irb.current_basic_block->debug_id, + ira->old_irb.current_basic_block->instruction_list.at(pos.instruction_index)->base.debug_id); + } + ira->const_predecessor_bb = nullptr; + ira->new_irb.current_basic_block = ira->old_irb.current_basic_block->child; + assert(ira->new_irb.current_basic_block != nullptr); + return ira->codegen->unreach_instruction; +} + +static void ir_start_next_bb(IrAnalyze *ira) { + ira->old_bb_index += 1; + + bool need_repeat = true; + for (;;) { + while (ira->old_bb_index < ira->old_irb.exec->basic_block_list.length) { + IrBasicBlockSrc *old_bb = ira->old_irb.exec->basic_block_list.at(ira->old_bb_index); + if (old_bb->child == nullptr && old_bb->suspend_instruction_ref == nullptr) { + ira->old_bb_index += 1; + continue; + } + // if it's already started, or + // if it's a suspended block, + // then skip it + if (old_bb->suspended || + (old_bb->child != nullptr && old_bb->child->instruction_list.length != 0) || + (old_bb->child != nullptr && old_bb->child->already_appended)) + { + ira->old_bb_index += 1; + continue; + } + + // if there is a resume_stack, pop one from there rather than moving on. + // the last item of the resume stack will be a basic block that will + // move on to the next one below + if (ira->resume_stack.length != 0) { + ira_resume(ira); + return; + } + + if (old_bb->child == nullptr) { + old_bb->child = ir_get_new_bb_runtime(ira, old_bb, old_bb->suspend_instruction_ref); + } + ira->new_irb.current_basic_block = old_bb->child; + ir_start_bb(ira, old_bb, nullptr); + return; + } + if (!need_repeat) { + if (ira->resume_stack.length != 0) { + ira_resume(ira); + } + return; + } + need_repeat = false; + ira->old_bb_index = 0; + continue; + } +} + +static void ir_finish_bb(IrAnalyze *ira) { + if (!ira->new_irb.current_basic_block->already_appended) { + ir_append_basic_block_gen(&ira->new_irb, ira->new_irb.current_basic_block); + if (ira->codegen->verbose_ir) { + fprintf(stderr, "append new bb %s_%" PRIu32 "\n", ira->new_irb.current_basic_block->name_hint, + ira->new_irb.current_basic_block->debug_id); + } + } + ira->instruction_index += 1; + while (ira->instruction_index < ira->old_irb.current_basic_block->instruction_list.length) { + IrInstSrc *next_instruction = ira->old_irb.current_basic_block->instruction_list.at(ira->instruction_index); + if (!next_instruction->is_gen) { + ir_add_error(ira, &next_instruction->base, buf_sprintf("unreachable code")); + break; + } + ira->instruction_index += 1; + } + + ir_start_next_bb(ira); +} + +static IrInstGen *ir_unreach_error(IrAnalyze *ira) { + ira->old_bb_index = SIZE_MAX; + if (ira->new_irb.exec->first_err_trace_msg == nullptr) { + ira->new_irb.exec->first_err_trace_msg = ira->codegen->trace_err; + } + return ira->codegen->unreach_instruction; +} + +static bool ir_emit_backward_branch(IrAnalyze *ira, IrInst* source_instruction) { + size_t *bbc = ira->new_irb.exec->backward_branch_count; + size_t *quota = ira->new_irb.exec->backward_branch_quota; + + // If we're already over quota, we've already given an error message for this. + if (*bbc > *quota) { + assert(ira->codegen->errors.length > 0); + return false; + } + + *bbc += 1; + if (*bbc > *quota) { + ir_add_error(ira, source_instruction, + buf_sprintf("evaluation exceeded %" ZIG_PRI_usize " backwards branches", *quota)); + return false; + } + return true; +} + +static IrInstGen *ir_inline_bb(IrAnalyze *ira, IrInst* source_instruction, IrBasicBlockSrc *old_bb) { + if (old_bb->debug_id <= ira->old_irb.current_basic_block->debug_id) { + if (!ir_emit_backward_branch(ira, source_instruction)) + return ir_unreach_error(ira); + } + + old_bb->child = ira->old_irb.current_basic_block->child; + ir_start_bb(ira, old_bb, ira->old_irb.current_basic_block); + return ira->codegen->unreach_instruction; +} + +static IrInstGen *ir_finish_anal(IrAnalyze *ira, IrInstGen *instruction) { + if (instruction->value->type->id == ZigTypeIdUnreachable) + ir_finish_bb(ira); + return instruction; +} + +static IrInstGen *ir_const_fn(IrAnalyze *ira, IrInst *source_instr, ZigFn *fn_entry) { + IrInstGen *result = ir_const(ira, source_instr, fn_entry->type_entry); + result->value->special = ConstValSpecialStatic; + result->value->data.x_ptr.data.fn.fn_entry = fn_entry; + result->value->data.x_ptr.mut = ConstPtrMutComptimeConst; + result->value->data.x_ptr.special = ConstPtrSpecialFunction; + return result; +} + +static IrInstGen *ir_const_bound_fn(IrAnalyze *ira, IrInst *src_inst, ZigFn *fn_entry, IrInstGen *first_arg, + IrInst *first_arg_src) +{ + // This is unfortunately required to avoid improperly freeing first_arg_src + ira_ref(ira); + + IrInstGen *result = ir_const(ira, src_inst, get_bound_fn_type(ira->codegen, fn_entry)); + result->value->data.x_bound_fn.fn = fn_entry; + result->value->data.x_bound_fn.first_arg = first_arg; + result->value->data.x_bound_fn.first_arg_src = first_arg_src; + return result; +} + +static IrInstGen *ir_const_type(IrAnalyze *ira, IrInst *source_instruction, ZigType *ty) { + IrInstGen *result = ir_const(ira, source_instruction, ira->codegen->builtin_types.entry_type); + result->value->data.x_type = ty; + return result; +} + +static IrInstGen *ir_const_bool(IrAnalyze *ira, IrInst *source_instruction, bool value) { + IrInstGen *result = ir_const(ira, source_instruction, ira->codegen->builtin_types.entry_bool); + result->value->data.x_bool = value; + return result; +} + +static IrInstGen *ir_const_undef(IrAnalyze *ira, IrInst *source_instruction, ZigType *ty) { + IrInstGen *result = ir_const(ira, source_instruction, ty); + result->value->special = ConstValSpecialUndef; + return result; +} + +static IrInstGen *ir_const_unreachable(IrAnalyze *ira, IrInst *source_instruction) { + IrInstGen *result = ir_const_noval(ira, source_instruction); + result->value = ira->codegen->intern.for_unreachable(); + return result; +} + +static IrInstGen *ir_const_void(IrAnalyze *ira, IrInst *source_instruction) { + IrInstGen *result = ir_const_noval(ira, source_instruction); + result->value = ira->codegen->intern.for_void(); + return result; +} + +static IrInstGen *ir_const_unsigned(IrAnalyze *ira, IrInst *source_instruction, uint64_t value) { + IrInstGen *result = ir_const(ira, source_instruction, ira->codegen->builtin_types.entry_num_lit_int); + bigint_init_unsigned(&result->value->data.x_bigint, value); + return result; +} + +static IrInstGen *ir_get_const_ptr(IrAnalyze *ira, IrInst *instruction, + ZigValue *pointee, ZigType *pointee_type, + ConstPtrMut ptr_mut, bool ptr_is_const, bool ptr_is_volatile, uint32_t ptr_align) +{ + ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, pointee_type, + ptr_is_const, ptr_is_volatile, PtrLenSingle, ptr_align, 0, 0, false); + IrInstGen *const_instr = ir_const(ira, instruction, ptr_type); + ZigValue *const_val = const_instr->value; + const_val->data.x_ptr.special = ConstPtrSpecialRef; + const_val->data.x_ptr.mut = ptr_mut; + const_val->data.x_ptr.data.ref.pointee = pointee; + return const_instr; +} + +static Error ir_resolve_const_val(CodeGen *codegen, IrExecutableGen *exec, AstNode *source_node, + ZigValue *val, UndefAllowed undef_allowed) +{ + Error err; + for (;;) { + switch (val->special) { + case ConstValSpecialStatic: + return ErrorNone; + case ConstValSpecialRuntime: + if (!type_has_bits(codegen, val->type)) + return ErrorNone; + + exec_add_error_node_gen(codegen, exec, source_node, + buf_sprintf("unable to evaluate constant expression")); + return ErrorSemanticAnalyzeFail; + case ConstValSpecialUndef: + if (undef_allowed == UndefOk || undef_allowed == LazyOk) + return ErrorNone; + + exec_add_error_node_gen(codegen, exec, source_node, + buf_sprintf("use of undefined value here causes undefined behavior")); + return ErrorSemanticAnalyzeFail; + case ConstValSpecialLazy: + if (undef_allowed == LazyOk || undef_allowed == LazyOkNoUndef) + return ErrorNone; + + if ((err = ir_resolve_lazy(codegen, source_node, val))) + return err; + + continue; + } + } +} + +static ZigValue *ir_resolve_const(IrAnalyze *ira, IrInstGen *value, UndefAllowed undef_allowed) { + Error err; + if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, value->base.source_node, + value->value, undef_allowed))) + { + return nullptr; + } + return value->value; +} + +Error ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node, + ZigValue *return_ptr, size_t *backward_branch_count, size_t *backward_branch_quota, + ZigFn *fn_entry, Buf *c_import_buf, AstNode *source_node, Buf *exec_name, + IrExecutableGen *parent_exec, AstNode *expected_type_source_node, UndefAllowed undef_allowed) +{ + Error err; + + src_assert(return_ptr->type->id == ZigTypeIdPointer, source_node); + + if (type_is_invalid(return_ptr->type)) + return ErrorSemanticAnalyzeFail; + + IrExecutableSrc *ir_executable = heap::c_allocator.create(); + ir_executable->source_node = source_node; + ir_executable->parent_exec = parent_exec; + ir_executable->name = exec_name; + ir_executable->is_inline = true; + ir_executable->fn_entry = fn_entry; + ir_executable->c_import_buf = c_import_buf; + ir_executable->begin_scope = scope; + + if (!ir_gen(codegen, node, scope, ir_executable)) + return ErrorSemanticAnalyzeFail; + + if (ir_executable->first_err_trace_msg != nullptr) { + codegen->trace_err = ir_executable->first_err_trace_msg; + return ErrorSemanticAnalyzeFail; + } + + if (codegen->verbose_ir) { + fprintf(stderr, "\nSource: "); + ast_render(stderr, node, 4); + fprintf(stderr, "\n{ // (IR)\n"); + ir_print_src(codegen, stderr, ir_executable, 2); + fprintf(stderr, "}\n"); + } + IrExecutableGen *analyzed_executable = heap::c_allocator.create(); + analyzed_executable->source_node = source_node; + analyzed_executable->parent_exec = parent_exec; + analyzed_executable->source_exec = ir_executable; + analyzed_executable->name = exec_name; + analyzed_executable->is_inline = true; + analyzed_executable->fn_entry = fn_entry; + analyzed_executable->c_import_buf = c_import_buf; + analyzed_executable->backward_branch_count = backward_branch_count; + analyzed_executable->backward_branch_quota = backward_branch_quota; + analyzed_executable->begin_scope = scope; + ZigType *result_type = ir_analyze(codegen, ir_executable, analyzed_executable, + return_ptr->type->data.pointer.child_type, expected_type_source_node, return_ptr); + if (type_is_invalid(result_type)) { + return ErrorSemanticAnalyzeFail; + } + + if (codegen->verbose_ir) { + fprintf(stderr, "{ // (analyzed)\n"); + ir_print_gen(codegen, stderr, analyzed_executable, 2); + fprintf(stderr, "}\n"); + } + + if ((err = ir_exec_scan_for_side_effects(codegen, analyzed_executable))) + return err; + + ZigValue *result = const_ptr_pointee(nullptr, codegen, return_ptr, source_node); + if (result == nullptr) + return ErrorSemanticAnalyzeFail; + if ((err = ir_resolve_const_val(codegen, analyzed_executable, node, result, undef_allowed))) + return err; + + return ErrorNone; +} + +static ErrorTableEntry *ir_resolve_error(IrAnalyze *ira, IrInstGen *err_value) { + if (type_is_invalid(err_value->value->type)) + return nullptr; + + if (err_value->value->type->id != ZigTypeIdErrorSet) { + ir_add_error_node(ira, err_value->base.source_node, + buf_sprintf("expected error, found '%s'", buf_ptr(&err_value->value->type->name))); + return nullptr; + } + + ZigValue *const_val = ir_resolve_const(ira, err_value, UndefBad); + if (!const_val) + return nullptr; + + assert(const_val->data.x_err_set != nullptr); + return const_val->data.x_err_set; +} + +static ZigType *ir_resolve_const_type(CodeGen *codegen, IrExecutableGen *exec, AstNode *source_node, + ZigValue *val) +{ + Error err; + if ((err = ir_resolve_const_val(codegen, exec, source_node, val, UndefBad))) + return codegen->builtin_types.entry_invalid; + + assert(val->data.x_type != nullptr); + return val->data.x_type; +} + +static ZigValue *ir_resolve_type_lazy(IrAnalyze *ira, IrInstGen *type_value) { + if (type_is_invalid(type_value->value->type)) + return nullptr; + + if (type_value->value->type->id != ZigTypeIdMetaType) { + ir_add_error_node(ira, type_value->base.source_node, + buf_sprintf("expected type 'type', found '%s'", buf_ptr(&type_value->value->type->name))); + return nullptr; + } + + Error err; + if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, type_value->base.source_node, + type_value->value, LazyOk))) + { + return nullptr; + } + + return type_value->value; +} + +static ZigType *ir_resolve_type(IrAnalyze *ira, IrInstGen *type_value) { + ZigValue *val = ir_resolve_type_lazy(ira, type_value); + if (val == nullptr) + return ira->codegen->builtin_types.entry_invalid; + + return ir_resolve_const_type(ira->codegen, ira->new_irb.exec, type_value->base.source_node, val); +} + +static Error ir_validate_vector_elem_type(IrAnalyze *ira, AstNode *source_node, ZigType *elem_type) { + Error err; + bool is_valid; + if ((err = is_valid_vector_elem_type(ira->codegen, elem_type, &is_valid))) + return err; + if (!is_valid) { + ir_add_error_node(ira, source_node, + buf_sprintf("vector element type must be integer, float, bool, or pointer; '%s' is invalid", + buf_ptr(&elem_type->name))); + return ErrorSemanticAnalyzeFail; + } + return ErrorNone; +} + +static ZigType *ir_resolve_vector_elem_type(IrAnalyze *ira, IrInstGen *elem_type_value) { + Error err; + ZigType *elem_type = ir_resolve_type(ira, elem_type_value); + if (type_is_invalid(elem_type)) + return ira->codegen->builtin_types.entry_invalid; + if ((err = ir_validate_vector_elem_type(ira, elem_type_value->base.source_node, elem_type))) + return ira->codegen->builtin_types.entry_invalid; + return elem_type; +} + +static ZigType *ir_resolve_int_type(IrAnalyze *ira, IrInstGen *type_value) { + ZigType *ty = ir_resolve_type(ira, type_value); + if (type_is_invalid(ty)) + return ira->codegen->builtin_types.entry_invalid; + + if (ty->id != ZigTypeIdInt) { + ErrorMsg *msg = ir_add_error_node(ira, type_value->base.source_node, + buf_sprintf("expected integer type, found '%s'", buf_ptr(&ty->name))); + if (ty->id == ZigTypeIdVector && + ty->data.vector.elem_type->id == ZigTypeIdInt) + { + add_error_note(ira->codegen, msg, type_value->base.source_node, + buf_sprintf("represent vectors with their element types, i.e. '%s'", + buf_ptr(&ty->data.vector.elem_type->name))); + } + return ira->codegen->builtin_types.entry_invalid; + } + + return ty; +} + +static ZigType *ir_resolve_error_set_type(IrAnalyze *ira, IrInst *op_source, IrInstGen *type_value) { + if (type_is_invalid(type_value->value->type)) + return ira->codegen->builtin_types.entry_invalid; + + if (type_value->value->type->id != ZigTypeIdMetaType) { + ErrorMsg *msg = ir_add_error_node(ira, type_value->base.source_node, + buf_sprintf("expected error set type, found '%s'", buf_ptr(&type_value->value->type->name))); + add_error_note(ira->codegen, msg, op_source->source_node, + buf_sprintf("`||` merges error sets; `or` performs boolean OR")); + return ira->codegen->builtin_types.entry_invalid; + } + + ZigValue *const_val = ir_resolve_const(ira, type_value, UndefBad); + if (!const_val) + return ira->codegen->builtin_types.entry_invalid; + + assert(const_val->data.x_type != nullptr); + ZigType *result_type = const_val->data.x_type; + if (result_type->id != ZigTypeIdErrorSet) { + ErrorMsg *msg = ir_add_error_node(ira, type_value->base.source_node, + buf_sprintf("expected error set type, found type '%s'", buf_ptr(&result_type->name))); + add_error_note(ira->codegen, msg, op_source->source_node, + buf_sprintf("`||` merges error sets; `or` performs boolean OR")); + return ira->codegen->builtin_types.entry_invalid; + } + return result_type; +} + +static ZigFn *ir_resolve_fn(IrAnalyze *ira, IrInstGen *fn_value) { + if (type_is_invalid(fn_value->value->type)) + return nullptr; + + if (fn_value->value->type->id != ZigTypeIdFn) { + ir_add_error_node(ira, fn_value->base.source_node, + buf_sprintf("expected function type, found '%s'", buf_ptr(&fn_value->value->type->name))); + return nullptr; + } + + ZigValue *const_val = ir_resolve_const(ira, fn_value, UndefBad); + if (!const_val) + return nullptr; + + // May be a ConstPtrSpecialHardCodedAddr + if (const_val->data.x_ptr.special != ConstPtrSpecialFunction) + return nullptr; + + return const_val->data.x_ptr.data.fn.fn_entry; +} + +static IrInstGen *ir_analyze_optional_wrap(IrAnalyze *ira, IrInst* source_instr, + IrInstGen *value, ZigType *wanted_type, ResultLoc *result_loc) +{ + assert(wanted_type->id == ZigTypeIdOptional); + + if (instr_is_comptime(value)) { + ZigType *payload_type = wanted_type->data.maybe.child_type; + IrInstGen *casted_payload = ir_implicit_cast(ira, value, payload_type); + if (type_is_invalid(casted_payload->value->type)) + return ira->codegen->invalid_inst_gen; + + ZigValue *val = ir_resolve_const(ira, casted_payload, UndefOk); + if (!val) + return ira->codegen->invalid_inst_gen; + + IrInstGenConst *const_instruction = ir_create_inst_gen(&ira->new_irb, + source_instr->scope, source_instr->source_node); + const_instruction->base.value->special = ConstValSpecialStatic; + if (types_have_same_zig_comptime_repr(ira->codegen, wanted_type, payload_type)) { + copy_const_val(ira->codegen, const_instruction->base.value, val); + } else { + const_instruction->base.value->data.x_optional = val; + } + const_instruction->base.value->type = wanted_type; + return &const_instruction->base; + } + + if (result_loc == nullptr && handle_is_ptr(ira->codegen, wanted_type)) { + result_loc = no_result_loc(); + } + IrInstGen *result_loc_inst = nullptr; + if (result_loc != nullptr) { + result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, wanted_type, nullptr, true, true); + if (type_is_invalid(result_loc_inst->value->type) || + result_loc_inst->value->type->id == ZigTypeIdUnreachable) + { + return result_loc_inst; + } + } + IrInstGen *result = ir_build_optional_wrap(ira, source_instr, wanted_type, value, result_loc_inst); + result->value->data.rh_maybe = RuntimeHintOptionalNonNull; + return result; +} + +static IrInstGen *ir_analyze_err_wrap_payload(IrAnalyze *ira, IrInst* source_instr, + IrInstGen *value, ZigType *wanted_type, ResultLoc *result_loc) +{ + assert(wanted_type->id == ZigTypeIdErrorUnion); + + ZigType *payload_type = wanted_type->data.error_union.payload_type; + ZigType *err_set_type = wanted_type->data.error_union.err_set_type; + if (instr_is_comptime(value)) { + IrInstGen *casted_payload = ir_implicit_cast(ira, value, payload_type); + if (type_is_invalid(casted_payload->value->type)) + return ira->codegen->invalid_inst_gen; + + ZigValue *val = ir_resolve_const(ira, casted_payload, UndefOk); + if (val == nullptr) + return ira->codegen->invalid_inst_gen; + + ZigValue *err_set_val = ira->codegen->pass1_arena->create(); + err_set_val->type = err_set_type; + err_set_val->special = ConstValSpecialStatic; + err_set_val->data.x_err_set = nullptr; + + IrInstGenConst *const_instruction = ir_create_inst_gen(&ira->new_irb, + source_instr->scope, source_instr->source_node); + const_instruction->base.value->type = wanted_type; + const_instruction->base.value->special = ConstValSpecialStatic; + const_instruction->base.value->data.x_err_union.error_set = err_set_val; + const_instruction->base.value->data.x_err_union.payload = val; + return &const_instruction->base; + } + + IrInstGen *result_loc_inst; + if (handle_is_ptr(ira->codegen, wanted_type)) { + if (result_loc == nullptr) result_loc = no_result_loc(); + result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, wanted_type, nullptr, true, true); + if (type_is_invalid(result_loc_inst->value->type) || + result_loc_inst->value->type->id == ZigTypeIdUnreachable) { + return result_loc_inst; + } + } else { + result_loc_inst = nullptr; + } + + IrInstGen *result = ir_build_err_wrap_payload(ira, source_instr, wanted_type, value, result_loc_inst); + result->value->data.rh_error_union = RuntimeHintErrorUnionNonError; + return result; +} + +static IrInstGen *ir_analyze_err_set_cast(IrAnalyze *ira, IrInst* source_instr, IrInstGen *value, + ZigType *wanted_type) +{ + assert(value->value->type->id == ZigTypeIdErrorSet); + assert(wanted_type->id == ZigTypeIdErrorSet); + + if (instr_is_comptime(value)) { + ZigValue *val = ir_resolve_const(ira, value, UndefBad); + if (!val) + return ira->codegen->invalid_inst_gen; + + if (!resolve_inferred_error_set(ira->codegen, wanted_type, source_instr->source_node)) { + return ira->codegen->invalid_inst_gen; + } + if (!type_is_global_error_set(wanted_type)) { + bool subset = false; + for (uint32_t i = 0, count = wanted_type->data.error_set.err_count; i < count; i += 1) { + if (wanted_type->data.error_set.errors[i]->value == val->data.x_err_set->value) { + subset = true; + break; + } + } + if (!subset) { + ir_add_error(ira, source_instr, + buf_sprintf("error.%s not a member of error set '%s'", + buf_ptr(&val->data.x_err_set->name), buf_ptr(&wanted_type->name))); + return ira->codegen->invalid_inst_gen; + } + } + + IrInstGenConst *const_instruction = ir_create_inst_gen(&ira->new_irb, + source_instr->scope, source_instr->source_node); + const_instruction->base.value->type = wanted_type; + const_instruction->base.value->special = ConstValSpecialStatic; + const_instruction->base.value->data.x_err_set = val->data.x_err_set; + return &const_instruction->base; + } + + return ir_build_cast(ira, source_instr, wanted_type, value, CastOpErrSet); +} + +static IrInstGen *ir_analyze_frame_ptr_to_anyframe(IrAnalyze *ira, IrInst* source_instr, + IrInstGen *frame_ptr, ZigType *wanted_type) +{ + if (instr_is_comptime(frame_ptr)) { + ZigValue *ptr_val = ir_resolve_const(ira, frame_ptr, UndefBad); + if (ptr_val == nullptr) + return ira->codegen->invalid_inst_gen; + + ir_assert(ptr_val->type->id == ZigTypeIdPointer, source_instr); + if (ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar) { + zig_panic("TODO comptime frame pointer"); + } + } + + return ir_build_cast(ira, source_instr, wanted_type, frame_ptr, CastOpBitCast); +} + +static IrInstGen *ir_analyze_anyframe_to_anyframe(IrAnalyze *ira, IrInst* source_instr, + IrInstGen *value, ZigType *wanted_type) +{ + if (instr_is_comptime(value)) { + zig_panic("TODO comptime anyframe->T to anyframe"); + } + + return ir_build_cast(ira, source_instr, wanted_type, value, CastOpBitCast); +} + + +static IrInstGen *ir_analyze_err_wrap_code(IrAnalyze *ira, IrInst* source_instr, IrInstGen *value, + ZigType *wanted_type, ResultLoc *result_loc) +{ + assert(wanted_type->id == ZigTypeIdErrorUnion); + + IrInstGen *casted_value = ir_implicit_cast(ira, value, wanted_type->data.error_union.err_set_type); + + if (instr_is_comptime(casted_value)) { + ZigValue *val = ir_resolve_const(ira, casted_value, UndefBad); + if (!val) + return ira->codegen->invalid_inst_gen; + + ZigValue *err_set_val = ira->codegen->pass1_arena->create(); + err_set_val->special = ConstValSpecialStatic; + err_set_val->type = wanted_type->data.error_union.err_set_type; + err_set_val->data.x_err_set = val->data.x_err_set; + + IrInstGenConst *const_instruction = ir_create_inst_gen(&ira->new_irb, + source_instr->scope, source_instr->source_node); + const_instruction->base.value->type = wanted_type; + const_instruction->base.value->special = ConstValSpecialStatic; + const_instruction->base.value->data.x_err_union.error_set = err_set_val; + const_instruction->base.value->data.x_err_union.payload = nullptr; + return &const_instruction->base; + } + + IrInstGen *result_loc_inst; + if (handle_is_ptr(ira->codegen, wanted_type)) { + if (result_loc == nullptr) result_loc = no_result_loc(); + result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, wanted_type, nullptr, true, true); + if (type_is_invalid(result_loc_inst->value->type) || + result_loc_inst->value->type->id == ZigTypeIdUnreachable) + { + return result_loc_inst; + } + } else { + result_loc_inst = nullptr; + } + + + IrInstGen *result = ir_build_err_wrap_code(ira, source_instr, wanted_type, value, result_loc_inst); + result->value->data.rh_error_union = RuntimeHintErrorUnionError; + return result; +} + +static IrInstGen *ir_analyze_null_to_maybe(IrAnalyze *ira, IrInst *source_instr, IrInstGen *value, ZigType *wanted_type) { + assert(wanted_type->id == ZigTypeIdOptional); + assert(instr_is_comptime(value)); + + ZigValue *val = ir_resolve_const(ira, value, UndefBad); + assert(val != nullptr); + + IrInstGen *result = ir_const(ira, source_instr, wanted_type); + result->value->special = ConstValSpecialStatic; + + if (get_src_ptr_type(wanted_type) != nullptr) { + result->value->data.x_ptr.special = ConstPtrSpecialNull; + } else if (is_opt_err_set(wanted_type)) { + result->value->data.x_err_set = nullptr; + } else { + result->value->data.x_optional = nullptr; + } + return result; +} + +static IrInstGen *ir_analyze_null_to_c_pointer(IrAnalyze *ira, IrInst *source_instr, + IrInstGen *value, ZigType *wanted_type) +{ + assert(wanted_type->id == ZigTypeIdPointer); + assert(wanted_type->data.pointer.ptr_len == PtrLenC); + assert(instr_is_comptime(value)); + + ZigValue *val = ir_resolve_const(ira, value, UndefBad); + assert(val != nullptr); + + IrInstGen *result = ir_const(ira, source_instr, wanted_type); + result->value->data.x_ptr.special = ConstPtrSpecialNull; + result->value->data.x_ptr.mut = ConstPtrMutComptimeConst; + return result; +} + +static IrInstGen *ir_get_ref2(IrAnalyze *ira, IrInst* source_instruction, IrInstGen *value, + ZigType *elem_type, bool is_const, bool is_volatile) +{ + Error err; + + if (type_is_invalid(elem_type)) + return ira->codegen->invalid_inst_gen; + + if (instr_is_comptime(value)) { + ZigValue *val = ir_resolve_const(ira, value, LazyOk); + if (!val) + return ira->codegen->invalid_inst_gen; + return ir_get_const_ptr(ira, source_instruction, val, elem_type, + ConstPtrMutComptimeConst, is_const, is_volatile, 0); + } + + ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, elem_type, + is_const, is_volatile, PtrLenSingle, 0, 0, 0, false); + + if ((err = type_resolve(ira->codegen, ptr_type, ResolveStatusZeroBitsKnown))) + return ira->codegen->invalid_inst_gen; + + IrInstGen *result_loc; + if (type_has_bits(ira->codegen, ptr_type) && !handle_is_ptr(ira->codegen, elem_type)) { + result_loc = ir_resolve_result(ira, source_instruction, no_result_loc(), elem_type, nullptr, true, true); + } else { + result_loc = nullptr; + } + + IrInstGen *new_instruction = ir_build_ref_gen(ira, source_instruction, ptr_type, value, result_loc); + new_instruction->value->data.rh_ptr = RuntimeHintPtrStack; + return new_instruction; +} + +static IrInstGen *ir_get_ref(IrAnalyze *ira, IrInst* source_instruction, IrInstGen *value, + bool is_const, bool is_volatile) +{ + return ir_get_ref2(ira, source_instruction, value, value->value->type, is_const, is_volatile); +} + +static ZigType *ir_resolve_union_tag_type(IrAnalyze *ira, AstNode *source_node, ZigType *union_type) { + assert(union_type->id == ZigTypeIdUnion); + + Error err; + if ((err = type_resolve(ira->codegen, union_type, ResolveStatusSizeKnown))) + return ira->codegen->builtin_types.entry_invalid; + + AstNode *decl_node = union_type->data.unionation.decl_node; + if (decl_node->data.container_decl.auto_enum || decl_node->data.container_decl.init_arg_expr != nullptr) { + assert(union_type->data.unionation.tag_type != nullptr); + return union_type->data.unionation.tag_type; + } else { + ErrorMsg *msg = ir_add_error_node(ira, source_node, buf_sprintf("union '%s' has no tag", + buf_ptr(&union_type->name))); + add_error_note(ira->codegen, msg, decl_node, buf_sprintf("consider 'union(enum)' here")); + return ira->codegen->builtin_types.entry_invalid; + } +} + +static IrInstGen *ir_analyze_enum_to_int(IrAnalyze *ira, IrInst *source_instr, IrInstGen *target) { + Error err; + + IrInstGen *enum_target; + ZigType *enum_type; + if (target->value->type->id == ZigTypeIdUnion) { + enum_type = ir_resolve_union_tag_type(ira, target->base.source_node, target->value->type); + if (type_is_invalid(enum_type)) + return ira->codegen->invalid_inst_gen; + enum_target = ir_implicit_cast(ira, target, enum_type); + if (type_is_invalid(enum_target->value->type)) + return ira->codegen->invalid_inst_gen; + } else if (target->value->type->id == ZigTypeIdEnum) { + enum_target = target; + enum_type = target->value->type; + } else { + ir_add_error_node(ira, target->base.source_node, + buf_sprintf("expected enum, found type '%s'", buf_ptr(&target->value->type->name))); + return ira->codegen->invalid_inst_gen; + } + + if ((err = type_resolve(ira->codegen, enum_type, ResolveStatusSizeKnown))) + return ira->codegen->invalid_inst_gen; + + ZigType *tag_type = enum_type->data.enumeration.tag_int_type; + assert(tag_type->id == ZigTypeIdInt || tag_type->id == ZigTypeIdComptimeInt); + + // If there is only one possible tag, then we know at comptime what it is. + if (enum_type->data.enumeration.layout == ContainerLayoutAuto && + enum_type->data.enumeration.src_field_count == 1 && + !enum_type->data.enumeration.non_exhaustive) + { + IrInstGen *result = ir_const(ira, source_instr, tag_type); + init_const_bigint(result->value, tag_type, + &enum_type->data.enumeration.fields[0].value); + return result; + } + + if (instr_is_comptime(enum_target)) { + ZigValue *val = ir_resolve_const(ira, enum_target, UndefBad); + if (!val) + return ira->codegen->invalid_inst_gen; + IrInstGen *result = ir_const(ira, source_instr, tag_type); + init_const_bigint(result->value, tag_type, &val->data.x_enum_tag); + return result; + } + + return ir_build_widen_or_shorten(ira, source_instr->scope, source_instr->source_node, enum_target, tag_type); +} + +static IrInstGen *ir_analyze_union_to_tag(IrAnalyze *ira, IrInst* source_instr, + IrInstGen *target, ZigType *wanted_type) +{ + assert(target->value->type->id == ZigTypeIdUnion); + assert(wanted_type->id == ZigTypeIdEnum); + assert(wanted_type == target->value->type->data.unionation.tag_type); + + if (instr_is_comptime(target)) { + ZigValue *val = ir_resolve_const(ira, target, UndefBad); + if (!val) + return ira->codegen->invalid_inst_gen; + IrInstGen *result = ir_const(ira, source_instr, wanted_type); + result->value->special = ConstValSpecialStatic; + result->value->type = wanted_type; + bigint_init_bigint(&result->value->data.x_enum_tag, &val->data.x_union.tag); + return result; + } + + // If there is only 1 possible tag, then we know at comptime what it is. + if (wanted_type->data.enumeration.layout == ContainerLayoutAuto && + wanted_type->data.enumeration.src_field_count == 1 && + !wanted_type->data.enumeration.non_exhaustive) + { + IrInstGen *result = ir_const(ira, source_instr, wanted_type); + result->value->special = ConstValSpecialStatic; + result->value->type = wanted_type; + TypeEnumField *enum_field = target->value->type->data.unionation.fields[0].enum_field; + bigint_init_bigint(&result->value->data.x_enum_tag, &enum_field->value); + return result; + } + + return ir_build_union_tag(ira, source_instr, target, wanted_type); +} + +static IrInstGen *ir_analyze_undefined_to_anything(IrAnalyze *ira, IrInst* source_instr, + IrInstGen *target, ZigType *wanted_type) +{ + IrInstGen *result = ir_const(ira, source_instr, wanted_type); + result->value->special = ConstValSpecialUndef; + return result; +} + +static IrInstGen *ir_analyze_enum_to_union(IrAnalyze *ira, IrInst* source_instr, + IrInstGen *uncasted_target, ZigType *wanted_type) +{ + Error err; + assert(wanted_type->id == ZigTypeIdUnion); + + if ((err = type_resolve(ira->codegen, wanted_type, ResolveStatusZeroBitsKnown))) + return ira->codegen->invalid_inst_gen; + + IrInstGen *target = ir_implicit_cast(ira, uncasted_target, wanted_type->data.unionation.tag_type); + if (type_is_invalid(target->value->type)) + return ira->codegen->invalid_inst_gen; + + if (instr_is_comptime(target)) { + ZigValue *val = ir_resolve_const(ira, target, UndefBad); + if (!val) + return ira->codegen->invalid_inst_gen; + TypeUnionField *union_field = find_union_field_by_tag(wanted_type, &val->data.x_enum_tag); + if (union_field == nullptr) { + Buf *int_buf = buf_alloc(); + bigint_append_buf(int_buf, &target->value->data.x_enum_tag, 10); + + ir_add_error(ira, &target->base, + buf_sprintf("no tag by value %s", buf_ptr(int_buf))); + return ira->codegen->invalid_inst_gen; + } + ZigType *field_type = resolve_union_field_type(ira->codegen, union_field); + if (field_type == nullptr) + return ira->codegen->invalid_inst_gen; + if ((err = type_resolve(ira->codegen, field_type, ResolveStatusZeroBitsKnown))) + return ira->codegen->invalid_inst_gen; + + switch (type_has_one_possible_value(ira->codegen, field_type)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_inst_gen; + case OnePossibleValueNo: { + AstNode *field_node = wanted_type->data.unionation.decl_node->data.container_decl.fields.at( + union_field->enum_field->decl_index); + ErrorMsg *msg = ir_add_error(ira, source_instr, + buf_sprintf("cast to union '%s' must initialize '%s' field '%s'", + buf_ptr(&wanted_type->name), + buf_ptr(&field_type->name), + buf_ptr(union_field->name))); + add_error_note(ira->codegen, msg, field_node, + buf_sprintf("field '%s' declared here", buf_ptr(union_field->name))); + return ira->codegen->invalid_inst_gen; + } + case OnePossibleValueYes: + break; + } + + IrInstGen *result = ir_const(ira, source_instr, wanted_type); + result->value->special = ConstValSpecialStatic; + result->value->type = wanted_type; + bigint_init_bigint(&result->value->data.x_union.tag, &val->data.x_enum_tag); + result->value->data.x_union.payload = ira->codegen->pass1_arena->create(); + result->value->data.x_union.payload->special = ConstValSpecialStatic; + result->value->data.x_union.payload->type = field_type; + return result; + } + + if (target->value->type->data.enumeration.non_exhaustive) { + ir_add_error(ira, source_instr, + buf_sprintf("runtime cast to union '%s' from non-exhustive enum", + buf_ptr(&wanted_type->name))); + return ira->codegen->invalid_inst_gen; + } + + // if the union has all fields 0 bits, we can do it + // and in fact it's a noop cast because the union value is just the enum value + if (wanted_type->data.unionation.gen_field_count == 0) { + return ir_build_cast(ira, &target->base, wanted_type, target, CastOpNoop); + } + + ErrorMsg *msg = ir_add_error(ira, source_instr, + buf_sprintf("runtime cast to union '%s' which has non-void fields", + buf_ptr(&wanted_type->name))); + for (uint32_t i = 0; i < wanted_type->data.unionation.src_field_count; i += 1) { + TypeUnionField *union_field = &wanted_type->data.unionation.fields[i]; + ZigType *field_type = resolve_union_field_type(ira->codegen, union_field); + if (field_type == nullptr) + return ira->codegen->invalid_inst_gen; + bool has_bits; + if ((err = type_has_bits2(ira->codegen, field_type, &has_bits))) + return ira->codegen->invalid_inst_gen; + if (has_bits) { + AstNode *field_node = wanted_type->data.unionation.decl_node->data.container_decl.fields.at(i); + add_error_note(ira->codegen, msg, field_node, + buf_sprintf("field '%s' has type '%s'", + buf_ptr(union_field->name), + buf_ptr(&field_type->name))); + } + } + return ira->codegen->invalid_inst_gen; +} + +static IrInstGen *ir_analyze_widen_or_shorten(IrAnalyze *ira, IrInst* source_instr, + IrInstGen *target, ZigType *wanted_type) +{ + assert(wanted_type->id == ZigTypeIdInt || wanted_type->id == ZigTypeIdFloat); + + if (instr_is_comptime(target)) { + ZigValue *val = ir_resolve_const(ira, target, UndefBad); + if (!val) + return ira->codegen->invalid_inst_gen; + if (wanted_type->id == ZigTypeIdInt) { + if (bigint_cmp_zero(&val->data.x_bigint) == CmpLT && !wanted_type->data.integral.is_signed) { + ir_add_error(ira, source_instr, + buf_sprintf("attempt to cast negative value to unsigned integer")); + return ira->codegen->invalid_inst_gen; + } + if (!bigint_fits_in_bits(&val->data.x_bigint, wanted_type->data.integral.bit_count, + wanted_type->data.integral.is_signed)) + { + ir_add_error(ira, source_instr, + buf_sprintf("cast from '%s' to '%s' truncates bits", + buf_ptr(&target->value->type->name), buf_ptr(&wanted_type->name))); + return ira->codegen->invalid_inst_gen; + } + } + IrInstGen *result = ir_const(ira, source_instr, wanted_type); + result->value->type = wanted_type; + if (wanted_type->id == ZigTypeIdInt) { + bigint_init_bigint(&result->value->data.x_bigint, &val->data.x_bigint); + } else { + float_init_float(result->value, val); + } + return result; + } + + // If the destination integer type has no bits, then we can emit a comptime + // zero. However, we still want to emit a runtime safety check to make sure + // the target is zero. + if (!type_has_bits(ira->codegen, wanted_type)) { + assert(wanted_type->id == ZigTypeIdInt); + assert(type_has_bits(ira->codegen, target->value->type)); + ir_build_assert_zero(ira, source_instr, target); + IrInstGen *result = ir_const_unsigned(ira, source_instr, 0); + result->value->type = wanted_type; + return result; + } + + return ir_build_widen_or_shorten(ira, source_instr->scope, source_instr->source_node, target, wanted_type); +} + +static IrInstGen *ir_analyze_int_to_enum(IrAnalyze *ira, IrInst* source_instr, + IrInstGen *target, ZigType *wanted_type) +{ + Error err; + assert(wanted_type->id == ZigTypeIdEnum); + + ZigType *actual_type = target->value->type; + + if ((err = type_resolve(ira->codegen, wanted_type, ResolveStatusSizeKnown))) + return ira->codegen->invalid_inst_gen; + + if (actual_type != wanted_type->data.enumeration.tag_int_type) { + ir_add_error(ira, source_instr, + buf_sprintf("integer to enum cast from '%s' instead of its tag type, '%s'", + buf_ptr(&actual_type->name), + buf_ptr(&wanted_type->data.enumeration.tag_int_type->name))); + return ira->codegen->invalid_inst_gen; + } + + assert(actual_type->id == ZigTypeIdInt || actual_type->id == ZigTypeIdComptimeInt); + + if (instr_is_comptime(target)) { + ZigValue *val = ir_resolve_const(ira, target, UndefBad); + if (!val) + return ira->codegen->invalid_inst_gen; + + TypeEnumField *field = find_enum_field_by_tag(wanted_type, &val->data.x_bigint); + if (field == nullptr && !wanted_type->data.enumeration.non_exhaustive) { + Buf *val_buf = buf_alloc(); + bigint_append_buf(val_buf, &val->data.x_bigint, 10); + ErrorMsg *msg = ir_add_error(ira, source_instr, + buf_sprintf("enum '%s' has no tag matching integer value %s", + buf_ptr(&wanted_type->name), buf_ptr(val_buf))); + add_error_note(ira->codegen, msg, wanted_type->data.enumeration.decl_node, + buf_sprintf("'%s' declared here", buf_ptr(&wanted_type->name))); + return ira->codegen->invalid_inst_gen; + } + + IrInstGen *result = ir_const(ira, source_instr, wanted_type); + bigint_init_bigint(&result->value->data.x_enum_tag, &val->data.x_bigint); + return result; + } + + return ir_build_int_to_enum_gen(ira, source_instr->scope, source_instr->source_node, wanted_type, target); +} + +static IrInstGen *ir_analyze_number_to_literal(IrAnalyze *ira, IrInst* source_instr, + IrInstGen *target, ZigType *wanted_type) +{ + ZigValue *val = ir_resolve_const(ira, target, UndefBad); + if (!val) + return ira->codegen->invalid_inst_gen; + + IrInstGen *result = ir_const(ira, source_instr, wanted_type); + if (wanted_type->id == ZigTypeIdComptimeFloat) { + float_init_float(result->value, val); + } else if (wanted_type->id == ZigTypeIdComptimeInt) { + bigint_init_bigint(&result->value->data.x_bigint, &val->data.x_bigint); + } else { + zig_unreachable(); + } + return result; +} + +static IrInstGen *ir_analyze_int_to_err(IrAnalyze *ira, IrInst* source_instr, IrInstGen *target, + ZigType *wanted_type) +{ + assert(target->value->type->id == ZigTypeIdInt); + assert(!target->value->type->data.integral.is_signed); + assert(wanted_type->id == ZigTypeIdErrorSet); + + if (instr_is_comptime(target)) { + ZigValue *val = ir_resolve_const(ira, target, UndefBad); + if (!val) + return ira->codegen->invalid_inst_gen; + + IrInstGen *result = ir_const(ira, source_instr, wanted_type); + + if (!resolve_inferred_error_set(ira->codegen, wanted_type, source_instr->source_node)) { + return ira->codegen->invalid_inst_gen; + } + + if (type_is_global_error_set(wanted_type)) { + BigInt err_count; + bigint_init_unsigned(&err_count, ira->codegen->errors_by_index.length); + + if (bigint_cmp_zero(&val->data.x_bigint) == CmpEQ || bigint_cmp(&val->data.x_bigint, &err_count) != CmpLT) { + Buf *val_buf = buf_alloc(); + bigint_append_buf(val_buf, &val->data.x_bigint, 10); + ir_add_error(ira, source_instr, + buf_sprintf("integer value %s represents no error", buf_ptr(val_buf))); + return ira->codegen->invalid_inst_gen; + } + + size_t index = bigint_as_usize(&val->data.x_bigint); + result->value->data.x_err_set = ira->codegen->errors_by_index.at(index); + return result; + } else { + ErrorTableEntry *err = nullptr; + BigInt err_int; + + for (uint32_t i = 0, count = wanted_type->data.error_set.err_count; i < count; i += 1) { + ErrorTableEntry *this_err = wanted_type->data.error_set.errors[i]; + bigint_init_unsigned(&err_int, this_err->value); + if (bigint_cmp(&val->data.x_bigint, &err_int) == CmpEQ) { + err = this_err; + break; + } + } + + if (err == nullptr) { + Buf *val_buf = buf_alloc(); + bigint_append_buf(val_buf, &val->data.x_bigint, 10); + ir_add_error(ira, source_instr, + buf_sprintf("integer value %s represents no error in '%s'", buf_ptr(val_buf), buf_ptr(&wanted_type->name))); + return ira->codegen->invalid_inst_gen; + } + + result->value->data.x_err_set = err; + return result; + } + } + + return ir_build_int_to_err_gen(ira, source_instr->scope, source_instr->source_node, target, wanted_type); +} + +static IrInstGen *ir_analyze_err_to_int(IrAnalyze *ira, IrInst* source_instr, IrInstGen *target, + ZigType *wanted_type) +{ + assert(wanted_type->id == ZigTypeIdInt); + + ZigType *err_type = target->value->type; + + if (instr_is_comptime(target)) { + ZigValue *val = ir_resolve_const(ira, target, UndefBad); + if (!val) + return ira->codegen->invalid_inst_gen; + + IrInstGen *result = ir_const(ira, source_instr, wanted_type); + + ErrorTableEntry *err; + if (err_type->id == ZigTypeIdErrorUnion) { + err = val->data.x_err_union.error_set->data.x_err_set; + } else if (err_type->id == ZigTypeIdErrorSet) { + err = val->data.x_err_set; + } else { + zig_unreachable(); + } + result->value->type = wanted_type; + uint64_t err_value = err ? err->value : 0; + bigint_init_unsigned(&result->value->data.x_bigint, err_value); + + if (!bigint_fits_in_bits(&result->value->data.x_bigint, + wanted_type->data.integral.bit_count, wanted_type->data.integral.is_signed)) + { + ir_add_error_node(ira, source_instr->source_node, + buf_sprintf("error code '%s' does not fit in '%s'", + buf_ptr(&err->name), buf_ptr(&wanted_type->name))); + return ira->codegen->invalid_inst_gen; + } + + return result; + } + + ZigType *err_set_type; + if (err_type->id == ZigTypeIdErrorUnion) { + err_set_type = err_type->data.error_union.err_set_type; + } else if (err_type->id == ZigTypeIdErrorSet) { + err_set_type = err_type; + } else { + zig_unreachable(); + } + if (!type_is_global_error_set(err_set_type)) { + if (!resolve_inferred_error_set(ira->codegen, err_set_type, source_instr->source_node)) { + return ira->codegen->invalid_inst_gen; + } + if (err_set_type->data.error_set.err_count == 0) { + IrInstGen *result = ir_const(ira, source_instr, wanted_type); + bigint_init_unsigned(&result->value->data.x_bigint, 0); + return result; + } else if (err_set_type->data.error_set.err_count == 1) { + IrInstGen *result = ir_const(ira, source_instr, wanted_type); + ErrorTableEntry *err = err_set_type->data.error_set.errors[0]; + bigint_init_unsigned(&result->value->data.x_bigint, err->value); + return result; + } + } + + BigInt bn; + bigint_init_unsigned(&bn, ira->codegen->errors_by_index.length); + if (!bigint_fits_in_bits(&bn, wanted_type->data.integral.bit_count, wanted_type->data.integral.is_signed)) { + ir_add_error_node(ira, source_instr->source_node, + buf_sprintf("too many error values to fit in '%s'", buf_ptr(&wanted_type->name))); + return ira->codegen->invalid_inst_gen; + } + + return ir_build_err_to_int_gen(ira, source_instr->scope, source_instr->source_node, target, wanted_type); +} + +static IrInstGen *ir_analyze_ptr_to_array(IrAnalyze *ira, IrInst* source_instr, IrInstGen *target, + ZigType *wanted_type) +{ + assert(wanted_type->id == ZigTypeIdPointer); + Error err; + if ((err = type_resolve(ira->codegen, target->value->type->data.pointer.child_type, ResolveStatusAlignmentKnown))) + return ira->codegen->invalid_inst_gen; + assert((wanted_type->data.pointer.is_const && target->value->type->data.pointer.is_const) || !target->value->type->data.pointer.is_const); + wanted_type = adjust_ptr_align(ira->codegen, wanted_type, get_ptr_align(ira->codegen, target->value->type)); + ZigType *array_type = wanted_type->data.pointer.child_type; + assert(array_type->id == ZigTypeIdArray); + assert(array_type->data.array.len == 1); + + if (instr_is_comptime(target)) { + ZigValue *val = ir_resolve_const(ira, target, UndefBad); + if (!val) + return ira->codegen->invalid_inst_gen; + + assert(val->type->id == ZigTypeIdPointer); + ZigValue *pointee = const_ptr_pointee(ira, ira->codegen, val, source_instr->source_node); + if (pointee == nullptr) + return ira->codegen->invalid_inst_gen; + if (pointee->special != ConstValSpecialRuntime) { + ZigValue *array_val = ira->codegen->pass1_arena->create(); + array_val->special = ConstValSpecialStatic; + array_val->type = array_type; + array_val->data.x_array.special = ConstArraySpecialNone; + array_val->data.x_array.data.s_none.elements = pointee; + array_val->parent.id = ConstParentIdScalar; + array_val->parent.data.p_scalar.scalar_val = pointee; + + IrInstGenConst *const_instruction = ir_create_inst_gen(&ira->new_irb, + source_instr->scope, source_instr->source_node); + const_instruction->base.value->type = wanted_type; + const_instruction->base.value->special = ConstValSpecialStatic; + const_instruction->base.value->data.x_ptr.special = ConstPtrSpecialRef; + const_instruction->base.value->data.x_ptr.data.ref.pointee = array_val; + const_instruction->base.value->data.x_ptr.mut = val->data.x_ptr.mut; + return &const_instruction->base; + } + } + + // pointer to array and pointer to single item are represented the same way at runtime + return ir_build_cast(ira, &target->base, wanted_type, target, CastOpBitCast); +} + +static void report_recursive_error(IrAnalyze *ira, AstNode *source_node, ConstCastOnly *cast_result, + ErrorMsg *parent_msg) +{ + switch (cast_result->id) { + case ConstCastResultIdOk: + zig_unreachable(); + case ConstCastResultIdInvalid: + zig_unreachable(); + case ConstCastResultIdOptionalChild: { + ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node, + buf_sprintf("optional type child '%s' cannot cast into optional type child '%s'", + buf_ptr(&cast_result->data.optional->actual_child->name), + buf_ptr(&cast_result->data.optional->wanted_child->name))); + report_recursive_error(ira, source_node, &cast_result->data.optional->child, msg); + break; + } + case ConstCastResultIdOptionalShape: { + add_error_note(ira->codegen, parent_msg, source_node, + buf_sprintf("optional type child '%s' cannot cast into optional type '%s'", + buf_ptr(&cast_result->data.type_mismatch->actual_type->name), + buf_ptr(&cast_result->data.type_mismatch->wanted_type->name))); + break; + } + case ConstCastResultIdErrorUnionErrorSet: { + ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node, + buf_sprintf("error set '%s' cannot cast into error set '%s'", + buf_ptr(&cast_result->data.error_union_error_set->actual_err_set->name), + buf_ptr(&cast_result->data.error_union_error_set->wanted_err_set->name))); + report_recursive_error(ira, source_node, &cast_result->data.error_union_error_set->child, msg); + break; + } + case ConstCastResultIdErrSet: { + ZigList *missing_errors = &cast_result->data.error_set_mismatch->missing_errors; + for (size_t i = 0; i < missing_errors->length; i += 1) { + ErrorTableEntry *error_entry = missing_errors->at(i); + add_error_note(ira->codegen, parent_msg, ast_field_to_symbol_node(error_entry->decl_node), + buf_sprintf("'error.%s' not a member of destination error set", buf_ptr(&error_entry->name))); + } + break; + } + case ConstCastResultIdErrSetGlobal: { + add_error_note(ira->codegen, parent_msg, source_node, + buf_sprintf("cannot cast global error set into smaller set")); + break; + } + case ConstCastResultIdPointerChild: { + ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node, + buf_sprintf("pointer type child '%s' cannot cast into pointer type child '%s'", + buf_ptr(&cast_result->data.pointer_mismatch->actual_child->name), + buf_ptr(&cast_result->data.pointer_mismatch->wanted_child->name))); + report_recursive_error(ira, source_node, &cast_result->data.pointer_mismatch->child, msg); + break; + } + case ConstCastResultIdSliceChild: { + ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node, + buf_sprintf("slice type child '%s' cannot cast into slice type child '%s'", + buf_ptr(&cast_result->data.slice_mismatch->actual_child->name), + buf_ptr(&cast_result->data.slice_mismatch->wanted_child->name))); + report_recursive_error(ira, source_node, &cast_result->data.slice_mismatch->child, msg); + break; + } + case ConstCastResultIdErrorUnionPayload: { + ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node, + buf_sprintf("error union payload '%s' cannot cast into error union payload '%s'", + buf_ptr(&cast_result->data.error_union_payload->actual_payload->name), + buf_ptr(&cast_result->data.error_union_payload->wanted_payload->name))); + report_recursive_error(ira, source_node, &cast_result->data.error_union_payload->child, msg); + break; + } + case ConstCastResultIdType: { + AstNode *wanted_decl_node = type_decl_node(cast_result->data.type_mismatch->wanted_type); + AstNode *actual_decl_node = type_decl_node(cast_result->data.type_mismatch->actual_type); + if (wanted_decl_node != nullptr) { + add_error_note(ira->codegen, parent_msg, wanted_decl_node, + buf_sprintf("%s declared here", + buf_ptr(&cast_result->data.type_mismatch->wanted_type->name))); + } + if (actual_decl_node != nullptr) { + add_error_note(ira->codegen, parent_msg, actual_decl_node, + buf_sprintf("%s declared here", + buf_ptr(&cast_result->data.type_mismatch->actual_type->name))); + } + break; + } + case ConstCastResultIdFnArg: { + ErrorMsg *msg = add_error_note(ira->codegen, parent_msg, source_node, + buf_sprintf("parameter %" ZIG_PRI_usize ": '%s' cannot cast into '%s'", + cast_result->data.fn_arg.arg_index, + buf_ptr(&cast_result->data.fn_arg.actual_param_type->name), + buf_ptr(&cast_result->data.fn_arg.expected_param_type->name))); + report_recursive_error(ira, source_node, cast_result->data.fn_arg.child, msg); + break; + } + case ConstCastResultIdBadAllowsZero: { + ZigType *wanted_type = cast_result->data.bad_allows_zero->wanted_type; + ZigType *actual_type = cast_result->data.bad_allows_zero->actual_type; + bool wanted_allows_zero = ptr_allows_addr_zero(wanted_type); + bool actual_allows_zero = ptr_allows_addr_zero(actual_type); + if (actual_allows_zero && !wanted_allows_zero) { + add_error_note(ira->codegen, parent_msg, source_node, + buf_sprintf("'%s' could have null values which are illegal in type '%s'", + buf_ptr(&actual_type->name), + buf_ptr(&wanted_type->name))); + } else { + add_error_note(ira->codegen, parent_msg, source_node, + buf_sprintf("mutable '%s' allows illegal null values stored to type '%s'", + buf_ptr(&wanted_type->name), + buf_ptr(&actual_type->name))); + } + break; + } + case ConstCastResultIdPtrLens: { + add_error_note(ira->codegen, parent_msg, source_node, + buf_sprintf("pointer length mismatch")); + break; + } + case ConstCastResultIdPtrSentinel: { + ZigType *actual_type = cast_result->data.bad_ptr_sentinel->actual_type; + ZigType *wanted_type = cast_result->data.bad_ptr_sentinel->wanted_type; + { + Buf *txt_msg = buf_sprintf("destination pointer requires a terminating '"); + render_const_value(ira->codegen, txt_msg, wanted_type->data.pointer.sentinel); + buf_appendf(txt_msg, "' sentinel"); + if (actual_type->data.pointer.sentinel != nullptr) { + buf_appendf(txt_msg, ", but source pointer has a terminating '"); + render_const_value(ira->codegen, txt_msg, actual_type->data.pointer.sentinel); + buf_appendf(txt_msg, "' sentinel"); + } + add_error_note(ira->codegen, parent_msg, source_node, txt_msg); + } + break; + } + case ConstCastResultIdSentinelArrays: { + ZigType *actual_type = cast_result->data.sentinel_arrays->actual_type; + ZigType *wanted_type = cast_result->data.sentinel_arrays->wanted_type; + Buf *txt_msg = buf_sprintf("destination array requires a terminating '"); + render_const_value(ira->codegen, txt_msg, wanted_type->data.array.sentinel); + buf_appendf(txt_msg, "' sentinel"); + if (actual_type->data.array.sentinel != nullptr) { + buf_appendf(txt_msg, ", but source array has a terminating '"); + render_const_value(ira->codegen, txt_msg, actual_type->data.array.sentinel); + buf_appendf(txt_msg, "' sentinel"); + } + add_error_note(ira->codegen, parent_msg, source_node, txt_msg); + break; + } + case ConstCastResultIdCV: { + ZigType *wanted_type = cast_result->data.bad_cv->wanted_type; + ZigType *actual_type = cast_result->data.bad_cv->actual_type; + bool ok_const = !actual_type->data.pointer.is_const || wanted_type->data.pointer.is_const; + bool ok_volatile = !actual_type->data.pointer.is_volatile || wanted_type->data.pointer.is_volatile; + if (!ok_const) { + add_error_note(ira->codegen, parent_msg, source_node, buf_sprintf("cast discards const qualifier")); + } else if (!ok_volatile) { + add_error_note(ira->codegen, parent_msg, source_node, buf_sprintf("cast discards volatile qualifier")); + } else { + zig_unreachable(); + } + break; + } + case ConstCastResultIdFnIsGeneric: + add_error_note(ira->codegen, parent_msg, source_node, + buf_sprintf("only one of the functions is generic")); + break; + case ConstCastResultIdFnCC: + add_error_note(ira->codegen, parent_msg, source_node, + buf_sprintf("calling convention mismatch")); + break; + case ConstCastResultIdIntShorten: { + ZigType *wanted_type = cast_result->data.int_shorten->wanted_type; + ZigType *actual_type = cast_result->data.int_shorten->actual_type; + const char *wanted_signed = wanted_type->data.integral.is_signed ? "signed" : "unsigned"; + const char *actual_signed = actual_type->data.integral.is_signed ? "signed" : "unsigned"; + add_error_note(ira->codegen, parent_msg, source_node, + buf_sprintf("%s %" PRIu32 "-bit int cannot represent all possible %s %" PRIu32 "-bit values", + wanted_signed, wanted_type->data.integral.bit_count, + actual_signed, actual_type->data.integral.bit_count)); + break; + } + case ConstCastResultIdFnAlign: // TODO + case ConstCastResultIdFnVarArgs: // TODO + case ConstCastResultIdFnReturnType: // TODO + case ConstCastResultIdFnArgCount: // TODO + case ConstCastResultIdFnGenericArgCount: // TODO + case ConstCastResultIdFnArgNoAlias: // TODO + case ConstCastResultIdUnresolvedInferredErrSet: // TODO + case ConstCastResultIdAsyncAllocatorType: // TODO + case ConstCastResultIdArrayChild: // TODO + break; + } +} + +static IrInstGen *ir_analyze_array_to_vector(IrAnalyze *ira, IrInst* source_instr, + IrInstGen *array, ZigType *vector_type) +{ + if (instr_is_comptime(array)) { + // arrays and vectors have the same ZigValue representation + IrInstGen *result = ir_const(ira, source_instr, vector_type); + copy_const_val(ira->codegen, result->value, array->value); + result->value->type = vector_type; + return result; + } + return ir_build_array_to_vector(ira, source_instr, array, vector_type); +} + +static IrInstGen *ir_analyze_vector_to_array(IrAnalyze *ira, IrInst* source_instr, + IrInstGen *vector, ZigType *array_type, ResultLoc *result_loc) +{ + if (instr_is_comptime(vector)) { + // arrays and vectors have the same ZigValue representation + IrInstGen *result = ir_const(ira, source_instr, array_type); + copy_const_val(ira->codegen, result->value, vector->value); + result->value->type = array_type; + return result; + } + if (result_loc == nullptr) { + result_loc = no_result_loc(); + } + IrInstGen *result_loc_inst = ir_resolve_result(ira, source_instr, result_loc, array_type, nullptr, true, true); + if (type_is_invalid(result_loc_inst->value->type) || result_loc_inst->value->type->id == ZigTypeIdUnreachable) { + return result_loc_inst; + } + return ir_build_vector_to_array(ira, source_instr, array_type, vector, result_loc_inst); +} + +static IrInstGen *ir_analyze_int_to_c_ptr(IrAnalyze *ira, IrInst* source_instr, + IrInstGen *integer, ZigType *dest_type) +{ + IrInstGen *unsigned_integer; + if (instr_is_comptime(integer)) { + unsigned_integer = integer; + } else { + assert(integer->value->type->id == ZigTypeIdInt); + + if (integer->value->type->data.integral.bit_count > + ira->codegen->builtin_types.entry_usize->data.integral.bit_count) + { + ir_add_error(ira, source_instr, + buf_sprintf("integer type '%s' too big for implicit @intToPtr to type '%s'", + buf_ptr(&integer->value->type->name), + buf_ptr(&dest_type->name))); + return ira->codegen->invalid_inst_gen; + } + + if (integer->value->type->data.integral.is_signed) { + ZigType *unsigned_int_type = get_int_type(ira->codegen, false, + integer->value->type->data.integral.bit_count); + unsigned_integer = ir_analyze_bit_cast(ira, source_instr, integer, unsigned_int_type); + if (type_is_invalid(unsigned_integer->value->type)) + return ira->codegen->invalid_inst_gen; + } else { + unsigned_integer = integer; + } + } + + return ir_analyze_int_to_ptr(ira, source_instr, unsigned_integer, dest_type); +} + +static bool is_pointery_and_elem_is_not_pointery(ZigType *ty) { + if (ty->id == ZigTypeIdPointer) return ty->data.pointer.child_type->id != ZigTypeIdPointer; + if (ty->id == ZigTypeIdFn) return true; + if (ty->id == ZigTypeIdOptional) { + ZigType *ptr_ty = ty->data.maybe.child_type; + if (ptr_ty->id == ZigTypeIdPointer) return ptr_ty->data.pointer.child_type->id != ZigTypeIdPointer; + if (ptr_ty->id == ZigTypeIdFn) return true; + } + return false; +} + +static IrInstGen *ir_analyze_enum_literal(IrAnalyze *ira, IrInst* source_instr, IrInstGen *value, + ZigType *enum_type) +{ + assert(enum_type->id == ZigTypeIdEnum); + + Error err; + if ((err = type_resolve(ira->codegen, enum_type, ResolveStatusZeroBitsKnown))) + return ira->codegen->invalid_inst_gen; + + TypeEnumField *field = find_enum_type_field(enum_type, value->value->data.x_enum_literal); + if (field == nullptr) { + ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("enum '%s' has no field named '%s'", + buf_ptr(&enum_type->name), buf_ptr(value->value->data.x_enum_literal))); + add_error_note(ira->codegen, msg, enum_type->data.enumeration.decl_node, + buf_sprintf("'%s' declared here", buf_ptr(&enum_type->name))); + return ira->codegen->invalid_inst_gen; + } + IrInstGen *result = ir_const(ira, source_instr, enum_type); + bigint_init_bigint(&result->value->data.x_enum_tag, &field->value); + + return result; +} + +static IrInstGen *ir_analyze_struct_literal_to_array(IrAnalyze *ira, IrInst* source_instr, + IrInstGen *value, ZigType *wanted_type) +{ + ir_add_error(ira, source_instr, buf_sprintf("TODO: type coercion of anon list literal to array")); + return ira->codegen->invalid_inst_gen; +} + +static IrInstGen *ir_analyze_struct_literal_to_struct(IrAnalyze *ira, IrInst* source_instr, + IrInstGen *struct_operand, ZigType *wanted_type) +{ + Error err; + + IrInstGen *struct_ptr = ir_get_ref(ira, source_instr, struct_operand, true, false); + if (type_is_invalid(struct_ptr->value->type)) + return ira->codegen->invalid_inst_gen; + + if (wanted_type->data.structure.resolve_status == ResolveStatusBeingInferred) { + ir_add_error(ira, source_instr, buf_sprintf("type coercion of anon struct literal to inferred struct")); + return ira->codegen->invalid_inst_gen; + } + + if ((err = type_resolve(ira->codegen, wanted_type, ResolveStatusSizeKnown))) + return ira->codegen->invalid_inst_gen; + + size_t actual_field_count = wanted_type->data.structure.src_field_count; + size_t instr_field_count = struct_operand->value->type->data.structure.src_field_count; + + bool need_comptime = ir_should_inline(ira->old_irb.exec, source_instr->scope) + || type_requires_comptime(ira->codegen, wanted_type) == ReqCompTimeYes; + bool is_comptime = true; + + // Determine if the struct_operand will be comptime. + // Also emit compile errors for missing fields and duplicate fields. + AstNode **field_assign_nodes = heap::c_allocator.allocate(actual_field_count); + ZigValue **field_values = heap::c_allocator.allocate(actual_field_count); + IrInstGen **casted_fields = heap::c_allocator.allocate(actual_field_count); + IrInstGen *const_result = ir_const(ira, source_instr, wanted_type); + + for (size_t i = 0; i < instr_field_count; i += 1) { + TypeStructField *src_field = struct_operand->value->type->data.structure.fields[i]; + TypeStructField *dst_field = find_struct_type_field(wanted_type, src_field->name); + if (dst_field == nullptr) { + ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("no field named '%s' in struct '%s'", + buf_ptr(src_field->name), buf_ptr(&wanted_type->name))); + if (wanted_type->data.structure.decl_node) { + add_error_note(ira->codegen, msg, wanted_type->data.structure.decl_node, + buf_sprintf("struct '%s' declared here", buf_ptr(&wanted_type->name))); + } + add_error_note(ira->codegen, msg, src_field->decl_node, + buf_sprintf("field '%s' declared here", buf_ptr(src_field->name))); + return ira->codegen->invalid_inst_gen; + } + + ir_assert(src_field->decl_node != nullptr, source_instr); + AstNode *existing_assign_node = field_assign_nodes[dst_field->src_index]; + if (existing_assign_node != nullptr) { + ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("duplicate field")); + add_error_note(ira->codegen, msg, existing_assign_node, buf_sprintf("other field here")); + return ira->codegen->invalid_inst_gen; + } + field_assign_nodes[dst_field->src_index] = src_field->decl_node; + + IrInstGen *field_ptr = ir_analyze_struct_field_ptr(ira, source_instr, src_field, struct_ptr, + struct_operand->value->type, false); + if (type_is_invalid(field_ptr->value->type)) + return ira->codegen->invalid_inst_gen; + IrInstGen *field_value = ir_get_deref(ira, source_instr, field_ptr, nullptr); + if (type_is_invalid(field_value->value->type)) + return ira->codegen->invalid_inst_gen; + IrInstGen *casted_value = ir_implicit_cast(ira, field_value, dst_field->type_entry); + if (type_is_invalid(casted_value->value->type)) + return ira->codegen->invalid_inst_gen; + + casted_fields[dst_field->src_index] = casted_value; + if (need_comptime || instr_is_comptime(casted_value)) { + ZigValue *field_val = ir_resolve_const(ira, casted_value, UndefOk); + if (field_val == nullptr) + return ira->codegen->invalid_inst_gen; + field_val->parent.id = ConstParentIdStruct; + field_val->parent.data.p_struct.struct_val = const_result->value; + field_val->parent.data.p_struct.field_index = dst_field->src_index; + field_values[dst_field->src_index] = field_val; + if (field_val->type->id == ZigTypeIdUndefined && dst_field->type_entry->id != ZigTypeIdUndefined) { + field_values[dst_field->src_index]->special = ConstValSpecialUndef; + } + } else { + is_comptime = false; + } + } + + bool any_missing = false; + for (size_t i = 0; i < actual_field_count; i += 1) { + if (field_assign_nodes[i] != nullptr) continue; + + // look for a default field value + TypeStructField *field = wanted_type->data.structure.fields[i]; + memoize_field_init_val(ira->codegen, wanted_type, field); + if (field->init_val == nullptr) { + ir_add_error(ira, source_instr, + buf_sprintf("missing field: '%s'", buf_ptr(field->name))); + any_missing = true; + continue; + } + if (type_is_invalid(field->init_val->type)) + return ira->codegen->invalid_inst_gen; + ZigValue *init_val_copy = ira->codegen->pass1_arena->create(); + copy_const_val(ira->codegen, init_val_copy, field->init_val); + init_val_copy->parent.id = ConstParentIdStruct; + init_val_copy->parent.data.p_struct.struct_val = const_result->value; + init_val_copy->parent.data.p_struct.field_index = i; + field_values[i] = init_val_copy; + casted_fields[i] = ir_const_move(ira, source_instr, init_val_copy); + } + if (any_missing) + return ira->codegen->invalid_inst_gen; + + if (is_comptime) { + heap::c_allocator.deallocate(field_assign_nodes, actual_field_count); + IrInstGen *const_result = ir_const(ira, source_instr, wanted_type); + const_result->value->data.x_struct.fields = field_values; + return const_result; + } + + IrInstGen *result_loc_inst = ir_resolve_result(ira, source_instr, no_result_loc(), + wanted_type, nullptr, true, true); + if (type_is_invalid(result_loc_inst->value->type) || result_loc_inst->value->type->id == ZigTypeIdUnreachable) { + return ira->codegen->invalid_inst_gen; + } + + for (size_t i = 0; i < actual_field_count; i += 1) { + TypeStructField *field = wanted_type->data.structure.fields[i]; + IrInstGen *field_ptr = ir_analyze_struct_field_ptr(ira, source_instr, field, result_loc_inst, wanted_type, true); + if (type_is_invalid(field_ptr->value->type)) + return ira->codegen->invalid_inst_gen; + IrInstGen *store_ptr_inst = ir_analyze_store_ptr(ira, source_instr, field_ptr, casted_fields[i], true); + if (type_is_invalid(store_ptr_inst->value->type)) + return ira->codegen->invalid_inst_gen; + } + + heap::c_allocator.deallocate(field_assign_nodes, actual_field_count); + heap::c_allocator.deallocate(field_values, actual_field_count); + heap::c_allocator.deallocate(casted_fields, actual_field_count); + + return ir_get_deref(ira, source_instr, result_loc_inst, nullptr); +} + +static IrInstGen *ir_analyze_struct_literal_to_union(IrAnalyze *ira, IrInst* source_instr, + IrInstGen *value, ZigType *union_type) +{ + Error err; + ZigType *struct_type = value->value->type; + + assert(struct_type->id == ZigTypeIdStruct); + assert(union_type->id == ZigTypeIdUnion); + assert(struct_type->data.structure.src_field_count == 1); + + TypeStructField *only_field = struct_type->data.structure.fields[0]; + + if ((err = type_resolve(ira->codegen, union_type, ResolveStatusZeroBitsKnown))) + return ira->codegen->invalid_inst_gen; + + TypeUnionField *union_field = find_union_type_field(union_type, only_field->name); + if (union_field == nullptr) { + ir_add_error_node(ira, only_field->decl_node, + buf_sprintf("no field named '%s' in union '%s'", + buf_ptr(only_field->name), buf_ptr(&union_type->name))); + return ira->codegen->invalid_inst_gen; + } + + ZigType *payload_type = resolve_union_field_type(ira->codegen, union_field); + if (payload_type == nullptr) + return ira->codegen->invalid_inst_gen; + + IrInstGen *field_value = ir_analyze_struct_value_field_value(ira, source_instr, value, only_field); + if (type_is_invalid(field_value->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *casted_value = ir_implicit_cast(ira, field_value, payload_type); + if (type_is_invalid(casted_value->value->type)) + return ira->codegen->invalid_inst_gen; + + if (instr_is_comptime(casted_value)) { + ZigValue *val = ir_resolve_const(ira, casted_value, UndefBad); + if (val == nullptr) + return ira->codegen->invalid_inst_gen; + + IrInstGen *result = ir_const(ira, source_instr, union_type); + bigint_init_bigint(&result->value->data.x_union.tag, &union_field->enum_field->value); + result->value->data.x_union.payload = val; + + val->parent.id = ConstParentIdUnion; + val->parent.data.p_union.union_val = result->value; + + return result; + } + + IrInstGen *result_loc_inst = ir_resolve_result(ira, source_instr, no_result_loc(), + union_type, nullptr, true, true); + if (type_is_invalid(result_loc_inst->value->type) || result_loc_inst->value->type->id == ZigTypeIdUnreachable) { + return ira->codegen->invalid_inst_gen; + } + + IrInstGen *payload_ptr = ir_analyze_container_field_ptr(ira, only_field->name, source_instr, + result_loc_inst, source_instr, union_type, true); + if (type_is_invalid(payload_ptr->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *store_ptr_inst = ir_analyze_store_ptr(ira, source_instr, payload_ptr, casted_value, false); + if (type_is_invalid(store_ptr_inst->value->type)) + return ira->codegen->invalid_inst_gen; + + return ir_get_deref(ira, source_instr, result_loc_inst, nullptr); +} + +// Add a compile error and return ErrorSemanticAnalyzeFail if the pointer alignment does not work, +// otherwise return ErrorNone. Does not emit any instructions. +// Assumes that the pointer types have element types with the same ABI alignment. Avoids resolving the +// pointer types' alignments if both of the pointer types are ABI aligned. +static Error ir_cast_ptr_align(IrAnalyze *ira, IrInst* source_instr, ZigType *dest_ptr_type, + ZigType *src_ptr_type, AstNode *src_source_node) +{ + Error err; + + ir_assert(dest_ptr_type->id == ZigTypeIdPointer, source_instr); + ir_assert(src_ptr_type->id == ZigTypeIdPointer, source_instr); + + if (dest_ptr_type->data.pointer.explicit_alignment == 0 && + src_ptr_type->data.pointer.explicit_alignment == 0) + { + return ErrorNone; + } + + if ((err = type_resolve(ira->codegen, dest_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) + return ErrorSemanticAnalyzeFail; + + if ((err = type_resolve(ira->codegen, src_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) + return ErrorSemanticAnalyzeFail; + + uint32_t wanted_align = get_ptr_align(ira->codegen, dest_ptr_type); + uint32_t actual_align = get_ptr_align(ira->codegen, src_ptr_type); + if (wanted_align > actual_align) { + ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("cast increases pointer alignment")); + add_error_note(ira->codegen, msg, src_source_node, + buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&src_ptr_type->name), actual_align)); + add_error_note(ira->codegen, msg, source_instr->source_node, + buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&dest_ptr_type->name), wanted_align)); + return ErrorSemanticAnalyzeFail; + } + + return ErrorNone; +} + +static IrInstGen *ir_analyze_struct_value_field_value(IrAnalyze *ira, IrInst* source_instr, + IrInstGen *struct_operand, TypeStructField *field) +{ + IrInstGen *struct_ptr = ir_get_ref(ira, source_instr, struct_operand, true, false); + if (type_is_invalid(struct_ptr->value->type)) + return ira->codegen->invalid_inst_gen; + IrInstGen *field_ptr = ir_analyze_struct_field_ptr(ira, source_instr, field, struct_ptr, + struct_operand->value->type, false); + if (type_is_invalid(field_ptr->value->type)) + return ira->codegen->invalid_inst_gen; + return ir_get_deref(ira, source_instr, field_ptr, nullptr); +} + +static IrInstGen *ir_analyze_optional_value_payload_value(IrAnalyze *ira, IrInst* source_instr, + IrInstGen *optional_operand, bool safety_check_on) +{ + IrInstGen *opt_ptr = ir_get_ref(ira, source_instr, optional_operand, true, false); + IrInstGen *payload_ptr = ir_analyze_unwrap_optional_payload(ira, source_instr, opt_ptr, + safety_check_on, false); + return ir_get_deref(ira, source_instr, payload_ptr, nullptr); +} + +static IrInstGen *ir_analyze_cast(IrAnalyze *ira, IrInst *source_instr, + ZigType *wanted_type, IrInstGen *value) +{ + Error err; + ZigType *actual_type = value->value->type; + AstNode *source_node = source_instr->source_node; + + if (type_is_invalid(wanted_type) || type_is_invalid(actual_type)) { + return ira->codegen->invalid_inst_gen; + } + + // This means the wanted type is anything. + if (wanted_type == ira->codegen->builtin_types.entry_anytype) { + return value; + } + + // perfect match or non-const to const + ConstCastOnly const_cast_result = types_match_const_cast_only(ira, wanted_type, actual_type, + source_node, false); + if (const_cast_result.id == ConstCastResultIdInvalid) + return ira->codegen->invalid_inst_gen; + if (const_cast_result.id == ConstCastResultIdOk) { + return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop); + } + + if (const_cast_result.id == ConstCastResultIdFnCC) { + ir_assert(value->value->type->id == ZigTypeIdFn, source_instr); + // ConstCastResultIdFnCC is guaranteed to be the last one reported, meaning everything else is ok. + if (wanted_type->data.fn.fn_type_id.cc == CallingConventionAsync && + actual_type->data.fn.fn_type_id.cc == CallingConventionUnspecified) + { + ir_assert(value->value->data.x_ptr.special == ConstPtrSpecialFunction, source_instr); + ZigFn *fn = value->value->data.x_ptr.data.fn.fn_entry; + if (fn->inferred_async_node == nullptr) { + fn->inferred_async_node = source_instr->source_node; + } + return ir_resolve_cast(ira, source_instr, value, wanted_type, CastOpNoop); + } + } + + // cast from T to ?T + // note that the *T to ?*T case is handled via the "ConstCastOnly" mechanism + if (wanted_type->id == ZigTypeIdOptional) { + ZigType *wanted_child_type = wanted_type->data.maybe.child_type; + if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node, + false).id == ConstCastResultIdOk) + { + return ir_analyze_optional_wrap(ira, source_instr, value, wanted_type, nullptr); + } else if (actual_type->id == ZigTypeIdComptimeInt || + actual_type->id == ZigTypeIdComptimeFloat) + { + if (ir_num_lit_fits_in_other_type(ira, value, wanted_child_type, true)) { + return ir_analyze_optional_wrap(ira, source_instr, value, wanted_type, nullptr); + } else { + return ira->codegen->invalid_inst_gen; + } + } else if ( + wanted_child_type->id == ZigTypeIdPointer && + wanted_child_type->data.pointer.ptr_len == PtrLenUnknown && + actual_type->id == ZigTypeIdPointer && + actual_type->data.pointer.ptr_len == PtrLenSingle && + actual_type->data.pointer.child_type->id == ZigTypeIdArray) + { + if ((err = type_resolve(ira->codegen, actual_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) + return ira->codegen->invalid_inst_gen; + if ((err = type_resolve(ira->codegen, wanted_child_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) + return ira->codegen->invalid_inst_gen; + if (get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, wanted_child_type) && + types_match_const_cast_only(ira, wanted_child_type->data.pointer.child_type, + actual_type->data.pointer.child_type->data.array.child_type, source_node, + !wanted_child_type->data.pointer.is_const).id == ConstCastResultIdOk) + { + IrInstGen *cast1 = ir_resolve_ptr_of_array_to_unknown_len_ptr(ira, source_instr, value, + wanted_child_type); + if (type_is_invalid(cast1->value->type)) + return ira->codegen->invalid_inst_gen; + return ir_analyze_optional_wrap(ira, source_instr, cast1, wanted_type, nullptr); + } + } + } + + // T to E!T + if (wanted_type->id == ZigTypeIdErrorUnion) { + if (types_match_const_cast_only(ira, wanted_type->data.error_union.payload_type, actual_type, + source_node, false).id == ConstCastResultIdOk) + { + return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type, nullptr); + } else if (actual_type->id == ZigTypeIdComptimeInt || + actual_type->id == ZigTypeIdComptimeFloat) + { + if (ir_num_lit_fits_in_other_type(ira, value, wanted_type->data.error_union.payload_type, true)) { + return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type, nullptr); + } else { + return ira->codegen->invalid_inst_gen; + } + } + } + + // cast from T to E!?T + if (wanted_type->id == ZigTypeIdErrorUnion && + wanted_type->data.error_union.payload_type->id == ZigTypeIdOptional && + actual_type->id != ZigTypeIdOptional) + { + ZigType *wanted_child_type = wanted_type->data.error_union.payload_type->data.maybe.child_type; + if (types_match_const_cast_only(ira, wanted_child_type, actual_type, source_node, false).id == ConstCastResultIdOk || + actual_type->id == ZigTypeIdNull || + actual_type->id == ZigTypeIdComptimeInt || + actual_type->id == ZigTypeIdComptimeFloat) + { + IrInstGen *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.error_union.payload_type, value); + if (type_is_invalid(cast1->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1); + if (type_is_invalid(cast2->value->type)) + return ira->codegen->invalid_inst_gen; + + return cast2; + } + } + + + // cast from comptime-known number to another number type + if (instr_is_comptime(value) && + (actual_type->id == ZigTypeIdInt || actual_type->id == ZigTypeIdComptimeInt || + actual_type->id == ZigTypeIdFloat || actual_type->id == ZigTypeIdComptimeFloat) && + (wanted_type->id == ZigTypeIdInt || wanted_type->id == ZigTypeIdComptimeInt || + wanted_type->id == ZigTypeIdFloat || wanted_type->id == ZigTypeIdComptimeFloat)) + { + if (value->value->special == ConstValSpecialUndef) { + IrInstGen *result = ir_const(ira, source_instr, wanted_type); + result->value->special = ConstValSpecialUndef; + return result; + } + if (ir_num_lit_fits_in_other_type(ira, value, wanted_type, true)) { + if (wanted_type->id == ZigTypeIdComptimeInt || wanted_type->id == ZigTypeIdInt) { + IrInstGen *result = ir_const(ira, source_instr, wanted_type); + if (actual_type->id == ZigTypeIdComptimeInt || actual_type->id == ZigTypeIdInt) { + copy_const_val(ira->codegen, result->value, value->value); + result->value->type = wanted_type; + } else { + float_init_bigint(&result->value->data.x_bigint, value->value); + } + return result; + } else if (wanted_type->id == ZigTypeIdComptimeFloat || wanted_type->id == ZigTypeIdFloat) { + IrInstGen *result = ir_const(ira, source_instr, wanted_type); + if (actual_type->id == ZigTypeIdComptimeInt || actual_type->id == ZigTypeIdInt) { + BigFloat bf; + bigfloat_init_bigint(&bf, &value->value->data.x_bigint); + float_init_bigfloat(result->value, &bf); + } else { + float_init_float(result->value, value->value); + } + return result; + } + zig_unreachable(); + } else { + return ira->codegen->invalid_inst_gen; + } + } + + // widening conversion + if (wanted_type->id == ZigTypeIdInt && + actual_type->id == ZigTypeIdInt && + wanted_type->data.integral.is_signed == actual_type->data.integral.is_signed && + wanted_type->data.integral.bit_count >= actual_type->data.integral.bit_count) + { + return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type); + } + + // small enough unsigned ints can get casted to large enough signed ints + if (wanted_type->id == ZigTypeIdInt && wanted_type->data.integral.is_signed && + actual_type->id == ZigTypeIdInt && !actual_type->data.integral.is_signed && + wanted_type->data.integral.bit_count > actual_type->data.integral.bit_count) + { + return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type); + } + + // float widening conversion + if (wanted_type->id == ZigTypeIdFloat && + actual_type->id == ZigTypeIdFloat && + wanted_type->data.floating.bit_count >= actual_type->data.floating.bit_count) + { + return ir_analyze_widen_or_shorten(ira, source_instr, value, wanted_type); + } + + // *[N]T to ?[]T + if (wanted_type->id == ZigTypeIdOptional && + is_slice(wanted_type->data.maybe.child_type) && + actual_type->id == ZigTypeIdPointer && + actual_type->data.pointer.ptr_len == PtrLenSingle && + actual_type->data.pointer.child_type->id == ZigTypeIdArray) + { + IrInstGen *cast1 = ir_analyze_cast(ira, source_instr, wanted_type->data.maybe.child_type, value); + if (type_is_invalid(cast1->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1); + if (type_is_invalid(cast2->value->type)) + return ira->codegen->invalid_inst_gen; + + return cast2; + } + + // *[N]T to [*]T and [*c]T + if (wanted_type->id == ZigTypeIdPointer && + (wanted_type->data.pointer.ptr_len == PtrLenUnknown || wanted_type->data.pointer.ptr_len == PtrLenC) && + actual_type->id == ZigTypeIdPointer && + actual_type->data.pointer.ptr_len == PtrLenSingle && + actual_type->data.pointer.child_type->id == ZigTypeIdArray && + (!actual_type->data.pointer.is_const || wanted_type->data.pointer.is_const) && + (!actual_type->data.pointer.is_volatile || wanted_type->data.pointer.is_volatile)) + { + ZigType *actual_array_type = actual_type->data.pointer.child_type; + if (wanted_type->data.pointer.sentinel == nullptr || + (actual_array_type->data.array.sentinel != nullptr && + const_values_equal(ira->codegen, wanted_type->data.pointer.sentinel, + actual_array_type->data.array.sentinel))) + { + if ((err = type_resolve(ira->codegen, actual_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) + return ira->codegen->invalid_inst_gen; + if ((err = type_resolve(ira->codegen, wanted_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) + return ira->codegen->invalid_inst_gen; + if (get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, wanted_type) && + types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, + actual_type->data.pointer.child_type->data.array.child_type, source_node, + !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk) + { + return ir_resolve_ptr_of_array_to_unknown_len_ptr(ira, source_instr, value, wanted_type); + } + } + } + + // *[N]T to []T + // *[N]T to E![]T + if ((is_slice(wanted_type) || + (wanted_type->id == ZigTypeIdErrorUnion && + is_slice(wanted_type->data.error_union.payload_type))) && + actual_type->id == ZigTypeIdPointer && + actual_type->data.pointer.ptr_len == PtrLenSingle && + actual_type->data.pointer.child_type->id == ZigTypeIdArray) + { + ZigType *slice_type = (wanted_type->id == ZigTypeIdErrorUnion) ? + wanted_type->data.error_union.payload_type : wanted_type; + ZigType *slice_ptr_type = slice_type->data.structure.fields[slice_ptr_index]->type_entry; + assert(slice_ptr_type->id == ZigTypeIdPointer); + ZigType *array_type = actual_type->data.pointer.child_type; + bool const_ok = (slice_ptr_type->data.pointer.is_const || array_type->data.array.len == 0 + || !actual_type->data.pointer.is_const); + + if (const_ok && types_match_const_cast_only(ira, slice_ptr_type->data.pointer.child_type, + array_type->data.array.child_type, source_node, + !slice_ptr_type->data.pointer.is_const).id == ConstCastResultIdOk && + (slice_ptr_type->data.pointer.sentinel == nullptr || + (array_type->data.array.sentinel != nullptr && + const_values_equal(ira->codegen, array_type->data.array.sentinel, + slice_ptr_type->data.pointer.sentinel)))) + { + // If the pointers both have ABI align, it works. + // Or if the array length is 0, alignment doesn't matter. + bool ok_align = array_type->data.array.len == 0 || + (slice_ptr_type->data.pointer.explicit_alignment == 0 && + actual_type->data.pointer.explicit_alignment == 0); + if (!ok_align) { + // If either one has non ABI align, we have to resolve them both + if ((err = type_resolve(ira->codegen, actual_type->data.pointer.child_type, + ResolveStatusAlignmentKnown))) + { + return ira->codegen->invalid_inst_gen; + } + if ((err = type_resolve(ira->codegen, slice_ptr_type->data.pointer.child_type, + ResolveStatusAlignmentKnown))) + { + return ira->codegen->invalid_inst_gen; + } + ok_align = get_ptr_align(ira->codegen, actual_type) >= get_ptr_align(ira->codegen, slice_ptr_type); + } + if (ok_align) { + if (wanted_type->id == ZigTypeIdErrorUnion) { + IrInstGen *cast1 = ir_analyze_cast(ira, source_instr, slice_type, value); + if (type_is_invalid(cast1->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *cast2 = ir_analyze_cast(ira, source_instr, wanted_type, cast1); + if (type_is_invalid(cast2->value->type)) + return ira->codegen->invalid_inst_gen; + + return cast2; + } else { + return ir_resolve_ptr_of_array_to_slice(ira, source_instr, value, slice_type, nullptr); + } + } + } + } + + // @Vector(N,T1) to @Vector(N,T2) + if (actual_type->id == ZigTypeIdVector && wanted_type->id == ZigTypeIdVector) { + if (actual_type->data.vector.len == wanted_type->data.vector.len && + types_match_const_cast_only(ira, wanted_type->data.vector.elem_type, + actual_type->data.vector.elem_type, source_node, false).id == ConstCastResultIdOk) + { + return ir_analyze_bit_cast(ira, source_instr, value, wanted_type); + } + } + + // *@Frame(func) to anyframe->T or anyframe + // *@Frame(func) to ?anyframe->T or ?anyframe + // *@Frame(func) to E!anyframe->T or E!anyframe + if (actual_type->id == ZigTypeIdPointer && actual_type->data.pointer.ptr_len == PtrLenSingle && + !actual_type->data.pointer.is_const && + actual_type->data.pointer.child_type->id == ZigTypeIdFnFrame) + { + ZigType *anyframe_type; + if (wanted_type->id == ZigTypeIdAnyFrame) { + anyframe_type = wanted_type; + } else if (wanted_type->id == ZigTypeIdOptional && + wanted_type->data.maybe.child_type->id == ZigTypeIdAnyFrame) + { + anyframe_type = wanted_type->data.maybe.child_type; + } else if (wanted_type->id == ZigTypeIdErrorUnion && + wanted_type->data.error_union.payload_type->id == ZigTypeIdAnyFrame) + { + anyframe_type = wanted_type->data.error_union.payload_type; + } else { + anyframe_type = nullptr; + } + if (anyframe_type != nullptr) { + bool ok = true; + if (anyframe_type->data.any_frame.result_type != nullptr) { + ZigFn *fn = actual_type->data.pointer.child_type->data.frame.fn; + ZigType *fn_return_type = fn->type_entry->data.fn.fn_type_id.return_type; + if (anyframe_type->data.any_frame.result_type != fn_return_type) { + ok = false; + } + } + if (ok) { + IrInstGen *cast1 = ir_analyze_frame_ptr_to_anyframe(ira, source_instr, value, anyframe_type); + if (anyframe_type == wanted_type) + return cast1; + return ir_analyze_cast(ira, source_instr, wanted_type, cast1); + } + } + } + + // anyframe->T to anyframe + if (actual_type->id == ZigTypeIdAnyFrame && actual_type->data.any_frame.result_type != nullptr && + wanted_type->id == ZigTypeIdAnyFrame && wanted_type->data.any_frame.result_type == nullptr) + { + return ir_analyze_anyframe_to_anyframe(ira, source_instr, value, wanted_type); + } + + // cast from null literal to maybe type + if (wanted_type->id == ZigTypeIdOptional && + actual_type->id == ZigTypeIdNull) + { + return ir_analyze_null_to_maybe(ira, source_instr, value, wanted_type); + } + + // cast from null literal to C pointer + if (wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.ptr_len == PtrLenC && + actual_type->id == ZigTypeIdNull) + { + return ir_analyze_null_to_c_pointer(ira, source_instr, value, wanted_type); + } + + // cast from E to E!T + if (wanted_type->id == ZigTypeIdErrorUnion && + actual_type->id == ZigTypeIdErrorSet) + { + return ir_analyze_err_wrap_code(ira, source_instr, value, wanted_type, nullptr); + } + + // cast from typed number to integer or float literal. + // works when the number is known at compile time + if (instr_is_comptime(value) && + ((actual_type->id == ZigTypeIdInt && wanted_type->id == ZigTypeIdComptimeInt) || + (actual_type->id == ZigTypeIdFloat && wanted_type->id == ZigTypeIdComptimeFloat))) + { + return ir_analyze_number_to_literal(ira, source_instr, value, wanted_type); + } + + // cast from enum literal to enum with matching field name + if (actual_type->id == ZigTypeIdEnumLiteral && wanted_type->id == ZigTypeIdEnum) + { + return ir_analyze_enum_literal(ira, source_instr, value, wanted_type); + } + + // cast from enum literal to optional enum + if (actual_type->id == ZigTypeIdEnumLiteral && + (wanted_type->id == ZigTypeIdOptional && wanted_type->data.maybe.child_type->id == ZigTypeIdEnum)) + { + IrInstGen *result = ir_analyze_enum_literal(ira, source_instr, value, wanted_type->data.maybe.child_type); + if (type_is_invalid(result->value->type)) + return result; + + return ir_analyze_optional_wrap(ira, source_instr, value, wanted_type, nullptr); + } + + // cast from enum literal to error union when payload is an enum + if (actual_type->id == ZigTypeIdEnumLiteral && + (wanted_type->id == ZigTypeIdErrorUnion && wanted_type->data.error_union.payload_type->id == ZigTypeIdEnum)) + { + IrInstGen *result = ir_analyze_enum_literal(ira, source_instr, value, wanted_type->data.error_union.payload_type); + if (type_is_invalid(result->value->type)) + return result; + + return ir_analyze_err_wrap_payload(ira, source_instr, value, wanted_type, nullptr); + } + + // cast from union to the enum type of the union + if (actual_type->id == ZigTypeIdUnion && wanted_type->id == ZigTypeIdEnum) { + if ((err = type_resolve(ira->codegen, actual_type, ResolveStatusZeroBitsKnown))) + return ira->codegen->invalid_inst_gen; + + if (actual_type->data.unionation.tag_type == wanted_type) { + return ir_analyze_union_to_tag(ira, source_instr, value, wanted_type); + } + } + + // enum to union which has the enum as the tag type, or + // enum literal to union which has a matching enum as the tag type + if (is_tagged_union(wanted_type) && (actual_type->id == ZigTypeIdEnum || + actual_type->id == ZigTypeIdEnumLiteral)) + { + return ir_analyze_enum_to_union(ira, source_instr, value, wanted_type); + } + + // cast from *T to *[1]T + if (wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.ptr_len == PtrLenSingle && + actual_type->id == ZigTypeIdPointer && actual_type->data.pointer.ptr_len == PtrLenSingle) + { + ZigType *array_type = wanted_type->data.pointer.child_type; + if (array_type->id == ZigTypeIdArray && array_type->data.array.len == 1 && + types_match_const_cast_only(ira, array_type->data.array.child_type, + actual_type->data.pointer.child_type, source_node, + !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk && + // `types_match_const_cast_only` only gets info for child_types + (!actual_type->data.pointer.is_const || wanted_type->data.pointer.is_const) && + (!actual_type->data.pointer.is_volatile || wanted_type->data.pointer.is_volatile)) + { + if ((err = ir_cast_ptr_align(ira, source_instr, wanted_type, actual_type, value->base.source_node))) + return ira->codegen->invalid_inst_gen; + + return ir_analyze_ptr_to_array(ira, source_instr, value, wanted_type); + } + } + + // [:x]T to [*:x]T + // [:x]T to [*c]T + if (wanted_type->id == ZigTypeIdPointer && is_slice(actual_type) && + ((wanted_type->data.pointer.ptr_len == PtrLenUnknown && wanted_type->data.pointer.sentinel != nullptr) || + wanted_type->data.pointer.ptr_len == PtrLenC)) + { + ZigType *slice_ptr_type = resolve_struct_field_type(ira->codegen, + actual_type->data.structure.fields[slice_ptr_index]); + if (types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, + slice_ptr_type->data.pointer.child_type, source_node, + !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk && + (slice_ptr_type->data.pointer.sentinel != nullptr && + (wanted_type->data.pointer.ptr_len == PtrLenC || + const_values_equal(ira->codegen, wanted_type->data.pointer.sentinel, + slice_ptr_type->data.pointer.sentinel)))) + { + TypeStructField *ptr_field = actual_type->data.structure.fields[slice_ptr_index]; + IrInstGen *slice_ptr = ir_analyze_struct_value_field_value(ira, source_instr, value, ptr_field); + return ir_implicit_cast2(ira, source_instr, slice_ptr, wanted_type); + } + } + + // cast from *T and [*]T to *c_void and ?*c_void + // but don't do it if the actual type is a double pointer + if (is_pointery_and_elem_is_not_pointery(actual_type)) { + ZigType *dest_ptr_type = nullptr; + if (wanted_type->id == ZigTypeIdPointer && + actual_type->id != ZigTypeIdOptional && + wanted_type->data.pointer.child_type == ira->codegen->builtin_types.entry_c_void) + { + dest_ptr_type = wanted_type; + } else if (wanted_type->id == ZigTypeIdOptional && + wanted_type->data.maybe.child_type->id == ZigTypeIdPointer && + wanted_type->data.maybe.child_type->data.pointer.child_type == ira->codegen->builtin_types.entry_c_void) + { + dest_ptr_type = wanted_type->data.maybe.child_type; + } + if (dest_ptr_type != nullptr) { + return ir_analyze_ptr_cast(ira, source_instr, value, source_instr, wanted_type, source_instr, true, + false); + } + } + + // cast from T to *T where T is zero bits + if (wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.ptr_len == PtrLenSingle && + types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, + actual_type, source_node, !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk) + { + bool has_bits; + if ((err = type_has_bits2(ira->codegen, actual_type, &has_bits))) + return ira->codegen->invalid_inst_gen; + if (!has_bits) { + return ir_get_ref(ira, source_instr, value, false, false); + } + } + + // cast from @Vector(N, T) to [N]T + if (wanted_type->id == ZigTypeIdArray && actual_type->id == ZigTypeIdVector && + wanted_type->data.array.len == actual_type->data.vector.len && + types_match_const_cast_only(ira, wanted_type->data.array.child_type, + actual_type->data.vector.elem_type, source_node, false).id == ConstCastResultIdOk) + { + return ir_analyze_vector_to_array(ira, source_instr, value, wanted_type, nullptr); + } + + // cast from [N]T to @Vector(N, T) + if (actual_type->id == ZigTypeIdArray && wanted_type->id == ZigTypeIdVector && + actual_type->data.array.len == wanted_type->data.vector.len && + types_match_const_cast_only(ira, actual_type->data.array.child_type, + wanted_type->data.vector.elem_type, source_node, false).id == ConstCastResultIdOk) + { + return ir_analyze_array_to_vector(ira, source_instr, value, wanted_type); + } + + // casting between C pointers and normal pointers + if (wanted_type->id == ZigTypeIdPointer && actual_type->id == ZigTypeIdPointer && + (wanted_type->data.pointer.ptr_len == PtrLenC || actual_type->data.pointer.ptr_len == PtrLenC) && + types_match_const_cast_only(ira, wanted_type->data.pointer.child_type, + actual_type->data.pointer.child_type, source_node, + !wanted_type->data.pointer.is_const).id == ConstCastResultIdOk) + { + return ir_analyze_ptr_cast(ira, source_instr, value, source_instr, wanted_type, source_instr, true, false); + } + + // cast from integer to C pointer + if (wanted_type->id == ZigTypeIdPointer && wanted_type->data.pointer.ptr_len == PtrLenC && + (actual_type->id == ZigTypeIdInt || actual_type->id == ZigTypeIdComptimeInt)) + { + return ir_analyze_int_to_c_ptr(ira, source_instr, value, wanted_type); + } + + // cast from inferred struct type to array, union, or struct + if (is_anon_container(actual_type)) { + const bool is_array_init = + actual_type->data.structure.special == StructSpecialInferredTuple; + const uint32_t field_count = actual_type->data.structure.src_field_count; + + if (wanted_type->id == ZigTypeIdArray && (is_array_init || field_count == 0) && + wanted_type->data.array.len == field_count) + { + return ir_analyze_struct_literal_to_array(ira, source_instr, value, wanted_type); + } else if (wanted_type->id == ZigTypeIdStruct && !is_slice(wanted_type) && + (!is_array_init || field_count == 0)) + { + return ir_analyze_struct_literal_to_struct(ira, source_instr, value, wanted_type); + } else if (wanted_type->id == ZigTypeIdUnion && !is_array_init && field_count == 1) { + return ir_analyze_struct_literal_to_union(ira, source_instr, value, wanted_type); + } + } + + // cast from undefined to anything + if (actual_type->id == ZigTypeIdUndefined) { + return ir_analyze_undefined_to_anything(ira, source_instr, value, wanted_type); + } + + // T to ?U, where T implicitly casts to U + if (wanted_type->id == ZigTypeIdOptional && actual_type->id != ZigTypeIdOptional) { + IrInstGen *cast1 = ir_implicit_cast2(ira, source_instr, value, wanted_type->data.maybe.child_type); + if (type_is_invalid(cast1->value->type)) + return ira->codegen->invalid_inst_gen; + return ir_implicit_cast2(ira, source_instr, cast1, wanted_type); + } + + // T to E!U, where T implicitly casts to U + if (wanted_type->id == ZigTypeIdErrorUnion && actual_type->id != ZigTypeIdErrorUnion && + actual_type->id != ZigTypeIdErrorSet) + { + IrInstGen *cast1 = ir_implicit_cast2(ira, source_instr, value, wanted_type->data.error_union.payload_type); + if (type_is_invalid(cast1->value->type)) + return ira->codegen->invalid_inst_gen; + return ir_implicit_cast2(ira, source_instr, cast1, wanted_type); + } + + ErrorMsg *parent_msg = ir_add_error_node(ira, source_instr->source_node, + buf_sprintf("expected type '%s', found '%s'", + buf_ptr(&wanted_type->name), + buf_ptr(&actual_type->name))); + report_recursive_error(ira, source_instr->source_node, &const_cast_result, parent_msg); + return ira->codegen->invalid_inst_gen; +} + +static IrInstGen *ir_implicit_cast2(IrAnalyze *ira, IrInst *value_source_instr, + IrInstGen *value, ZigType *expected_type) +{ + assert(value); + assert(!expected_type || !type_is_invalid(expected_type)); + assert(value->value->type); + assert(!type_is_invalid(value->value->type)); + if (expected_type == nullptr) + return value; // anything will do + if (expected_type == value->value->type) + return value; // match + if (value->value->type->id == ZigTypeIdUnreachable) + return value; + + return ir_analyze_cast(ira, value_source_instr, expected_type, value); +} + +static IrInstGen *ir_implicit_cast(IrAnalyze *ira, IrInstGen *value, ZigType *expected_type) { + return ir_implicit_cast2(ira, &value->base, value, expected_type); +} + +static ZigType *get_ptr_elem_type(CodeGen *g, IrInstGen *ptr) { + ir_assert_gen(ptr->value->type->id == ZigTypeIdPointer, ptr); + ZigType *elem_type = ptr->value->type->data.pointer.child_type; + if (elem_type != g->builtin_types.entry_anytype) + return elem_type; + + if (ir_resolve_lazy(g, ptr->base.source_node, ptr->value)) + return g->builtin_types.entry_invalid; + + assert(value_is_comptime(ptr->value)); + ZigValue *pointee = const_ptr_pointee_unchecked(g, ptr->value); + return pointee->type; +} + +static IrInstGen *ir_get_deref(IrAnalyze *ira, IrInst* source_instruction, IrInstGen *ptr, + ResultLoc *result_loc) +{ + Error err; + ZigType *ptr_type = ptr->value->type; + if (type_is_invalid(ptr_type)) + return ira->codegen->invalid_inst_gen; + + if (ptr_type->id != ZigTypeIdPointer) { + ir_add_error_node(ira, source_instruction->source_node, + buf_sprintf("attempt to dereference non-pointer type '%s'", + buf_ptr(&ptr_type->name))); + return ira->codegen->invalid_inst_gen; + } + + ZigType *child_type = ptr_type->data.pointer.child_type; + if (type_is_invalid(child_type)) + return ira->codegen->invalid_inst_gen; + // if the child type has one possible value, the deref is comptime + switch (type_has_one_possible_value(ira->codegen, child_type)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_inst_gen; + case OnePossibleValueYes: + return ir_const_move(ira, source_instruction, + get_the_one_possible_value(ira->codegen, child_type)); + case OnePossibleValueNo: + break; + } + if (instr_is_comptime(ptr)) { + if (ptr->value->special == ConstValSpecialUndef) { + // If we are in a TypeOf call, we return an undefined value instead of erroring + // since we know the type. + if (get_scope_typeof(source_instruction->scope)) { + return ir_const_undef(ira, source_instruction, child_type); + } + + ir_add_error(ira, &ptr->base, buf_sprintf("attempt to dereference undefined value")); + return ira->codegen->invalid_inst_gen; + } + if (ptr->value->data.x_ptr.mut != ConstPtrMutRuntimeVar) { + ZigValue *pointee = const_ptr_pointee_unchecked(ira->codegen, ptr->value); + if (child_type == ira->codegen->builtin_types.entry_anytype) { + child_type = pointee->type; + } + if (pointee->special != ConstValSpecialRuntime) { + IrInstGen *result = ir_const(ira, source_instruction, child_type); + + if ((err = ir_read_const_ptr(ira, ira->codegen, source_instruction->source_node, result->value, + ptr->value))) + { + return ira->codegen->invalid_inst_gen; + } + result->value->type = child_type; + return result; + } + } + } + + // if the instruction is a const ref instruction we can skip it + if (ptr->id == IrInstGenIdRef) { + IrInstGenRef *ref_inst = reinterpret_cast(ptr); + return ref_inst->operand; + } + + // If the instruction is a element pointer instruction to a vector, we emit + // vector element extract instruction rather than load pointer. If the + // pointer type has non-VECTOR_INDEX_RUNTIME value, it would have been + // possible to implement this in the codegen for IrInstGenLoadPtr. + // However if it has VECTOR_INDEX_RUNTIME then we must emit a compile error + // if the vector index cannot be determined right here, right now, because + // the type information does not contain enough information to actually + // perform a dereference. + if (ptr_type->data.pointer.vector_index == VECTOR_INDEX_RUNTIME) { + if (ptr->id == IrInstGenIdElemPtr) { + IrInstGenElemPtr *elem_ptr = (IrInstGenElemPtr *)ptr; + IrInstGen *vector_loaded = ir_get_deref(ira, &elem_ptr->array_ptr->base, + elem_ptr->array_ptr, nullptr); + IrInstGen *elem_index = elem_ptr->elem_index; + return ir_build_vector_extract_elem(ira, source_instruction, vector_loaded, elem_index); + } + ir_add_error(ira, &ptr->base, + buf_sprintf("unable to determine vector element index of type '%s'", buf_ptr(&ptr_type->name))); + return ira->codegen->invalid_inst_gen; + } + + IrInstGen *result_loc_inst; + if (ptr_type->data.pointer.host_int_bytes != 0 && handle_is_ptr(ira->codegen, child_type)) { + if (result_loc == nullptr) result_loc = no_result_loc(); + result_loc_inst = ir_resolve_result(ira, source_instruction, result_loc, child_type, nullptr, true, true); + if (type_is_invalid(result_loc_inst->value->type) || result_loc_inst->value->type->id == ZigTypeIdUnreachable) { + return result_loc_inst; + } + } else { + result_loc_inst = nullptr; + } + + return ir_build_load_ptr_gen(ira, source_instruction, ptr, child_type, result_loc_inst); +} + +static bool ir_resolve_const_align(CodeGen *codegen, IrExecutableGen *exec, AstNode *source_node, + ZigValue *const_val, uint32_t *out) +{ + Error err; + if ((err = ir_resolve_const_val(codegen, exec, source_node, const_val, UndefBad))) + return false; + + uint32_t align_bytes = bigint_as_u32(&const_val->data.x_bigint); + if (align_bytes == 0) { + exec_add_error_node_gen(codegen, exec, source_node, buf_sprintf("alignment must be >= 1")); + return false; + } + + if (!is_power_of_2(align_bytes)) { + exec_add_error_node_gen(codegen, exec, source_node, + buf_sprintf("alignment value %" PRIu32 " is not a power of 2", align_bytes)); + return false; + } + + *out = align_bytes; + return true; +} + +static bool ir_resolve_align(IrAnalyze *ira, IrInstGen *value, ZigType *elem_type, uint32_t *out) { + if (type_is_invalid(value->value->type)) + return false; + + // Look for this pattern: `*align(@alignOf(T)) T`. + // This can be resolved to be `*out = 0` without resolving any alignment. + if (elem_type != nullptr && value->value->special == ConstValSpecialLazy && + value->value->data.x_lazy->id == LazyValueIdAlignOf) + { + LazyValueAlignOf *lazy_align_of = reinterpret_cast(value->value->data.x_lazy); + + ZigType *lazy_elem_type = ir_resolve_type(lazy_align_of->ira, lazy_align_of->target_type); + if (type_is_invalid(lazy_elem_type)) + return false; + + if (elem_type == lazy_elem_type) { + *out = 0; + return true; + } + } + + IrInstGen *casted_value = ir_implicit_cast(ira, value, get_align_amt_type(ira->codegen)); + if (type_is_invalid(casted_value->value->type)) + return false; + + return ir_resolve_const_align(ira->codegen, ira->new_irb.exec, value->base.source_node, + casted_value->value, out); +} + +static bool ir_resolve_unsigned(IrAnalyze *ira, IrInstGen *value, ZigType *int_type, uint64_t *out) { + if (type_is_invalid(value->value->type)) + return false; + + IrInstGen *casted_value = ir_implicit_cast(ira, value, int_type); + if (type_is_invalid(casted_value->value->type)) + return false; + + ZigValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); + if (!const_val) + return false; + + *out = bigint_as_u64(&const_val->data.x_bigint); + return true; +} + +static bool ir_resolve_usize(IrAnalyze *ira, IrInstGen *value, uint64_t *out) { + return ir_resolve_unsigned(ira, value, ira->codegen->builtin_types.entry_usize, out); +} + +static bool ir_resolve_bool(IrAnalyze *ira, IrInstGen *value, bool *out) { + if (type_is_invalid(value->value->type)) + return false; + + IrInstGen *casted_value = ir_implicit_cast(ira, value, ira->codegen->builtin_types.entry_bool); + if (type_is_invalid(casted_value->value->type)) + return false; + + ZigValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); + if (!const_val) + return false; + + *out = const_val->data.x_bool; + return true; +} + +static bool ir_resolve_comptime(IrAnalyze *ira, IrInstGen *value, bool *out) { + if (!value) { + *out = false; + return true; + } + return ir_resolve_bool(ira, value, out); +} + +static bool ir_resolve_atomic_order(IrAnalyze *ira, IrInstGen *value, AtomicOrder *out) { + if (type_is_invalid(value->value->type)) + return false; + + ZigType *atomic_order_type = get_builtin_type(ira->codegen, "AtomicOrder"); + + IrInstGen *casted_value = ir_implicit_cast(ira, value, atomic_order_type); + if (type_is_invalid(casted_value->value->type)) + return false; + + ZigValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); + if (!const_val) + return false; + + *out = (AtomicOrder)bigint_as_u32(&const_val->data.x_enum_tag); + return true; +} + +static bool ir_resolve_atomic_rmw_op(IrAnalyze *ira, IrInstGen *value, AtomicRmwOp *out) { + if (type_is_invalid(value->value->type)) + return false; + + ZigType *atomic_rmw_op_type = get_builtin_type(ira->codegen, "AtomicRmwOp"); + + IrInstGen *casted_value = ir_implicit_cast(ira, value, atomic_rmw_op_type); + if (type_is_invalid(casted_value->value->type)) + return false; + + ZigValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); + if (!const_val) + return false; + + *out = (AtomicRmwOp)bigint_as_u32(&const_val->data.x_enum_tag); + return true; +} + +static bool ir_resolve_global_linkage(IrAnalyze *ira, IrInstGen *value, GlobalLinkageId *out) { + if (type_is_invalid(value->value->type)) + return false; + + ZigType *global_linkage_type = get_builtin_type(ira->codegen, "GlobalLinkage"); + + IrInstGen *casted_value = ir_implicit_cast(ira, value, global_linkage_type); + if (type_is_invalid(casted_value->value->type)) + return false; + + ZigValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); + if (!const_val) + return false; + + *out = (GlobalLinkageId)bigint_as_u32(&const_val->data.x_enum_tag); + return true; +} + +static bool ir_resolve_float_mode(IrAnalyze *ira, IrInstGen *value, FloatMode *out) { + if (type_is_invalid(value->value->type)) + return false; + + ZigType *float_mode_type = get_builtin_type(ira->codegen, "FloatMode"); + + IrInstGen *casted_value = ir_implicit_cast(ira, value, float_mode_type); + if (type_is_invalid(casted_value->value->type)) + return false; + + ZigValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); + if (!const_val) + return false; + + *out = (FloatMode)bigint_as_u32(&const_val->data.x_enum_tag); + return true; +} + +static Buf *ir_resolve_str(IrAnalyze *ira, IrInstGen *value) { + if (type_is_invalid(value->value->type)) + return nullptr; + + ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, + true, false, PtrLenUnknown, 0, 0, 0, false); + ZigType *str_type = get_slice_type(ira->codegen, ptr_type); + IrInstGen *casted_value = ir_implicit_cast(ira, value, str_type); + if (type_is_invalid(casted_value->value->type)) + return nullptr; + + ZigValue *const_val = ir_resolve_const(ira, casted_value, UndefBad); + if (!const_val) + return nullptr; + + ZigValue *ptr_field = const_val->data.x_struct.fields[slice_ptr_index]; + ZigValue *len_field = const_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; + expand_undef_array(ira->codegen, array_val); + size_t len = bigint_as_usize(&len_field->data.x_bigint); + if (array_val->data.x_array.special == ConstArraySpecialBuf && len == buf_len(array_val->data.x_array.data.s_buf)) { + return array_val->data.x_array.data.s_buf; + } + 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) { + ir_add_error(ira, &casted_value->base, buf_sprintf("use of undefined value")); + return nullptr; + } + 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; + } + return result; +} + +static IrInstGen *ir_analyze_instruction_add_implicit_return_type(IrAnalyze *ira, + IrInstSrcAddImplicitReturnType *instruction) +{ + IrInstGen *value = instruction->value->child; + if (type_is_invalid(value->value->type)) + return ir_unreach_error(ira); + + if (instruction->result_loc_ret == nullptr || !instruction->result_loc_ret->implicit_return_type_done) { + ira->src_implicit_return_type_list.append(value); + } + + return ir_const_void(ira, &instruction->base.base); +} + +static IrInstGen *ir_analyze_instruction_return(IrAnalyze *ira, IrInstSrcReturn *instruction) { + if (instruction->operand == nullptr) { + // result location mechanism took care of it. + IrInstGen *result = ir_build_return_gen(ira, &instruction->base.base, nullptr); + return ir_finish_anal(ira, result); + } + + IrInstGen *operand = instruction->operand->child; + if (type_is_invalid(operand->value->type)) + return ir_unreach_error(ira); + + IrInstGen *casted_operand = ir_implicit_cast(ira, operand, ira->explicit_return_type); + if (type_is_invalid(casted_operand->value->type)) { + AstNode *source_node = ira->explicit_return_type_source_node; + if (source_node != nullptr) { + ErrorMsg *msg = ira->codegen->errors.last(); + add_error_note(ira->codegen, msg, source_node, + buf_sprintf("return type declared here")); + } + return ir_unreach_error(ira); + } + + if (!instr_is_comptime(operand) && ira->explicit_return_type != nullptr && + handle_is_ptr(ira->codegen, ira->explicit_return_type)) + { + // result location mechanism took care of it. + IrInstGen *result = ir_build_return_gen(ira, &instruction->base.base, nullptr); + return ir_finish_anal(ira, result); + } + + if (casted_operand->value->special == ConstValSpecialRuntime && + casted_operand->value->type->id == ZigTypeIdPointer && + casted_operand->value->data.rh_ptr == RuntimeHintPtrStack) + { + ir_add_error(ira, &instruction->operand->base, buf_sprintf("function returns address of local variable")); + return ir_unreach_error(ira); + } + + IrInstGen *result = ir_build_return_gen(ira, &instruction->base.base, casted_operand); + return ir_finish_anal(ira, result); +} + +static IrInstGen *ir_analyze_instruction_const(IrAnalyze *ira, IrInstSrcConst *instruction) { + return ir_const_move(ira, &instruction->base.base, instruction->value); +} + +static IrInstGen *ir_analyze_bin_op_bool(IrAnalyze *ira, IrInstSrcBinOp *bin_op_instruction) { + IrInstGen *op1 = bin_op_instruction->op1->child; + if (type_is_invalid(op1->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *op2 = bin_op_instruction->op2->child; + if (type_is_invalid(op2->value->type)) + return ira->codegen->invalid_inst_gen; + + ZigType *bool_type = ira->codegen->builtin_types.entry_bool; + + IrInstGen *casted_op1 = ir_implicit_cast(ira, op1, bool_type); + if (type_is_invalid(casted_op1->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *casted_op2 = ir_implicit_cast(ira, op2, bool_type); + if (type_is_invalid(casted_op2->value->type)) + return ira->codegen->invalid_inst_gen; + + if (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2)) { + ZigValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad); + if (op1_val == nullptr) + return ira->codegen->invalid_inst_gen; + + ZigValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); + if (op2_val == nullptr) + return ira->codegen->invalid_inst_gen; + + assert(casted_op1->value->type->id == ZigTypeIdBool); + assert(casted_op2->value->type->id == ZigTypeIdBool); + bool result_bool; + if (bin_op_instruction->op_id == IrBinOpBoolOr) { + result_bool = op1_val->data.x_bool || op2_val->data.x_bool; + } else if (bin_op_instruction->op_id == IrBinOpBoolAnd) { + result_bool = op1_val->data.x_bool && op2_val->data.x_bool; + } else { + zig_unreachable(); + } + return ir_const_bool(ira, &bin_op_instruction->base.base, result_bool); + } + + return ir_build_bin_op_gen(ira, &bin_op_instruction->base.base, bool_type, + bin_op_instruction->op_id, casted_op1, casted_op2, bin_op_instruction->safety_check_on); +} + +static bool resolve_cmp_op_id(IrBinOp op_id, Cmp cmp) { + switch (op_id) { + case IrBinOpCmpEq: + return cmp == CmpEQ; + case IrBinOpCmpNotEq: + return cmp != CmpEQ; + case IrBinOpCmpLessThan: + return cmp == CmpLT; + case IrBinOpCmpGreaterThan: + return cmp == CmpGT; + case IrBinOpCmpLessOrEq: + return cmp != CmpGT; + case IrBinOpCmpGreaterOrEq: + return cmp != CmpLT; + default: + zig_unreachable(); + } +} + +static void set_optional_value_to_null(ZigValue *val) { + assert(val->special == ConstValSpecialStatic); + if (val->type->id == ZigTypeIdNull) return; // nothing to do + assert(val->type->id == ZigTypeIdOptional); + if (get_src_ptr_type(val->type) != nullptr) { + val->data.x_ptr.special = ConstPtrSpecialNull; + } else if (is_opt_err_set(val->type)) { + val->data.x_err_set = nullptr; + } else { + val->data.x_optional = nullptr; + } +} + +static void set_optional_payload(ZigValue *opt_val, ZigValue *payload) { + assert(opt_val->special == ConstValSpecialStatic); + assert(opt_val->type->id == ZigTypeIdOptional); + if (payload == nullptr) { + set_optional_value_to_null(opt_val); + } else if (is_opt_err_set(opt_val->type)) { + assert(payload->type->id == ZigTypeIdErrorSet); + opt_val->data.x_err_set = payload->data.x_err_set; + } else { + opt_val->data.x_optional = payload; + } +} + +static IrInstGen *ir_evaluate_bin_op_cmp(IrAnalyze *ira, ZigType *resolved_type, + ZigValue *op1_val, ZigValue *op2_val, IrInst *source_instr, IrBinOp op_id, + bool one_possible_value) +{ + if (op1_val->special == ConstValSpecialUndef || + op2_val->special == ConstValSpecialUndef) + return ir_const_undef(ira, source_instr, resolved_type); + if (resolved_type->id == ZigTypeIdPointer && op_id != IrBinOpCmpEq && op_id != IrBinOpCmpNotEq) { + if ((op1_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr || + op1_val->data.x_ptr.special == ConstPtrSpecialNull) && + (op2_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr || + op2_val->data.x_ptr.special == ConstPtrSpecialNull)) + { + uint64_t op1_addr = op1_val->data.x_ptr.special == ConstPtrSpecialNull ? + 0 : op1_val->data.x_ptr.data.hard_coded_addr.addr; + uint64_t op2_addr = op2_val->data.x_ptr.special == ConstPtrSpecialNull ? + 0 : op2_val->data.x_ptr.data.hard_coded_addr.addr; + Cmp cmp_result; + if (op1_addr > op2_addr) { + cmp_result = CmpGT; + } else if (op1_addr < op2_addr) { + cmp_result = CmpLT; + } else { + cmp_result = CmpEQ; + } + bool answer = resolve_cmp_op_id(op_id, cmp_result); + return ir_const_bool(ira, source_instr, answer); + } + } else { + bool are_equal = one_possible_value || const_values_equal(ira->codegen, op1_val, op2_val); + bool answer; + if (op_id == IrBinOpCmpEq) { + answer = are_equal; + } else if (op_id == IrBinOpCmpNotEq) { + answer = !are_equal; + } else { + zig_unreachable(); + } + return ir_const_bool(ira, source_instr, answer); + } + zig_unreachable(); +} + +static IrInstGen *ir_try_evaluate_bin_op_cmp_const(IrAnalyze *ira, IrInst *source_instr, IrInstGen *op1, IrInstGen *op2, + ZigType *resolved_type, IrBinOp op_id) +{ + assert(op1->value->type == resolved_type && op2->value->type == resolved_type); + bool one_possible_value; + switch (type_has_one_possible_value(ira->codegen, resolved_type)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_inst_gen; + case OnePossibleValueYes: + one_possible_value = true; + break; + case OnePossibleValueNo: + one_possible_value = false; + break; + } + + if (one_possible_value || (instr_is_comptime(op1) && instr_is_comptime(op2))) { + ZigValue *op1_val = one_possible_value ? op1->value : ir_resolve_const(ira, op1, UndefBad); + if (op1_val == nullptr) + return ira->codegen->invalid_inst_gen; + ZigValue *op2_val = one_possible_value ? op2->value : ir_resolve_const(ira, op2, UndefBad); + if (op2_val == nullptr) + return ira->codegen->invalid_inst_gen; + if (resolved_type->id != ZigTypeIdVector) + return ir_evaluate_bin_op_cmp(ira, resolved_type, op1_val, op2_val, source_instr, op_id, one_possible_value); + IrInstGen *result = ir_const(ira, source_instr, + get_vector_type(ira->codegen, resolved_type->data.vector.len, ira->codegen->builtin_types.entry_bool)); + result->value->data.x_array.data.s_none.elements = + ira->codegen->pass1_arena->allocate(resolved_type->data.vector.len); + + expand_undef_array(ira->codegen, result->value); + for (size_t i = 0;i < resolved_type->data.vector.len;i++) { + IrInstGen *cur_res = ir_evaluate_bin_op_cmp(ira, resolved_type->data.vector.elem_type, + &op1_val->data.x_array.data.s_none.elements[i], + &op2_val->data.x_array.data.s_none.elements[i], + source_instr, op_id, one_possible_value); + copy_const_val(ira->codegen, &result->value->data.x_array.data.s_none.elements[i], cur_res->value); + } + return result; + } else { + return nullptr; + } +} + +// Returns ErrorNotLazy when the value cannot be determined +static Error lazy_cmp_zero(CodeGen *codegen, AstNode *source_node, ZigValue *val, Cmp *result) { + Error err; + + switch (type_has_one_possible_value(codegen, val->type)) { + case OnePossibleValueInvalid: + return ErrorSemanticAnalyzeFail; + case OnePossibleValueNo: + break; + case OnePossibleValueYes: + switch (val->type->id) { + case ZigTypeIdInt: + src_assert(val->type->data.integral.bit_count == 0, source_node); + *result = CmpEQ; + return ErrorNone; + case ZigTypeIdUndefined: + return ErrorNotLazy; + default: + zig_unreachable(); + } + } + + switch (val->special) { + case ConstValSpecialRuntime: + case ConstValSpecialUndef: + return ErrorNotLazy; + case ConstValSpecialStatic: + switch (val->type->id) { + case ZigTypeIdComptimeInt: + case ZigTypeIdInt: + *result = bigint_cmp_zero(&val->data.x_bigint); + return ErrorNone; + case ZigTypeIdComptimeFloat: + case ZigTypeIdFloat: + if (float_is_nan(val)) + return ErrorNotLazy; + *result = float_cmp_zero(val); + return ErrorNone; + default: + return ErrorNotLazy; + } + case ConstValSpecialLazy: + switch (val->data.x_lazy->id) { + case LazyValueIdInvalid: + zig_unreachable(); + case LazyValueIdAlignOf: { + LazyValueAlignOf *lazy_align_of = reinterpret_cast(val->data.x_lazy); + IrAnalyze *ira = lazy_align_of->ira; + + bool is_zero_bits; + if ((err = type_val_resolve_zero_bits(ira->codegen, lazy_align_of->target_type->value, + nullptr, nullptr, &is_zero_bits))) + { + return err; + } + + *result = is_zero_bits ? CmpEQ : CmpGT; + return ErrorNone; + } + case LazyValueIdSizeOf: { + LazyValueSizeOf *lazy_size_of = reinterpret_cast(val->data.x_lazy); + IrAnalyze *ira = lazy_size_of->ira; + bool is_zero_bits; + if ((err = type_val_resolve_zero_bits(ira->codegen, lazy_size_of->target_type->value, + nullptr, nullptr, &is_zero_bits))) + { + return err; + } + *result = is_zero_bits ? CmpEQ : CmpGT; + return ErrorNone; + } + default: + return ErrorNotLazy; + } + } + zig_unreachable(); +} + +static ErrorMsg *ir_eval_bin_op_cmp_scalar(IrAnalyze *ira, IrInst* source_instr, + ZigValue *op1_val, IrBinOp op_id, ZigValue *op2_val, ZigValue *out_val) +{ + Error err; + { + // Before resolving the values, we special case comparisons against zero. These can often + // be done without resolving lazy values, preventing potential dependency loops. + Cmp op1_cmp_zero; + if ((err = lazy_cmp_zero(ira->codegen, source_instr->source_node, op1_val, &op1_cmp_zero))) { + if (err == ErrorNotLazy) goto never_mind_just_calculate_it_normally; + return ira->codegen->trace_err; + } + Cmp op2_cmp_zero; + if ((err = lazy_cmp_zero(ira->codegen, source_instr->source_node, op2_val, &op2_cmp_zero))) { + if (err == ErrorNotLazy) goto never_mind_just_calculate_it_normally; + return ira->codegen->trace_err; + } + bool can_cmp_zero = false; + Cmp cmp_result; + if (op1_cmp_zero == CmpEQ && op2_cmp_zero == CmpEQ) { + can_cmp_zero = true; + cmp_result = CmpEQ; + } else if (op1_cmp_zero == CmpGT && op2_cmp_zero == CmpEQ) { + can_cmp_zero = true; + cmp_result = CmpGT; + } else if (op1_cmp_zero == CmpEQ && op2_cmp_zero == CmpGT) { + can_cmp_zero = true; + cmp_result = CmpLT; + } else if (op1_cmp_zero == CmpLT && op2_cmp_zero == CmpEQ) { + can_cmp_zero = true; + cmp_result = CmpLT; + } else if (op1_cmp_zero == CmpEQ && op2_cmp_zero == CmpLT) { + can_cmp_zero = true; + cmp_result = CmpGT; + } else if (op1_cmp_zero == CmpLT && op2_cmp_zero == CmpGT) { + can_cmp_zero = true; + cmp_result = CmpLT; + } else if (op1_cmp_zero == CmpGT && op2_cmp_zero == CmpLT) { + can_cmp_zero = true; + cmp_result = CmpGT; + } + if (can_cmp_zero) { + bool answer = resolve_cmp_op_id(op_id, cmp_result); + out_val->special = ConstValSpecialStatic; + out_val->data.x_bool = answer; + return nullptr; + } + } +never_mind_just_calculate_it_normally: + + if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, source_instr->source_node, + op1_val, UndefOk))) + { + return ira->codegen->trace_err; + } + if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, source_instr->source_node, + op2_val, UndefOk))) + { + return ira->codegen->trace_err; + } + + + if (op1_val->special == ConstValSpecialUndef || op2_val->special == ConstValSpecialUndef || + op1_val->type->id == ZigTypeIdUndefined || op2_val->type->id == ZigTypeIdUndefined) + { + out_val->special = ConstValSpecialUndef; + return nullptr; + } + + bool op1_is_float = op1_val->type->id == ZigTypeIdFloat || op1_val->type->id == ZigTypeIdComptimeFloat; + bool op2_is_float = op2_val->type->id == ZigTypeIdFloat || op2_val->type->id == ZigTypeIdComptimeFloat; + if (op1_is_float && op2_is_float) { + if (float_is_nan(op1_val) || float_is_nan(op2_val)) { + out_val->special = ConstValSpecialStatic; + out_val->data.x_bool = op_id == IrBinOpCmpNotEq; + return nullptr; + } + if (op1_val->type->id == ZigTypeIdComptimeFloat) { + IrInstGen *tmp = ir_const_noval(ira, source_instr); + tmp->value = op1_val; + IrInstGen *casted = ir_implicit_cast(ira, tmp, op2_val->type); + op1_val = casted->value; + } else if (op2_val->type->id == ZigTypeIdComptimeFloat) { + IrInstGen *tmp = ir_const_noval(ira, source_instr); + tmp->value = op2_val; + IrInstGen *casted = ir_implicit_cast(ira, tmp, op1_val->type); + op2_val = casted->value; + } + Cmp cmp_result = float_cmp(op1_val, op2_val); + out_val->special = ConstValSpecialStatic; + out_val->data.x_bool = resolve_cmp_op_id(op_id, cmp_result); + return nullptr; + } + + bool op1_is_int = op1_val->type->id == ZigTypeIdInt || op1_val->type->id == ZigTypeIdComptimeInt; + bool op2_is_int = op2_val->type->id == ZigTypeIdInt || op2_val->type->id == ZigTypeIdComptimeInt; + + if (op1_is_int && op2_is_int) { + Cmp cmp_result = bigint_cmp(&op1_val->data.x_bigint, &op2_val->data.x_bigint); + out_val->special = ConstValSpecialStatic; + out_val->data.x_bool = resolve_cmp_op_id(op_id, cmp_result); + + return nullptr; + } + + // Handle the case where one of the two operands is a fp value and the other + // is an integer value + ZigValue *float_val; + if (op1_is_int && op2_is_float) { + float_val = op2_val; + } else if (op1_is_float && op2_is_int) { + float_val = op1_val; + } else { + zig_unreachable(); + } + + // They can never be equal if the fp value has a non-zero decimal part + if (op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq) { + if (float_has_fraction(float_val)) { + out_val->special = ConstValSpecialStatic; + out_val->data.x_bool = op_id == IrBinOpCmpNotEq; + return nullptr; + } + } + + // Cast the integer operand into a fp value to perform the comparison + BigFloat op1_bigfloat; + BigFloat op2_bigfloat; + value_to_bigfloat(&op1_bigfloat, op1_val); + value_to_bigfloat(&op2_bigfloat, op2_val); + + Cmp cmp_result = bigfloat_cmp(&op1_bigfloat, &op2_bigfloat); + out_val->special = ConstValSpecialStatic; + out_val->data.x_bool = resolve_cmp_op_id(op_id, cmp_result); + + return nullptr; +} + +static IrInstGen *ir_analyze_bin_op_cmp_numeric(IrAnalyze *ira, IrInst *source_instr, + IrInstGen *op1, IrInstGen *op2, IrBinOp op_id) +{ + Error err; + + ZigType *scalar_result_type = ira->codegen->builtin_types.entry_bool; + ZigType *result_type = scalar_result_type; + ZigType *op1_scalar_type = op1->value->type; + ZigType *op2_scalar_type = op2->value->type; + if (op1->value->type->id == ZigTypeIdVector && op2->value->type->id == ZigTypeIdVector) { + if (op1->value->type->data.vector.len != op2->value->type->data.vector.len) { + ir_add_error(ira, source_instr, + buf_sprintf("vector length mismatch: %" PRIu64 " and %" PRIu64, + op1->value->type->data.vector.len, op2->value->type->data.vector.len)); + return ira->codegen->invalid_inst_gen; + } + result_type = get_vector_type(ira->codegen, op1->value->type->data.vector.len, scalar_result_type); + op1_scalar_type = op1->value->type->data.vector.elem_type; + op2_scalar_type = op2->value->type->data.vector.elem_type; + } else if (op1->value->type->id == ZigTypeIdVector || op2->value->type->id == ZigTypeIdVector) { + ir_add_error(ira, source_instr, + buf_sprintf("mixed scalar and vector operands to comparison operator: '%s' and '%s'", + buf_ptr(&op1->value->type->name), buf_ptr(&op2->value->type->name))); + return ira->codegen->invalid_inst_gen; + } + + bool opv_op1; + switch (type_has_one_possible_value(ira->codegen, op1->value->type)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_inst_gen; + case OnePossibleValueYes: + opv_op1 = true; + break; + case OnePossibleValueNo: + opv_op1 = false; + break; + } + bool opv_op2; + switch (type_has_one_possible_value(ira->codegen, op2->value->type)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_inst_gen; + case OnePossibleValueYes: + opv_op2 = true; + break; + case OnePossibleValueNo: + opv_op2 = false; + break; + } + Cmp op1_cmp_zero; + bool have_op1_cmp_zero = false; + if ((err = lazy_cmp_zero(ira->codegen, source_instr->source_node, op1->value, &op1_cmp_zero))) { + if (err != ErrorNotLazy) return ira->codegen->invalid_inst_gen; + } else { + have_op1_cmp_zero = true; + } + Cmp op2_cmp_zero; + bool have_op2_cmp_zero = false; + if ((err = lazy_cmp_zero(ira->codegen, source_instr->source_node, op2->value, &op2_cmp_zero))) { + if (err != ErrorNotLazy) return ira->codegen->invalid_inst_gen; + } else { + have_op2_cmp_zero = true; + } + if (((opv_op1 || instr_is_comptime(op1)) && (opv_op2 || instr_is_comptime(op2))) || + (have_op1_cmp_zero && have_op2_cmp_zero)) + { + IrInstGen *result_instruction = ir_const(ira, source_instr, result_type); + ZigValue *out_val = result_instruction->value; + if (result_type->id == ZigTypeIdVector) { + size_t len = result_type->data.vector.len; + expand_undef_array(ira->codegen, op1->value); + expand_undef_array(ira->codegen, op2->value); + out_val->special = ConstValSpecialUndef; + expand_undef_array(ira->codegen, out_val); + for (size_t i = 0; i < len; i += 1) { + ZigValue *scalar_op1_val = &op1->value->data.x_array.data.s_none.elements[i]; + ZigValue *scalar_op2_val = &op2->value->data.x_array.data.s_none.elements[i]; + ZigValue *scalar_out_val = &out_val->data.x_array.data.s_none.elements[i]; + assert(scalar_out_val->type == scalar_result_type); + ErrorMsg *msg = ir_eval_bin_op_cmp_scalar(ira, source_instr, + scalar_op1_val, op_id, scalar_op2_val, scalar_out_val); + if (msg != nullptr) { + add_error_note(ira->codegen, msg, source_instr->source_node, + buf_sprintf("when computing vector element at index %" ZIG_PRI_usize, i)); + return ira->codegen->invalid_inst_gen; + } + } + out_val->type = result_type; + out_val->special = ConstValSpecialStatic; + } else { + if (ir_eval_bin_op_cmp_scalar(ira, source_instr, op1->value, op_id, + op2->value, out_val) != nullptr) + { + return ira->codegen->invalid_inst_gen; + } + } + return result_instruction; + } + + // If one operand has a comptime-known comparison with 0, and the other operand is unsigned, we might + // know the answer, depending on the operator. + // TODO make this work with vectors + if (have_op1_cmp_zero && op2_scalar_type->id == ZigTypeIdInt && !op2_scalar_type->data.integral.is_signed) { + if (op1_cmp_zero == CmpEQ) { + // 0 <= unsigned_x // true + // 0 > unsigned_x // false + switch (op_id) { + case IrBinOpCmpLessOrEq: + return ir_const_bool(ira, source_instr, true); + case IrBinOpCmpGreaterThan: + return ir_const_bool(ira, source_instr, false); + default: + break; + } + } else if (op1_cmp_zero == CmpLT) { + // -1 != unsigned_x // true + // -1 <= unsigned_x // true + // -1 < unsigned_x // true + // -1 == unsigned_x // false + // -1 >= unsigned_x // false + // -1 > unsigned_x // false + switch (op_id) { + case IrBinOpCmpNotEq: + case IrBinOpCmpLessOrEq: + case IrBinOpCmpLessThan: + return ir_const_bool(ira, source_instr, true); + case IrBinOpCmpEq: + case IrBinOpCmpGreaterOrEq: + case IrBinOpCmpGreaterThan: + return ir_const_bool(ira, source_instr, false); + default: + break; + } + } + } + if (have_op2_cmp_zero && op1_scalar_type->id == ZigTypeIdInt && !op1_scalar_type->data.integral.is_signed) { + if (op2_cmp_zero == CmpEQ) { + // unsigned_x < 0 // false + // unsigned_x >= 0 // true + switch (op_id) { + case IrBinOpCmpLessThan: + return ir_const_bool(ira, source_instr, false); + case IrBinOpCmpGreaterOrEq: + return ir_const_bool(ira, source_instr, true); + default: + break; + } + } else if (op2_cmp_zero == CmpLT) { + // unsigned_x != -1 // true + // unsigned_x >= -1 // true + // unsigned_x > -1 // true + // unsigned_x == -1 // false + // unsigned_x < -1 // false + // unsigned_x <= -1 // false + switch (op_id) { + case IrBinOpCmpNotEq: + case IrBinOpCmpGreaterOrEq: + case IrBinOpCmpGreaterThan: + return ir_const_bool(ira, source_instr, true); + case IrBinOpCmpEq: + case IrBinOpCmpLessThan: + case IrBinOpCmpLessOrEq: + return ir_const_bool(ira, source_instr, false); + default: + break; + } + } + } + + // It must be a runtime comparison. + // For floats, emit a float comparison instruction. + bool op1_is_float = op1_scalar_type->id == ZigTypeIdFloat || op1_scalar_type->id == ZigTypeIdComptimeFloat; + bool op2_is_float = op2_scalar_type->id == ZigTypeIdFloat || op2_scalar_type->id == ZigTypeIdComptimeFloat; + if (op1_is_float && op2_is_float) { + // Implicit cast the smaller one to the larger one. + ZigType *dest_scalar_type; + if (op1_scalar_type->id == ZigTypeIdComptimeFloat) { + dest_scalar_type = op2_scalar_type; + } else if (op2_scalar_type->id == ZigTypeIdComptimeFloat) { + dest_scalar_type = op1_scalar_type; + } else if (op1_scalar_type->data.floating.bit_count >= op2_scalar_type->data.floating.bit_count) { + dest_scalar_type = op1_scalar_type; + } else { + dest_scalar_type = op2_scalar_type; + } + ZigType *dest_type = (result_type->id == ZigTypeIdVector) ? + get_vector_type(ira->codegen, result_type->data.vector.len, dest_scalar_type) : dest_scalar_type; + IrInstGen *casted_op1 = ir_implicit_cast(ira, op1, dest_type); + IrInstGen *casted_op2 = ir_implicit_cast(ira, op2, dest_type); + if (type_is_invalid(casted_op1->value->type) || type_is_invalid(casted_op2->value->type)) + return ira->codegen->invalid_inst_gen; + return ir_build_bin_op_gen(ira, source_instr, result_type, op_id, casted_op1, casted_op2, true); + } + + // 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. + bool dest_int_is_signed = false; + if (have_op1_cmp_zero) { + if (op1_cmp_zero == CmpLT) dest_int_is_signed = true; + } else if (op1_is_float) { + dest_int_is_signed = true; + } else if (op1_scalar_type->id == ZigTypeIdInt && op1_scalar_type->data.integral.is_signed) { + dest_int_is_signed = true; + } + if (have_op2_cmp_zero) { + if (op2_cmp_zero == CmpLT) dest_int_is_signed = true; + } else if (op2_is_float) { + dest_int_is_signed = true; + } else if (op2->value->type->id == ZigTypeIdInt && op2->value->type->data.integral.is_signed) { + dest_int_is_signed = true; + } + ZigType *dest_float_type = nullptr; + uint32_t op1_bits; + if (instr_is_comptime(op1) && result_type->id != ZigTypeIdVector) { + ZigValue *op1_val = ir_resolve_const(ira, op1, UndefOk); + if (op1_val == nullptr) + return ira->codegen->invalid_inst_gen; + if (op1_val->special == ConstValSpecialUndef) + return ir_const_undef(ira, source_instr, ira->codegen->builtin_types.entry_bool); + bool is_unsigned; + if (op1_is_float) { + BigInt bigint = {}; + float_init_bigint(&bigint, op1_val); + Cmp zcmp = float_cmp_zero(op1_val); + if (float_has_fraction(op1_val)) { + if (op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq) { + return ir_const_bool(ira, source_instr, op_id == IrBinOpCmpNotEq); + } + if (zcmp == CmpLT) { + bigint_decr(&bigint); + } else { + bigint_incr(&bigint); + } + } + op1_bits = bigint_bits_needed(&bigint); + is_unsigned = zcmp != CmpLT; + } else { + op1_bits = bigint_bits_needed(&op1_val->data.x_bigint); + is_unsigned = bigint_cmp_zero(&op1_val->data.x_bigint) != CmpLT; + } + if (is_unsigned && dest_int_is_signed) { + op1_bits += 1; + } + } else if (op1_is_float) { + ir_assert(op1_scalar_type->id == ZigTypeIdFloat, source_instr); + dest_float_type = op1_scalar_type; + } else { + ir_assert(op1_scalar_type->id == ZigTypeIdInt, source_instr); + op1_bits = op1_scalar_type->data.integral.bit_count; + if (!op1_scalar_type->data.integral.is_signed && dest_int_is_signed) { + op1_bits += 1; + } + } + uint32_t op2_bits; + if (instr_is_comptime(op2) && result_type->id != ZigTypeIdVector) { + ZigValue *op2_val = ir_resolve_const(ira, op2, UndefOk); + if (op2_val == nullptr) + return ira->codegen->invalid_inst_gen; + if (op2_val->special == ConstValSpecialUndef) + return ir_const_undef(ira, source_instr, ira->codegen->builtin_types.entry_bool); + bool is_unsigned; + if (op2_is_float) { + BigInt bigint = {}; + float_init_bigint(&bigint, op2_val); + Cmp zcmp = float_cmp_zero(op2_val); + if (float_has_fraction(op2_val)) { + if (op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq) { + return ir_const_bool(ira, source_instr, op_id == IrBinOpCmpNotEq); + } + if (zcmp == CmpLT) { + bigint_decr(&bigint); + } else { + bigint_incr(&bigint); + } + } + op2_bits = bigint_bits_needed(&bigint); + is_unsigned = zcmp != CmpLT; + } else { + op2_bits = bigint_bits_needed(&op2_val->data.x_bigint); + is_unsigned = bigint_cmp_zero(&op2_val->data.x_bigint) != CmpLT; + } + if (is_unsigned && dest_int_is_signed) { + op2_bits += 1; + } + } else if (op2_is_float) { + ir_assert(op2_scalar_type->id == ZigTypeIdFloat, source_instr); + dest_float_type = op2_scalar_type; + } else { + ir_assert(op2_scalar_type->id == ZigTypeIdInt, source_instr); + op2_bits = op2_scalar_type->data.integral.bit_count; + if (!op2_scalar_type->data.integral.is_signed && dest_int_is_signed) { + op2_bits += 1; + } + } + ZigType *dest_scalar_type = (dest_float_type == nullptr) ? + get_int_type(ira->codegen, dest_int_is_signed, (op1_bits > op2_bits) ? op1_bits : op2_bits) : + dest_float_type; + ZigType *dest_type = (result_type->id == ZigTypeIdVector) ? + get_vector_type(ira->codegen, result_type->data.vector.len, dest_scalar_type) : dest_scalar_type; + + IrInstGen *casted_op1 = ir_implicit_cast(ira, op1, dest_type); + if (type_is_invalid(casted_op1->value->type)) + return ira->codegen->invalid_inst_gen; + IrInstGen *casted_op2 = ir_implicit_cast(ira, op2, dest_type); + if (type_is_invalid(casted_op2->value->type)) + return ira->codegen->invalid_inst_gen; + return ir_build_bin_op_gen(ira, source_instr, result_type, op_id, casted_op1, casted_op2, true); +} + +static bool type_is_self_comparable(ZigType *ty, bool is_equality_cmp) { + if (type_is_numeric(ty)) { + return true; + } + switch (ty->id) { + case ZigTypeIdInvalid: + zig_unreachable(); + + case ZigTypeIdComptimeFloat: + case ZigTypeIdComptimeInt: + case ZigTypeIdInt: + case ZigTypeIdFloat: + zig_unreachable(); // handled with the type_is_numeric check above + + case ZigTypeIdVector: + // Not every case is handled by the type_is_numeric check above, + // vectors of bool trigger this code path + case ZigTypeIdBool: + case ZigTypeIdMetaType: + case ZigTypeIdVoid: + case ZigTypeIdErrorSet: + case ZigTypeIdFn: + case ZigTypeIdOpaque: + case ZigTypeIdBoundFn: + case ZigTypeIdEnum: + case ZigTypeIdEnumLiteral: + case ZigTypeIdAnyFrame: + return is_equality_cmp; + + case ZigTypeIdPointer: + return is_equality_cmp || (ty->data.pointer.ptr_len == PtrLenC); + + case ZigTypeIdUnreachable: + case ZigTypeIdArray: + case ZigTypeIdStruct: + case ZigTypeIdUndefined: + case ZigTypeIdNull: + case ZigTypeIdErrorUnion: + case ZigTypeIdUnion: + case ZigTypeIdFnFrame: + return false; + + case ZigTypeIdOptional: + return is_equality_cmp && get_src_ptr_type(ty) != nullptr; + } + zig_unreachable(); +} + +static IrInstGen *ir_try_evaluate_cmp_optional_non_optional_const(IrAnalyze *ira, IrInst *source_instr, ZigType *child_type, + IrInstGen *optional, IrInstGen *non_optional, IrBinOp op_id) +{ + assert(optional->value->type->id == ZigTypeIdOptional); + assert(optional->value->type->data.maybe.child_type == non_optional->value->type); + assert(non_optional->value->type == child_type); + assert(op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq); + + if (instr_is_comptime(optional) && instr_is_comptime(non_optional)) { + ZigValue *optional_val = ir_resolve_const(ira, optional, UndefBad); + if (!optional_val) { + return ira->codegen->invalid_inst_gen; + } + + ZigValue *non_optional_val = ir_resolve_const(ira, non_optional, UndefBad); + if (!non_optional_val) { + return ira->codegen->invalid_inst_gen; + } + + if (!optional_value_is_null(optional_val)) { + IrInstGen *optional_unwrapped = ir_analyze_optional_value_payload_value(ira, source_instr, optional, false); + if (type_is_invalid(optional_unwrapped->value->type)) { + return ira->codegen->invalid_inst_gen; + } + + IrInstGen *ret = ir_try_evaluate_bin_op_cmp_const(ira, source_instr, optional_unwrapped, non_optional, child_type, op_id); + assert(ret != nullptr); + return ret; + } + return ir_const_bool(ira, source_instr, (op_id != IrBinOpCmpEq)); + } else { + return nullptr; + } +} + +static IrInstGen *ir_evaluate_cmp_optional_non_optional(IrAnalyze *ira, IrInst *source_instr, ZigType *child_type, + IrInstGen *optional, IrInstGen *non_optional, IrBinOp op_id) +{ + assert(optional->value->type->id == ZigTypeIdOptional); + assert(optional->value->type->data.maybe.child_type == non_optional->value->type); + assert(non_optional->value->type == child_type); + assert(op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq); + + ZigType *result_type = ira->codegen->builtin_types.entry_bool; + ir_append_basic_block_gen(&ira->new_irb, ira->new_irb.current_basic_block); + + IrBasicBlockGen *null_block = ir_create_basic_block_gen(ira, source_instr->scope, "CmpOptionalNonOptionalOptionalNull"); + IrBasicBlockGen *non_null_block = ir_create_basic_block_gen(ira, source_instr->scope, "CmpOptionalNonOptionalOptionalNotNull"); + IrBasicBlockGen *end_block = ir_create_basic_block_gen(ira, source_instr->scope, "CmpOptionalNonOptionalEnd"); + + IrInstGen *is_non_null = ir_build_test_non_null_gen(ira, source_instr, optional); + ir_build_cond_br_gen(ira, source_instr, is_non_null, non_null_block, null_block); + + ir_set_cursor_at_end_and_append_block_gen(&ira->new_irb, non_null_block); + IrInstGen *optional_unwrapped = ir_analyze_optional_value_payload_value(ira, source_instr, optional, false); + if (type_is_invalid(optional_unwrapped->value->type)) { + return ira->codegen->invalid_inst_gen; + } + IrInstGen *non_null_cmp_result = ir_build_bin_op_gen(ira, source_instr, result_type, op_id, + optional_unwrapped, non_optional, false); // safety check unnecessary for comparison operators + ir_build_br_gen(ira, source_instr, end_block); + + + ir_set_cursor_at_end_and_append_block_gen(&ira->new_irb, null_block); + IrInstGen *null_result = ir_const_bool(ira, source_instr, (op_id != IrBinOpCmpEq)); + ir_build_br_gen(ira, source_instr, end_block); + + ir_set_cursor_at_end_gen(&ira->new_irb, end_block); + int incoming_count = 2; + IrBasicBlockGen **incoming_blocks = heap::c_allocator.allocate_nonzero(incoming_count); + incoming_blocks[0] = null_block; + incoming_blocks[1] = non_null_block; + IrInstGen **incoming_values = heap::c_allocator.allocate_nonzero(incoming_count); + incoming_values[0] = null_result; + incoming_values[1] = non_null_cmp_result; + + return ir_build_phi_gen(ira, source_instr, incoming_count, incoming_blocks, incoming_values, result_type); +} + +static IrInstGen *ir_analyze_cmp_optional_non_optional(IrAnalyze *ira, IrInst *source_instr, + IrInstGen *op1, IrInstGen *op2, IrInstGen *optional, IrBinOp op_id) +{ + assert(op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq); + assert(optional->value->type->id == ZigTypeIdOptional); + assert(get_src_ptr_type(optional->value->type) == nullptr); + + IrInstGen *non_optional; + if (op1 == optional) { + non_optional = op2; + } else if (op2 == optional) { + non_optional = op1; + } else { + zig_unreachable(); + } + + ZigType *child_type = optional->value->type->data.maybe.child_type; + bool child_type_matches = (child_type == non_optional->value->type); + if (!child_type_matches || !type_is_self_comparable(child_type, true)) { + ErrorMsg *msg = ir_add_error_node(ira, source_instr->source_node, buf_sprintf("cannot compare types '%s' and '%s'", + buf_ptr(&op1->value->type->name), + buf_ptr(&op2->value->type->name))); + + if (!child_type_matches) { + if (non_optional->value->type->id == ZigTypeIdOptional) { + add_error_note(ira->codegen, msg, source_instr->source_node, buf_sprintf( + "optional to optional comparison is only supported for optional pointer types")); + } else { + add_error_note(ira->codegen, msg, source_instr->source_node, + buf_sprintf("optional child type '%s' must be the same as non-optional type '%s'", + buf_ptr(&child_type->name), + buf_ptr(&non_optional->value->type->name))); + } + } else { + add_error_note(ira->codegen, msg, source_instr->source_node, + buf_sprintf("operator not supported for type '%s'", + buf_ptr(&child_type->name))); + } + return ira->codegen->invalid_inst_gen; + } + + if (child_type->id == ZigTypeIdVector) { + ir_add_error_node(ira, source_instr->source_node, buf_sprintf("TODO add comparison of optional vector")); + return ira->codegen->invalid_inst_gen; + } + + if (IrInstGen *const_result = ir_try_evaluate_cmp_optional_non_optional_const(ira, source_instr, child_type, + optional, non_optional, op_id)) + { + return const_result; + } + + return ir_evaluate_cmp_optional_non_optional(ira, source_instr, child_type, optional, non_optional, op_id); +} + +static IrInstGen *ir_analyze_bin_op_cmp(IrAnalyze *ira, IrInstSrcBinOp *bin_op_instruction) { + IrInstGen *op1 = bin_op_instruction->op1->child; + if (type_is_invalid(op1->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *op2 = bin_op_instruction->op2->child; + if (type_is_invalid(op2->value->type)) + return ira->codegen->invalid_inst_gen; + + AstNode *source_node = bin_op_instruction->base.base.source_node; + + IrBinOp op_id = bin_op_instruction->op_id; + bool is_equality_cmp = (op_id == IrBinOpCmpEq || op_id == IrBinOpCmpNotEq); + if (is_equality_cmp && op1->value->type->id == ZigTypeIdNull && op2->value->type->id == ZigTypeIdNull) { + return ir_const_bool(ira, &bin_op_instruction->base.base, (op_id == IrBinOpCmpEq)); + } else if (is_equality_cmp && + ((op1->value->type->id == ZigTypeIdNull && op2->value->type->id == ZigTypeIdOptional) || + (op2->value->type->id == ZigTypeIdNull && op1->value->type->id == ZigTypeIdOptional))) + { + IrInstGen *maybe_op; + if (op1->value->type->id == ZigTypeIdNull) { + maybe_op = op2; + } else if (op2->value->type->id == ZigTypeIdNull) { + maybe_op = op1; + } else { + zig_unreachable(); + } + if (instr_is_comptime(maybe_op)) { + ZigValue *maybe_val = ir_resolve_const(ira, maybe_op, UndefBad); + if (!maybe_val) + return ira->codegen->invalid_inst_gen; + bool is_null = optional_value_is_null(maybe_val); + bool bool_result = (op_id == IrBinOpCmpEq) ? is_null : !is_null; + return ir_const_bool(ira, &bin_op_instruction->base.base, bool_result); + } + + IrInstGen *is_non_null = ir_build_test_non_null_gen(ira, &bin_op_instruction->base.base, maybe_op); + + if (op_id == IrBinOpCmpEq) { + return ir_build_bool_not_gen(ira, &bin_op_instruction->base.base, is_non_null); + } else { + return is_non_null; + } + } else if (is_equality_cmp && + ((op1->value->type->id == ZigTypeIdNull && op2->value->type->id == ZigTypeIdPointer && + op2->value->type->data.pointer.ptr_len == PtrLenC) || + (op2->value->type->id == ZigTypeIdNull && op1->value->type->id == ZigTypeIdPointer && + op1->value->type->data.pointer.ptr_len == PtrLenC))) + { + IrInstGen *c_ptr_op; + if (op1->value->type->id == ZigTypeIdNull) { + c_ptr_op = op2; + } else if (op2->value->type->id == ZigTypeIdNull) { + c_ptr_op = op1; + } else { + zig_unreachable(); + } + if (instr_is_comptime(c_ptr_op)) { + ZigValue *c_ptr_val = ir_resolve_const(ira, c_ptr_op, UndefOk); + if (!c_ptr_val) + return ira->codegen->invalid_inst_gen; + if (c_ptr_val->special == ConstValSpecialUndef) + return ir_const_undef(ira, &bin_op_instruction->base.base, ira->codegen->builtin_types.entry_bool); + bool is_null = c_ptr_val->data.x_ptr.special == ConstPtrSpecialNull || + (c_ptr_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr && + c_ptr_val->data.x_ptr.data.hard_coded_addr.addr == 0); + bool bool_result = (op_id == IrBinOpCmpEq) ? is_null : !is_null; + return ir_const_bool(ira, &bin_op_instruction->base.base, bool_result); + } + IrInstGen *is_non_null = ir_build_test_non_null_gen(ira, &bin_op_instruction->base.base, c_ptr_op); + + if (op_id == IrBinOpCmpEq) { + return ir_build_bool_not_gen(ira, &bin_op_instruction->base.base, is_non_null); + } else { + return is_non_null; + } + } else if (is_equality_cmp && + (op1->value->type->id == ZigTypeIdOptional && get_src_ptr_type(op1->value->type) == nullptr)) + { + return ir_analyze_cmp_optional_non_optional(ira, &bin_op_instruction->base.base, op1, op2, op1, op_id); + } else if(is_equality_cmp && + (op2->value->type->id == ZigTypeIdOptional && get_src_ptr_type(op2->value->type) == nullptr)) + { + return ir_analyze_cmp_optional_non_optional(ira, &bin_op_instruction->base.base, op1, op2, op2, op_id); + } else if (op1->value->type->id == ZigTypeIdNull || op2->value->type->id == ZigTypeIdNull) { + ZigType *non_null_type = (op1->value->type->id == ZigTypeIdNull) ? op2->value->type : op1->value->type; + ir_add_error_node(ira, source_node, buf_sprintf("comparison of '%s' with null", + buf_ptr(&non_null_type->name))); + return ira->codegen->invalid_inst_gen; + } else if (is_equality_cmp && ( + (op1->value->type->id == ZigTypeIdEnumLiteral && op2->value->type->id == ZigTypeIdUnion) || + (op2->value->type->id == ZigTypeIdEnumLiteral && op1->value->type->id == ZigTypeIdUnion))) + { + // Support equality comparison between a union's tag value and a enum literal + IrInstGen *union_val = op1->value->type->id == ZigTypeIdUnion ? op1 : op2; + IrInstGen *enum_val = op1->value->type->id == ZigTypeIdUnion ? op2 : op1; + + if (!is_tagged_union(union_val->value->type)) { + ErrorMsg *msg = ir_add_error_node(ira, source_node, + buf_sprintf("comparison of union and enum literal is only valid for tagged union types")); + add_error_note(ira->codegen, msg, union_val->value->type->data.unionation.decl_node, + buf_sprintf("type %s is not a tagged union", + buf_ptr(&union_val->value->type->name))); + return ira->codegen->invalid_inst_gen; + } + + ZigType *tag_type = union_val->value->type->data.unionation.tag_type; + assert(tag_type != nullptr); + + IrInstGen *casted_union = ir_implicit_cast(ira, union_val, tag_type); + if (type_is_invalid(casted_union->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *casted_val = ir_implicit_cast(ira, enum_val, tag_type); + if (type_is_invalid(casted_val->value->type)) + return ira->codegen->invalid_inst_gen; + + if (instr_is_comptime(casted_union)) { + ZigValue *const_union_val = ir_resolve_const(ira, casted_union, UndefBad); + if (!const_union_val) + return ira->codegen->invalid_inst_gen; + + ZigValue *const_enum_val = ir_resolve_const(ira, casted_val, UndefBad); + if (!const_enum_val) + return ira->codegen->invalid_inst_gen; + + Cmp cmp_result = bigint_cmp(&const_union_val->data.x_union.tag, &const_enum_val->data.x_enum_tag); + bool bool_result = (op_id == IrBinOpCmpEq) ? cmp_result == CmpEQ : cmp_result != CmpEQ; + + return ir_const_bool(ira, &bin_op_instruction->base.base, bool_result); + } + + return ir_build_bin_op_gen(ira, &bin_op_instruction->base.base, ira->codegen->builtin_types.entry_bool, + op_id, casted_union, casted_val, bin_op_instruction->safety_check_on); + } + + if (op1->value->type->id == ZigTypeIdErrorSet && op2->value->type->id == ZigTypeIdErrorSet) { + if (!is_equality_cmp) { + ir_add_error_node(ira, source_node, buf_sprintf("operator not allowed for errors")); + return ira->codegen->invalid_inst_gen; + } + ZigType *intersect_type = get_error_set_intersection(ira, op1->value->type, op2->value->type, source_node); + if (type_is_invalid(intersect_type)) { + return ira->codegen->invalid_inst_gen; + } + + if (!resolve_inferred_error_set(ira->codegen, intersect_type, source_node)) { + return ira->codegen->invalid_inst_gen; + } + + // exception if one of the operators has the type of the empty error set, we allow the comparison + // (and make it comptime known) + // this is a function which is evaluated at comptime and returns an inferred error set will have an empty + // error set. + if (op1->value->type->data.error_set.err_count == 0 || op2->value->type->data.error_set.err_count == 0) { + bool are_equal = false; + bool answer; + if (op_id == IrBinOpCmpEq) { + answer = are_equal; + } else if (op_id == IrBinOpCmpNotEq) { + answer = !are_equal; + } else { + zig_unreachable(); + } + return ir_const_bool(ira, &bin_op_instruction->base.base, answer); + } + + if (!type_is_global_error_set(intersect_type)) { + if (intersect_type->data.error_set.err_count == 0) { + ir_add_error_node(ira, source_node, + buf_sprintf("error sets '%s' and '%s' have no common errors", + buf_ptr(&op1->value->type->name), buf_ptr(&op2->value->type->name))); + return ira->codegen->invalid_inst_gen; + } + if (op1->value->type->data.error_set.err_count == 1 && op2->value->type->data.error_set.err_count == 1) { + bool are_equal = true; + bool answer; + if (op_id == IrBinOpCmpEq) { + answer = are_equal; + } else if (op_id == IrBinOpCmpNotEq) { + answer = !are_equal; + } else { + zig_unreachable(); + } + return ir_const_bool(ira, &bin_op_instruction->base.base, answer); + } + } + + if (instr_is_comptime(op1) && instr_is_comptime(op2)) { + ZigValue *op1_val = ir_resolve_const(ira, op1, UndefBad); + if (op1_val == nullptr) + return ira->codegen->invalid_inst_gen; + ZigValue *op2_val = ir_resolve_const(ira, op2, UndefBad); + if (op2_val == nullptr) + return ira->codegen->invalid_inst_gen; + + bool answer; + bool are_equal = op1_val->data.x_err_set->value == op2_val->data.x_err_set->value; + if (op_id == IrBinOpCmpEq) { + answer = are_equal; + } else if (op_id == IrBinOpCmpNotEq) { + answer = !are_equal; + } else { + zig_unreachable(); + } + + return ir_const_bool(ira, &bin_op_instruction->base.base, answer); + } + + return ir_build_bin_op_gen(ira, &bin_op_instruction->base.base, ira->codegen->builtin_types.entry_bool, + op_id, op1, op2, bin_op_instruction->safety_check_on); + } + + if (type_is_numeric(op1->value->type) && type_is_numeric(op2->value->type)) { + // 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 ir_analyze_bin_op_cmp_numeric(ira, &bin_op_instruction->base.base, op1, op2, op_id); + } + + IrInstGen *instructions[] = {op1, op2}; + ZigType *resolved_type = ir_resolve_peer_types(ira, source_node, nullptr, instructions, 2); + if (type_is_invalid(resolved_type)) + return ira->codegen->invalid_inst_gen; + + bool operator_allowed = type_is_self_comparable(resolved_type, is_equality_cmp); + + if (!operator_allowed) { + ir_add_error_node(ira, source_node, + buf_sprintf("operator not allowed for type '%s'", buf_ptr(&resolved_type->name))); + return ira->codegen->invalid_inst_gen; + } + + IrInstGen *casted_op1 = ir_implicit_cast(ira, op1, resolved_type); + if (type_is_invalid(casted_op1->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *casted_op2 = ir_implicit_cast(ira, op2, resolved_type); + if (type_is_invalid(casted_op2->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *resolve_const_result = ir_try_evaluate_bin_op_cmp_const(ira, &bin_op_instruction->base.base, casted_op1, + casted_op2, resolved_type, op_id); + if (resolve_const_result != nullptr) { + return resolve_const_result; + } + + ZigType *res_type = (resolved_type->id == ZigTypeIdVector) ? + get_vector_type(ira->codegen, resolved_type->data.vector.len, ira->codegen->builtin_types.entry_bool) : + ira->codegen->builtin_types.entry_bool; + return ir_build_bin_op_gen(ira, &bin_op_instruction->base.base, res_type, + op_id, casted_op1, casted_op2, bin_op_instruction->safety_check_on); +} + +static ErrorMsg *ir_eval_math_op_scalar(IrAnalyze *ira, IrInst* source_instr, ZigType *type_entry, + ZigValue *op1_val, IrBinOp op_id, ZigValue *op2_val, ZigValue *out_val) +{ + bool is_int; + bool is_float; + Cmp op2_zcmp; + if (type_entry->id == ZigTypeIdInt || type_entry->id == ZigTypeIdComptimeInt) { + is_int = true; + is_float = false; + op2_zcmp = bigint_cmp_zero(&op2_val->data.x_bigint); + } else if (type_entry->id == ZigTypeIdFloat || + type_entry->id == ZigTypeIdComptimeFloat) + { + is_int = false; + is_float = true; + op2_zcmp = float_cmp_zero(op2_val); + } else { + zig_unreachable(); + } + + if ((op_id == IrBinOpDivUnspecified || op_id == IrBinOpRemRem || op_id == IrBinOpRemMod || + op_id == IrBinOpDivTrunc || op_id == IrBinOpDivFloor) && op2_zcmp == CmpEQ) + { + return ir_add_error(ira, source_instr, buf_sprintf("division by zero")); + } + if ((op_id == IrBinOpRemRem || op_id == IrBinOpRemMod) && op2_zcmp == CmpLT) { + return ir_add_error(ira, source_instr, buf_sprintf("negative denominator")); + } + + switch (op_id) { + case IrBinOpInvalid: + case IrBinOpBoolOr: + case IrBinOpBoolAnd: + case IrBinOpCmpEq: + case IrBinOpCmpNotEq: + case IrBinOpCmpLessThan: + case IrBinOpCmpGreaterThan: + case IrBinOpCmpLessOrEq: + case IrBinOpCmpGreaterOrEq: + case IrBinOpArrayCat: + case IrBinOpArrayMult: + case IrBinOpRemUnspecified: + zig_unreachable(); + case IrBinOpBinOr: + assert(is_int); + bigint_or(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); + break; + case IrBinOpBinXor: + assert(is_int); + bigint_xor(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); + break; + case IrBinOpBinAnd: + assert(is_int); + bigint_and(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); + break; + case IrBinOpBitShiftLeftExact: + assert(is_int); + bigint_shl(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); + break; + case IrBinOpBitShiftLeftLossy: + assert(type_entry->id == ZigTypeIdInt); + bigint_shl_trunc(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint, + type_entry->data.integral.bit_count, type_entry->data.integral.is_signed); + break; + case IrBinOpBitShiftRightExact: + { + assert(is_int); + bigint_shr(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); + BigInt orig_bigint; + bigint_shl(&orig_bigint, &out_val->data.x_bigint, &op2_val->data.x_bigint); + if (bigint_cmp(&op1_val->data.x_bigint, &orig_bigint) != CmpEQ) { + return ir_add_error(ira, source_instr, buf_sprintf("exact shift shifted out 1 bits")); + } + break; + } + case IrBinOpBitShiftRightLossy: + assert(is_int); + bigint_shr(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); + break; + case IrBinOpAdd: + if (is_int) { + bigint_add(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); + } else { + float_add(out_val, op1_val, op2_val); + } + break; + case IrBinOpAddWrap: + assert(type_entry->id == ZigTypeIdInt); + bigint_add_wrap(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint, + type_entry->data.integral.bit_count, type_entry->data.integral.is_signed); + break; + case IrBinOpSub: + if (is_int) { + bigint_sub(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); + } else { + float_sub(out_val, op1_val, op2_val); + } + break; + case IrBinOpSubWrap: + assert(type_entry->id == ZigTypeIdInt); + bigint_sub_wrap(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint, + type_entry->data.integral.bit_count, type_entry->data.integral.is_signed); + break; + case IrBinOpMult: + if (is_int) { + bigint_mul(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); + } else { + float_mul(out_val, op1_val, op2_val); + } + break; + case IrBinOpMultWrap: + assert(type_entry->id == ZigTypeIdInt); + bigint_mul_wrap(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint, + type_entry->data.integral.bit_count, type_entry->data.integral.is_signed); + break; + case IrBinOpDivUnspecified: + assert(is_float); + float_div(out_val, op1_val, op2_val); + break; + case IrBinOpDivTrunc: + if (is_int) { + bigint_div_trunc(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); + } else { + float_div_trunc(out_val, op1_val, op2_val); + } + break; + case IrBinOpDivFloor: + if (is_int) { + bigint_div_floor(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); + } else { + float_div_floor(out_val, op1_val, op2_val); + } + break; + case IrBinOpDivExact: + if (is_int) { + bigint_div_trunc(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); + BigInt remainder; + bigint_rem(&remainder, &op1_val->data.x_bigint, &op2_val->data.x_bigint); + if (bigint_cmp_zero(&remainder) != CmpEQ) { + return ir_add_error(ira, source_instr, buf_sprintf("exact division had a remainder")); + } + } else { + float_div_trunc(out_val, op1_val, op2_val); + ZigValue remainder = {}; + float_rem(&remainder, op1_val, op2_val); + if (float_cmp_zero(&remainder) != CmpEQ) { + return ir_add_error(ira, source_instr, buf_sprintf("exact division had a remainder")); + } + } + break; + case IrBinOpRemRem: + if (is_int) { + bigint_rem(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); + } else { + float_rem(out_val, op1_val, op2_val); + } + break; + case IrBinOpRemMod: + if (is_int) { + bigint_mod(&out_val->data.x_bigint, &op1_val->data.x_bigint, &op2_val->data.x_bigint); + } else { + float_mod(out_val, op1_val, op2_val); + } + break; + } + + if (type_entry->id == ZigTypeIdInt) { + if (!bigint_fits_in_bits(&out_val->data.x_bigint, type_entry->data.integral.bit_count, + type_entry->data.integral.is_signed)) + { + return ir_add_error(ira, source_instr, buf_sprintf("operation caused overflow")); + } + } + + out_val->type = type_entry; + out_val->special = ConstValSpecialStatic; + return nullptr; +} + +// This works on operands that have already been checked to be comptime known. +static IrInstGen *ir_analyze_math_op(IrAnalyze *ira, IrInst* source_instr, + ZigType *type_entry, ZigValue *op1_val, IrBinOp op_id, ZigValue *op2_val) +{ + IrInstGen *result_instruction = ir_const(ira, source_instr, type_entry); + ZigValue *out_val = result_instruction->value; + if (type_entry->id == ZigTypeIdVector) { + expand_undef_array(ira->codegen, op1_val); + expand_undef_array(ira->codegen, op2_val); + out_val->special = ConstValSpecialUndef; + expand_undef_array(ira->codegen, out_val); + size_t len = type_entry->data.vector.len; + ZigType *scalar_type = type_entry->data.vector.elem_type; + for (size_t i = 0; i < len; i += 1) { + ZigValue *scalar_op1_val = &op1_val->data.x_array.data.s_none.elements[i]; + ZigValue *scalar_op2_val = &op2_val->data.x_array.data.s_none.elements[i]; + ZigValue *scalar_out_val = &out_val->data.x_array.data.s_none.elements[i]; + assert(scalar_op1_val->type == scalar_type); + assert(scalar_out_val->type == scalar_type); + ErrorMsg *msg = ir_eval_math_op_scalar(ira, source_instr, scalar_type, + scalar_op1_val, op_id, scalar_op2_val, scalar_out_val); + if (msg != nullptr) { + add_error_note(ira->codegen, msg, source_instr->source_node, + buf_sprintf("when computing vector element at index %" ZIG_PRI_usize, i)); + return ira->codegen->invalid_inst_gen; + } + } + out_val->type = type_entry; + out_val->special = ConstValSpecialStatic; + } else { + if (ir_eval_math_op_scalar(ira, source_instr, type_entry, op1_val, op_id, op2_val, out_val) != nullptr) { + return ira->codegen->invalid_inst_gen; + } + } + return ir_implicit_cast(ira, result_instruction, type_entry); +} + +static IrInstGen *ir_analyze_bit_shift(IrAnalyze *ira, IrInstSrcBinOp *bin_op_instruction) { + IrInstGen *op1 = bin_op_instruction->op1->child; + if (type_is_invalid(op1->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *op2 = bin_op_instruction->op2->child; + if (type_is_invalid(op2->value->type)) + return ira->codegen->invalid_inst_gen; + + ZigType *op1_type = op1->value->type; + ZigType *op2_type = op2->value->type; + + if (op1_type->id == ZigTypeIdVector && op2_type->id != ZigTypeIdVector) { + ir_add_error(ira, &bin_op_instruction->op1->base, + buf_sprintf("bit shifting operation expected vector type, found '%s'", + buf_ptr(&op2_type->name))); + return ira->codegen->invalid_inst_gen; + } + + if (op1_type->id != ZigTypeIdVector && op2_type->id == ZigTypeIdVector) { + ir_add_error(ira, &bin_op_instruction->op1->base, + buf_sprintf("bit shifting operation expected vector type, found '%s'", + buf_ptr(&op1_type->name))); + return ira->codegen->invalid_inst_gen; + } + + ZigType *op1_scalar_type = (op1_type->id == ZigTypeIdVector) ? + op1_type->data.vector.elem_type : op1_type; + ZigType *op2_scalar_type = (op2_type->id == ZigTypeIdVector) ? + op2_type->data.vector.elem_type : op2_type; + + if (op1_scalar_type->id != ZigTypeIdInt && op1_scalar_type->id != ZigTypeIdComptimeInt) { + ir_add_error(ira, &bin_op_instruction->op1->base, + buf_sprintf("bit shifting operation expected integer type, found '%s'", + buf_ptr(&op1_scalar_type->name))); + return ira->codegen->invalid_inst_gen; + } + + if (op2_scalar_type->id != ZigTypeIdInt && op2_scalar_type->id != ZigTypeIdComptimeInt) { + ir_add_error(ira, &bin_op_instruction->op2->base, + buf_sprintf("shift amount has to be an integer type, but found '%s'", + buf_ptr(&op2_scalar_type->name))); + return ira->codegen->invalid_inst_gen; + } + + IrInstGen *casted_op2; + IrBinOp op_id = bin_op_instruction->op_id; + if (op1_scalar_type->id == ZigTypeIdComptimeInt) { + // comptime_int has no finite bit width + casted_op2 = op2; + + if (op_id == IrBinOpBitShiftLeftLossy) { + op_id = IrBinOpBitShiftLeftExact; + } + + if (!instr_is_comptime(op2)) { + ir_add_error(ira, &bin_op_instruction->base.base, + buf_sprintf("LHS of shift must be a fixed-width integer type, or RHS must be compile-time known")); + return ira->codegen->invalid_inst_gen; + } + + ZigValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); + if (op2_val == nullptr) + return ira->codegen->invalid_inst_gen; + + if (op2_val->data.x_bigint.is_negative) { + Buf *val_buf = buf_alloc(); + bigint_append_buf(val_buf, &op2_val->data.x_bigint, 10); + ir_add_error(ira, &casted_op2->base, + buf_sprintf("shift by negative value %s", buf_ptr(val_buf))); + return ira->codegen->invalid_inst_gen; + } + } else { + const unsigned bit_count = op1_scalar_type->data.integral.bit_count; + ZigType *shift_amt_type = get_smallest_unsigned_int_type(ira->codegen, + bit_count > 0 ? bit_count - 1 : 0); + + if (op1_type->id == ZigTypeIdVector) { + shift_amt_type = get_vector_type(ira->codegen, op1_type->data.vector.len, + shift_amt_type); + } + + casted_op2 = ir_implicit_cast(ira, op2, shift_amt_type); + if (type_is_invalid(casted_op2->value->type)) + return ira->codegen->invalid_inst_gen; + + // This check is only valid iff op1 has at least one bit + if (bit_count > 0 && instr_is_comptime(casted_op2)) { + ZigValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); + if (op2_val == nullptr) + return ira->codegen->invalid_inst_gen; + + ZigValue bit_count_value = {}; + init_const_usize(ira->codegen, &bit_count_value, bit_count); + + if (!value_cmp_numeric_val_all(op2_val, CmpLT, &bit_count_value)) { + ErrorMsg* msg = ir_add_error(ira, + &bin_op_instruction->base.base, + buf_sprintf("RHS of shift is too large for LHS type")); + add_error_note(ira->codegen, msg, op1->base.source_node, + buf_sprintf("type %s has only %u bits", + buf_ptr(&op1->value->type->name), bit_count)); + + return ira->codegen->invalid_inst_gen; + } + } + } + + // Fast path for zero RHS + if (instr_is_comptime(casted_op2)) { + ZigValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); + if (op2_val == nullptr) + return ira->codegen->invalid_inst_gen; + + if (value_cmp_numeric_val_all(op2_val, CmpEQ, nullptr)) + return ir_analyze_cast(ira, &bin_op_instruction->base.base, op1->value->type, op1); + } + + if (instr_is_comptime(op1) && instr_is_comptime(casted_op2)) { + ZigValue *op1_val = ir_resolve_const(ira, op1, UndefBad); + if (op1_val == nullptr) + return ira->codegen->invalid_inst_gen; + + ZigValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); + if (op2_val == nullptr) + return ira->codegen->invalid_inst_gen; + + return ir_analyze_math_op(ira, &bin_op_instruction->base.base, op1_type, op1_val, op_id, op2_val); + } + + return ir_build_bin_op_gen(ira, &bin_op_instruction->base.base, op1->value->type, + op_id, op1, casted_op2, bin_op_instruction->safety_check_on); +} + +static bool ok_float_op(IrBinOp op) { + switch (op) { + case IrBinOpInvalid: + zig_unreachable(); + case IrBinOpAdd: + case IrBinOpSub: + case IrBinOpMult: + case IrBinOpDivUnspecified: + case IrBinOpDivTrunc: + case IrBinOpDivFloor: + case IrBinOpDivExact: + case IrBinOpRemRem: + case IrBinOpRemMod: + case IrBinOpRemUnspecified: + return true; + + case IrBinOpBoolOr: + case IrBinOpBoolAnd: + case IrBinOpCmpEq: + case IrBinOpCmpNotEq: + case IrBinOpCmpLessThan: + case IrBinOpCmpGreaterThan: + case IrBinOpCmpLessOrEq: + case IrBinOpCmpGreaterOrEq: + case IrBinOpBinOr: + case IrBinOpBinXor: + case IrBinOpBinAnd: + case IrBinOpBitShiftLeftLossy: + case IrBinOpBitShiftLeftExact: + case IrBinOpBitShiftRightLossy: + case IrBinOpBitShiftRightExact: + case IrBinOpAddWrap: + case IrBinOpSubWrap: + case IrBinOpMultWrap: + case IrBinOpArrayCat: + case IrBinOpArrayMult: + return false; + } + zig_unreachable(); +} + +static bool is_pointer_arithmetic_allowed(ZigType *lhs_type, IrBinOp op) { + switch (op) { + case IrBinOpAdd: + case IrBinOpSub: + break; + default: + return false; + } + if (lhs_type->id != ZigTypeIdPointer) + return false; + switch (lhs_type->data.pointer.ptr_len) { + case PtrLenSingle: + return lhs_type->data.pointer.child_type->id == ZigTypeIdArray; + case PtrLenUnknown: + case PtrLenC: + return true; + } + zig_unreachable(); +} + +static bool value_cmp_numeric_val(ZigValue *left, Cmp predicate, ZigValue *right, bool any) { + assert(left->special == ConstValSpecialStatic); + assert(right == nullptr || right->special == ConstValSpecialStatic); + + switch (left->type->id) { + case ZigTypeIdComptimeInt: + case ZigTypeIdInt: { + const Cmp result = right ? + bigint_cmp(&left->data.x_bigint, &right->data.x_bigint) : + bigint_cmp_zero(&left->data.x_bigint); + return result == predicate; + } + case ZigTypeIdComptimeFloat: + case ZigTypeIdFloat: { + if (float_is_nan(left)) + return false; + if (right != nullptr && float_is_nan(right)) + return false; + + const Cmp result = right ? float_cmp(left, right) : float_cmp_zero(left); + return result == predicate; + } + case ZigTypeIdVector: { + for (size_t i = 0; i < left->type->data.vector.len; i++) { + ZigValue *scalar_val = &left->data.x_array.data.s_none.elements[i]; + const bool result = value_cmp_numeric_val(scalar_val, predicate, right, any); + + if (any && result) + return true; // This element satisfies the predicate + else if (!any && !result) + return false; // This element doesn't satisfy the predicate + } + return any ? false : true; + } + default: + zig_unreachable(); + } +} + +static bool value_cmp_numeric_val_any(ZigValue *left, Cmp predicate, ZigValue *right) { + return value_cmp_numeric_val(left, predicate, right, true); +} + +static bool value_cmp_numeric_val_all(ZigValue *left, Cmp predicate, ZigValue *right) { + return value_cmp_numeric_val(left, predicate, right, false); +} + +static IrInstGen *ir_analyze_bin_op_math(IrAnalyze *ira, IrInstSrcBinOp *instruction) { + Error err; + + IrInstGen *op1 = instruction->op1->child; + if (type_is_invalid(op1->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *op2 = instruction->op2->child; + if (type_is_invalid(op2->value->type)) + return ira->codegen->invalid_inst_gen; + + IrBinOp op_id = instruction->op_id; + + // look for pointer math + if (is_pointer_arithmetic_allowed(op1->value->type, op_id)) { + IrInstGen *casted_op2 = ir_implicit_cast(ira, op2, ira->codegen->builtin_types.entry_usize); + if (type_is_invalid(casted_op2->value->type)) + return ira->codegen->invalid_inst_gen; + + // If either operand is undef, result is undef. + ZigValue *op1_val = nullptr; + ZigValue *op2_val = nullptr; + if (instr_is_comptime(op1)) { + op1_val = ir_resolve_const(ira, op1, UndefOk); + if (op1_val == nullptr) + return ira->codegen->invalid_inst_gen; + if (op1_val->special == ConstValSpecialUndef) + return ir_const_undef(ira, &instruction->base.base, op1->value->type); + } + if (instr_is_comptime(casted_op2)) { + op2_val = ir_resolve_const(ira, casted_op2, UndefOk); + if (op2_val == nullptr) + return ira->codegen->invalid_inst_gen; + if (op2_val->special == ConstValSpecialUndef) + return ir_const_undef(ira, &instruction->base.base, op1->value->type); + } + + ZigType *elem_type = op1->value->type->data.pointer.child_type; + if ((err = type_resolve(ira->codegen, elem_type, ResolveStatusSizeKnown))) + return ira->codegen->invalid_inst_gen; + + // NOTE: this variable is meaningful iff op2_val is not null! + uint64_t byte_offset; + if (op2_val != nullptr) { + uint64_t elem_offset; + if (!ir_resolve_usize(ira, casted_op2, &elem_offset)) + return ira->codegen->invalid_inst_gen; + + byte_offset = type_size(ira->codegen, elem_type) * elem_offset; + } + + // Fast path for cases where the RHS is zero + if (op2_val != nullptr && byte_offset == 0) { + return op1; + } + + ZigType *result_type = op1->value->type; + // Calculate the new alignment of the pointer + { + uint32_t align_bytes; + if ((err = resolve_ptr_align(ira, op1->value->type, &align_bytes))) + return ira->codegen->invalid_inst_gen; + + // If the addend is not a comptime-known value we can still count on + // it being a multiple of the type size + uint32_t addend = op2_val ? byte_offset : type_size(ira->codegen, elem_type); + + // The resulting pointer is aligned to the lcd between the + // offset (an arbitrary number) and the alignment factor (always + // a power of two, non zero) + uint32_t new_align = 1 << ctzll(addend | align_bytes); + // Rough guard to prevent overflows + assert(new_align); + result_type = adjust_ptr_align(ira->codegen, result_type, new_align); + } + + if (op2_val != nullptr && op1_val != nullptr && + (op1->value->data.x_ptr.special == ConstPtrSpecialHardCodedAddr || + op1->value->data.x_ptr.special == ConstPtrSpecialNull)) + { + uint64_t start_addr = (op1_val->data.x_ptr.special == ConstPtrSpecialNull) ? + 0 : op1_val->data.x_ptr.data.hard_coded_addr.addr; + uint64_t new_addr; + if (op_id == IrBinOpAdd) { + new_addr = start_addr + byte_offset; + } else if (op_id == IrBinOpSub) { + new_addr = start_addr - byte_offset; + } else { + zig_unreachable(); + } + IrInstGen *result = ir_const(ira, &instruction->base.base, result_type); + result->value->data.x_ptr.special = ConstPtrSpecialHardCodedAddr; + result->value->data.x_ptr.mut = ConstPtrMutRuntimeVar; + result->value->data.x_ptr.data.hard_coded_addr.addr = new_addr; + return result; + } + + return ir_build_bin_op_gen(ira, &instruction->base.base, result_type, op_id, op1, casted_op2, true); + } + + IrInstGen *instructions[] = {op1, op2}; + ZigType *resolved_type = ir_resolve_peer_types(ira, instruction->base.base.source_node, nullptr, instructions, 2); + if (type_is_invalid(resolved_type)) + return ira->codegen->invalid_inst_gen; + + ZigType *scalar_type = (resolved_type->id == ZigTypeIdVector) ? + resolved_type->data.vector.elem_type : resolved_type; + + bool is_int = scalar_type->id == ZigTypeIdInt || scalar_type->id == ZigTypeIdComptimeInt; + bool is_float = scalar_type->id == ZigTypeIdFloat || scalar_type->id == ZigTypeIdComptimeFloat; + + if (!is_int && !(is_float && ok_float_op(op_id))) { + AstNode *source_node = instruction->base.base.source_node; + ir_add_error_node(ira, source_node, + buf_sprintf("invalid operands to binary expression: '%s' and '%s'", + buf_ptr(&op1->value->type->name), + buf_ptr(&op2->value->type->name))); + return ira->codegen->invalid_inst_gen; + } + + IrInstGen *casted_op1 = ir_implicit_cast(ira, op1, resolved_type); + if (type_is_invalid(casted_op1->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *casted_op2 = ir_implicit_cast(ira, op2, resolved_type); + if (type_is_invalid(casted_op2->value->type)) + return ira->codegen->invalid_inst_gen; + + // Comptime integers have no fixed size + if (scalar_type->id == ZigTypeIdComptimeInt) { + if (op_id == IrBinOpAddWrap) { + op_id = IrBinOpAdd; + } else if (op_id == IrBinOpSubWrap) { + op_id = IrBinOpSub; + } else if (op_id == IrBinOpMultWrap) { + op_id = IrBinOpMult; + } + } + + if (instr_is_comptime(casted_op1) && instr_is_comptime(casted_op2)) { + ZigValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad); + if (op1_val == nullptr) + return ira->codegen->invalid_inst_gen; + + ZigValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); + if (op2_val == nullptr) + return ira->codegen->invalid_inst_gen; + + // Promote division with negative numbers to signed + bool is_signed_div = value_cmp_numeric_val_any(op1_val, CmpLT, nullptr) || + value_cmp_numeric_val_any(op2_val, CmpLT, nullptr); + + if (op_id == IrBinOpDivUnspecified && is_int) { + // Default to truncating division and check if it's valid for the + // given operands if signed + op_id = IrBinOpDivTrunc; + + if (is_signed_div) { + bool ok = false; + + if (value_cmp_numeric_val_any(op2_val, CmpEQ, nullptr)) { + // the division by zero error will be caught later, but we don't have a + // division function ambiguity problem. + ok = true; + } else { + IrInstGen *trunc_val = ir_analyze_math_op(ira, &instruction->base.base, resolved_type, + op1_val, IrBinOpDivTrunc, op2_val); + if (type_is_invalid(trunc_val->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *floor_val = ir_analyze_math_op(ira, &instruction->base.base, resolved_type, + op1_val, IrBinOpDivFloor, op2_val); + if (type_is_invalid(floor_val->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *cmp_val = ir_analyze_bin_op_cmp_numeric(ira, &instruction->base.base, + trunc_val, floor_val, IrBinOpCmpEq); + if (type_is_invalid(cmp_val->value->type)) + return ira->codegen->invalid_inst_gen; + + // We can "upgrade" the operator only if trunc(a/b) == floor(a/b) + if (!ir_resolve_bool(ira, cmp_val, &ok)) + return ira->codegen->invalid_inst_gen; + } + + if (!ok) { + ir_add_error(ira, &instruction->base.base, + buf_sprintf("division with '%s' and '%s': signed integers must use @divTrunc, @divFloor, or @divExact", + buf_ptr(&op1->value->type->name), + buf_ptr(&op2->value->type->name))); + return ira->codegen->invalid_inst_gen; + } + } + } else if (op_id == IrBinOpRemUnspecified) { + op_id = IrBinOpRemRem; + + if (is_signed_div) { + bool ok = false; + + if (value_cmp_numeric_val_any(op2_val, CmpEQ, nullptr)) { + // the division by zero error will be caught later, but we don't have a + // division function ambiguity problem. + ok = true; + } else { + IrInstGen *rem_val = ir_analyze_math_op(ira, &instruction->base.base, resolved_type, + op1_val, IrBinOpRemRem, op2_val); + if (type_is_invalid(rem_val->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *mod_val = ir_analyze_math_op(ira, &instruction->base.base, resolved_type, + op1_val, IrBinOpRemMod, op2_val); + if (type_is_invalid(mod_val->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *cmp_val = ir_analyze_bin_op_cmp_numeric(ira, &instruction->base.base, + rem_val, mod_val, IrBinOpCmpEq); + if (type_is_invalid(cmp_val->value->type)) + return ira->codegen->invalid_inst_gen; + + // We can "upgrade" the operator only if mod(a,b) == rem(a,b) + if (!ir_resolve_bool(ira, cmp_val, &ok)) + return ira->codegen->invalid_inst_gen; + } + + if (!ok) { + ir_add_error(ira, &instruction->base.base, + buf_sprintf("remainder division with '%s' and '%s': signed integers and floats must use @rem or @mod", + buf_ptr(&op1->value->type->name), + buf_ptr(&op2->value->type->name))); + return ira->codegen->invalid_inst_gen; + } + } + } + + return ir_analyze_math_op(ira, &instruction->base.base, resolved_type, op1_val, op_id, op2_val); + } + + const bool is_signed_div = + (scalar_type->id == ZigTypeIdInt && scalar_type->data.integral.is_signed) || + scalar_type->id == ZigTypeIdFloat; + + // Warn the user to use the proper operators here + if (op_id == IrBinOpDivUnspecified && is_int) { + op_id = IrBinOpDivTrunc; + + if (is_signed_div) { + ir_add_error(ira, &instruction->base.base, + buf_sprintf("division with '%s' and '%s': signed integers must use @divTrunc, @divFloor, or @divExact", + buf_ptr(&op1->value->type->name), + buf_ptr(&op2->value->type->name))); + return ira->codegen->invalid_inst_gen; + } + } else if (op_id == IrBinOpRemUnspecified) { + op_id = IrBinOpRemRem; + + if (is_signed_div) { + ir_add_error(ira, &instruction->base.base, + buf_sprintf("remainder division with '%s' and '%s': signed integers and floats must use @rem or @mod", + buf_ptr(&op1->value->type->name), + buf_ptr(&op2->value->type->name))); + return ira->codegen->invalid_inst_gen; + } + } + + return ir_build_bin_op_gen(ira, &instruction->base.base, resolved_type, + op_id, casted_op1, casted_op2, instruction->safety_check_on); +} + +static IrInstGen *ir_analyze_tuple_cat(IrAnalyze *ira, IrInst* source_instr, + IrInstGen *op1, IrInstGen *op2) +{ + Error err; + ZigType *op1_type = op1->value->type; + ZigType *op2_type = op2->value->type; + + uint32_t op1_field_count = op1_type->data.structure.src_field_count; + uint32_t op2_field_count = op2_type->data.structure.src_field_count; + + Buf *bare_name = buf_alloc(); + Buf *name = get_anon_type_name(ira->codegen, nullptr, container_string(ContainerKindStruct), + source_instr->scope, source_instr->source_node, bare_name); + ZigType *new_type = get_partial_container_type(ira->codegen, source_instr->scope, + ContainerKindStruct, source_instr->source_node, buf_ptr(name), bare_name, ContainerLayoutAuto); + new_type->data.structure.special = StructSpecialInferredTuple; + new_type->data.structure.resolve_status = ResolveStatusBeingInferred; + uint32_t new_field_count = op1_field_count + op2_field_count; + + new_type->data.structure.src_field_count = new_field_count; + new_type->data.structure.fields = realloc_type_struct_fields(new_type->data.structure.fields, + 0, new_field_count); + + IrInstGen *new_struct_ptr = ir_resolve_result(ira, source_instr, no_result_loc(), + new_type, nullptr, false, true); + + for (uint32_t i = 0; i < new_field_count; i += 1) { + TypeStructField *src_field; + if (i < op1_field_count) { + src_field = op1_type->data.structure.fields[i]; + } else { + src_field = op2_type->data.structure.fields[i - op1_field_count]; + } + TypeStructField *new_field = new_type->data.structure.fields[i]; + new_field->name = buf_sprintf("%" PRIu32, i); + new_field->type_entry = src_field->type_entry; + new_field->type_val = src_field->type_val; + new_field->src_index = i; + new_field->decl_node = src_field->decl_node; + new_field->init_val = src_field->init_val; + new_field->is_comptime = src_field->is_comptime; + } + if ((err = type_resolve(ira->codegen, new_type, ResolveStatusZeroBitsKnown))) + return ira->codegen->invalid_inst_gen; + + ZigList const_ptrs = {}; + for (uint32_t i = 0; i < new_field_count; i += 1) { + TypeStructField *dst_field = new_type->data.structure.fields[i]; + IrInstGen *src_struct_op; + TypeStructField *src_field; + if (i < op1_field_count) { + src_field = op1_type->data.structure.fields[i]; + src_struct_op = op1; + } else { + src_field = op2_type->data.structure.fields[i - op1_field_count]; + src_struct_op = op2; + } + IrInstGen *field_value = ir_analyze_struct_value_field_value(ira, source_instr, + src_struct_op, src_field); + if (type_is_invalid(field_value->value->type)) + return ira->codegen->invalid_inst_gen; + IrInstGen *dest_ptr = ir_analyze_struct_field_ptr(ira, source_instr, dst_field, + new_struct_ptr, new_type, true); + if (type_is_invalid(dest_ptr->value->type)) + return ira->codegen->invalid_inst_gen; + if (instr_is_comptime(field_value)) { + const_ptrs.append(dest_ptr); + } + IrInstGen *store_ptr_inst = ir_analyze_store_ptr(ira, source_instr, dest_ptr, field_value, + true); + if (type_is_invalid(store_ptr_inst->value->type)) + return ira->codegen->invalid_inst_gen; + } + if (const_ptrs.length != new_field_count) { + new_struct_ptr->value->special = ConstValSpecialRuntime; + for (size_t i = 0; i < const_ptrs.length; i += 1) { + IrInstGen *elem_result_loc = const_ptrs.at(i); + assert(elem_result_loc->value->special == ConstValSpecialStatic); + if (elem_result_loc->value->type->data.pointer.inferred_struct_field != nullptr) { + // This field will be generated comptime; no need to do this. + continue; + } + IrInstGen *deref = ir_get_deref(ira, &elem_result_loc->base, elem_result_loc, nullptr); + if (!type_requires_comptime(ira->codegen, elem_result_loc->value->type->data.pointer.child_type)) { + elem_result_loc->value->special = ConstValSpecialRuntime; + } + ir_analyze_store_ptr(ira, &elem_result_loc->base, elem_result_loc, deref, true); + } + } + + const_ptrs.deinit(); + + return ir_get_deref(ira, source_instr, new_struct_ptr, nullptr); +} + +static IrInstGen *ir_analyze_array_cat(IrAnalyze *ira, IrInstSrcBinOp *instruction) { + IrInstGen *op1 = instruction->op1->child; + ZigType *op1_type = op1->value->type; + if (type_is_invalid(op1_type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *op2 = instruction->op2->child; + ZigType *op2_type = op2->value->type; + if (type_is_invalid(op2_type)) + return ira->codegen->invalid_inst_gen; + + if (is_tuple(op1_type) && is_tuple(op2_type)) { + return ir_analyze_tuple_cat(ira, &instruction->base.base, op1, op2); + } + + ZigValue *op1_val = ir_resolve_const(ira, op1, UndefBad); + if (!op1_val) + return ira->codegen->invalid_inst_gen; + + ZigValue *op2_val = ir_resolve_const(ira, op2, UndefBad); + if (!op2_val) + return ira->codegen->invalid_inst_gen; + + ZigValue *sentinel1 = nullptr; + ZigValue *op1_array_val; + size_t op1_array_index; + size_t op1_array_end; + ZigType *child_type; + if (op1_type->id == ZigTypeIdArray) { + child_type = op1_type->data.array.child_type; + op1_array_val = op1_val; + op1_array_index = 0; + op1_array_end = op1_type->data.array.len; + sentinel1 = op1_type->data.array.sentinel; + } else if (op1_type->id == ZigTypeIdPointer && + op1_type->data.pointer.child_type == ira->codegen->builtin_types.entry_u8 && + op1_type->data.pointer.sentinel != nullptr && + op1_val->data.x_ptr.special == ConstPtrSpecialBaseArray) + { + child_type = op1_type->data.pointer.child_type; + op1_array_val = op1_val->data.x_ptr.data.base_array.array_val; + op1_array_index = op1_val->data.x_ptr.data.base_array.elem_index; + op1_array_end = op1_array_val->type->data.array.len; + sentinel1 = op1_type->data.pointer.sentinel; + } else if (is_slice(op1_type)) { + ZigType *ptr_type = op1_type->data.structure.fields[slice_ptr_index]->type_entry; + child_type = ptr_type->data.pointer.child_type; + ZigValue *ptr_val = op1_val->data.x_struct.fields[slice_ptr_index]; + assert(ptr_val->data.x_ptr.special == ConstPtrSpecialBaseArray); + op1_array_val = ptr_val->data.x_ptr.data.base_array.array_val; + op1_array_index = ptr_val->data.x_ptr.data.base_array.elem_index; + ZigValue *len_val = op1_val->data.x_struct.fields[slice_len_index]; + op1_array_end = op1_array_index + bigint_as_usize(&len_val->data.x_bigint); + sentinel1 = ptr_type->data.pointer.sentinel; + } else if (op1_type->id == ZigTypeIdPointer && + op1_type->data.pointer.ptr_len == PtrLenSingle && + op1_type->data.pointer.child_type->id == ZigTypeIdArray) + { + ZigType *array_type = op1_type->data.pointer.child_type; + child_type = array_type->data.array.child_type; + op1_array_val = const_ptr_pointee(ira, ira->codegen, op1_val, op1->base.source_node); + if (op1_array_val == nullptr) + return ira->codegen->invalid_inst_gen; + op1_array_index = 0; + op1_array_end = array_type->data.array.len; + sentinel1 = array_type->data.array.sentinel; + } else { + ir_add_error(ira, &op1->base, buf_sprintf("expected array, found '%s'", buf_ptr(&op1->value->type->name))); + return ira->codegen->invalid_inst_gen; + } + + ZigValue *sentinel2 = nullptr; + ZigValue *op2_array_val; + size_t op2_array_index; + size_t op2_array_end; + bool op2_type_valid; + if (op2_type->id == ZigTypeIdArray) { + op2_type_valid = op2_type->data.array.child_type == child_type; + op2_array_val = op2_val; + op2_array_index = 0; + op2_array_end = op2_array_val->type->data.array.len; + sentinel2 = op2_type->data.array.sentinel; + } else if (op2_type->id == ZigTypeIdPointer && + op2_type->data.pointer.sentinel != nullptr && + op2_val->data.x_ptr.special == ConstPtrSpecialBaseArray) + { + op2_type_valid = op2_type->data.pointer.child_type == child_type; + op2_array_val = op2_val->data.x_ptr.data.base_array.array_val; + op2_array_index = op2_val->data.x_ptr.data.base_array.elem_index; + op2_array_end = op2_array_val->type->data.array.len; + + sentinel2 = op2_type->data.pointer.sentinel; + } else if (is_slice(op2_type)) { + ZigType *ptr_type = op2_type->data.structure.fields[slice_ptr_index]->type_entry; + op2_type_valid = ptr_type->data.pointer.child_type == child_type; + ZigValue *ptr_val = op2_val->data.x_struct.fields[slice_ptr_index]; + assert(ptr_val->data.x_ptr.special == ConstPtrSpecialBaseArray); + op2_array_val = ptr_val->data.x_ptr.data.base_array.array_val; + op2_array_index = ptr_val->data.x_ptr.data.base_array.elem_index; + ZigValue *len_val = op2_val->data.x_struct.fields[slice_len_index]; + op2_array_end = op2_array_index + bigint_as_usize(&len_val->data.x_bigint); + + sentinel2 = ptr_type->data.pointer.sentinel; + } else if (op2_type->id == ZigTypeIdPointer && op2_type->data.pointer.ptr_len == PtrLenSingle && + op2_type->data.pointer.child_type->id == ZigTypeIdArray) + { + ZigType *array_type = op2_type->data.pointer.child_type; + op2_type_valid = array_type->data.array.child_type == child_type; + op2_array_val = const_ptr_pointee(ira, ira->codegen, op2_val, op2->base.source_node); + if (op2_array_val == nullptr) + return ira->codegen->invalid_inst_gen; + op2_array_index = 0; + op2_array_end = array_type->data.array.len; + + sentinel2 = array_type->data.array.sentinel; + } else { + ir_add_error(ira, &op2->base, + buf_sprintf("expected array or C string literal, found '%s'", buf_ptr(&op2->value->type->name))); + return ira->codegen->invalid_inst_gen; + } + if (!op2_type_valid) { + ir_add_error(ira, &op2->base, buf_sprintf("expected array of type '%s', found '%s'", + buf_ptr(&child_type->name), + buf_ptr(&op2->value->type->name))); + return ira->codegen->invalid_inst_gen; + } + + ZigValue *sentinel; + if (sentinel1 != nullptr && sentinel2 != nullptr) { + // When there is a sentinel mismatch, no sentinel on the result. The type system + // will catch this if it is a problem. + sentinel = const_values_equal(ira->codegen, sentinel1, sentinel2) ? sentinel1 : nullptr; + } else if (sentinel1 != nullptr) { + sentinel = sentinel1; + } else if (sentinel2 != nullptr) { + sentinel = sentinel2; + } else { + sentinel = nullptr; + } + + // The type of result is populated in the following if blocks + IrInstGen *result = ir_const(ira, &instruction->base.base, nullptr); + ZigValue *out_val = result->value; + + ZigValue *out_array_val; + size_t new_len = (op1_array_end - op1_array_index) + (op2_array_end - op2_array_index); + if (op1_type->id == ZigTypeIdPointer || op2_type->id == ZigTypeIdPointer) { + out_array_val = ira->codegen->pass1_arena->create(); + out_array_val->special = ConstValSpecialStatic; + out_array_val->type = get_array_type(ira->codegen, child_type, new_len, sentinel); + + out_val->data.x_ptr.special = ConstPtrSpecialRef; + out_val->data.x_ptr.data.ref.pointee = out_array_val; + out_val->type = get_pointer_to_type(ira->codegen, out_array_val->type, true); + } else if (is_slice(op1_type) || is_slice(op2_type)) { + ZigType *ptr_type = get_pointer_to_type_extra2(ira->codegen, child_type, + true, false, PtrLenUnknown, 0, 0, 0, false, + VECTOR_INDEX_NONE, nullptr, sentinel); + result->value->type = get_slice_type(ira->codegen, ptr_type); + out_array_val = ira->codegen->pass1_arena->create(); + out_array_val->special = ConstValSpecialStatic; + out_array_val->type = get_array_type(ira->codegen, child_type, new_len, sentinel); + + out_val->data.x_struct.fields = alloc_const_vals_ptrs(ira->codegen, 2); + + out_val->data.x_struct.fields[slice_ptr_index]->type = ptr_type; + out_val->data.x_struct.fields[slice_ptr_index]->special = ConstValSpecialStatic; + out_val->data.x_struct.fields[slice_ptr_index]->data.x_ptr.special = ConstPtrSpecialBaseArray; + out_val->data.x_struct.fields[slice_ptr_index]->data.x_ptr.data.base_array.array_val = out_array_val; + out_val->data.x_struct.fields[slice_ptr_index]->data.x_ptr.data.base_array.elem_index = 0; + + out_val->data.x_struct.fields[slice_len_index]->type = ira->codegen->builtin_types.entry_usize; + out_val->data.x_struct.fields[slice_len_index]->special = ConstValSpecialStatic; + bigint_init_unsigned(&out_val->data.x_struct.fields[slice_len_index]->data.x_bigint, new_len); + } else if (op1_type->id == ZigTypeIdArray || op2_type->id == ZigTypeIdArray) { + result->value->type = get_array_type(ira->codegen, child_type, new_len, sentinel); + out_array_val = out_val; + } else { + result->value->type = get_pointer_to_type_extra2(ira->codegen, child_type, true, false, PtrLenUnknown, + 0, 0, 0, false, VECTOR_INDEX_NONE, nullptr, sentinel); + out_array_val = ira->codegen->pass1_arena->create(); + out_array_val->special = ConstValSpecialStatic; + out_array_val->type = get_array_type(ira->codegen, child_type, new_len, sentinel); + out_val->data.x_ptr.special = ConstPtrSpecialBaseArray; + out_val->data.x_ptr.data.base_array.array_val = out_array_val; + out_val->data.x_ptr.data.base_array.elem_index = 0; + } + + if (op1_array_val->data.x_array.special == ConstArraySpecialUndef && + op2_array_val->data.x_array.special == ConstArraySpecialUndef) + { + out_array_val->data.x_array.special = ConstArraySpecialUndef; + return result; + } + + uint64_t full_len = new_len + ((sentinel != nullptr) ? 1 : 0); + out_array_val->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(full_len); + // TODO handle the buf case here for an optimization + expand_undef_array(ira->codegen, op1_array_val); + expand_undef_array(ira->codegen, op2_array_val); + + size_t next_index = 0; + for (size_t i = op1_array_index; i < op1_array_end; i += 1, next_index += 1) { + ZigValue *elem_dest_val = &out_array_val->data.x_array.data.s_none.elements[next_index]; + copy_const_val(ira->codegen, elem_dest_val, &op1_array_val->data.x_array.data.s_none.elements[i]); + elem_dest_val->parent.id = ConstParentIdArray; + elem_dest_val->parent.data.p_array.array_val = out_array_val; + elem_dest_val->parent.data.p_array.elem_index = next_index; + } + for (size_t i = op2_array_index; i < op2_array_end; i += 1, next_index += 1) { + ZigValue *elem_dest_val = &out_array_val->data.x_array.data.s_none.elements[next_index]; + copy_const_val(ira->codegen, elem_dest_val, &op2_array_val->data.x_array.data.s_none.elements[i]); + elem_dest_val->parent.id = ConstParentIdArray; + elem_dest_val->parent.data.p_array.array_val = out_array_val; + elem_dest_val->parent.data.p_array.elem_index = next_index; + } + if (next_index < full_len) { + ZigValue *elem_dest_val = &out_array_val->data.x_array.data.s_none.elements[next_index]; + copy_const_val(ira->codegen, elem_dest_val, sentinel); + elem_dest_val->parent.id = ConstParentIdArray; + elem_dest_val->parent.data.p_array.array_val = out_array_val; + elem_dest_val->parent.data.p_array.elem_index = next_index; + next_index += 1; + } + assert(next_index == full_len); + + return result; +} + +static IrInstGen *ir_analyze_tuple_mult(IrAnalyze *ira, IrInst* source_instr, + IrInstGen *op1, IrInstGen *op2) +{ + Error err; + ZigType *op1_type = op1->value->type; + uint64_t op1_field_count = op1_type->data.structure.src_field_count; + + uint64_t mult_amt; + if (!ir_resolve_usize(ira, op2, &mult_amt)) + return ira->codegen->invalid_inst_gen; + + uint64_t new_field_count; + if (mul_u64_overflow(op1_field_count, mult_amt, &new_field_count)) { + ir_add_error(ira, source_instr, buf_sprintf("operation results in overflow")); + return ira->codegen->invalid_inst_gen; + } + + Buf *bare_name = buf_alloc(); + Buf *name = get_anon_type_name(ira->codegen, nullptr, container_string(ContainerKindStruct), + source_instr->scope, source_instr->source_node, bare_name); + ZigType *new_type = get_partial_container_type(ira->codegen, source_instr->scope, + ContainerKindStruct, source_instr->source_node, buf_ptr(name), bare_name, ContainerLayoutAuto); + new_type->data.structure.special = StructSpecialInferredTuple; + new_type->data.structure.resolve_status = ResolveStatusBeingInferred; + new_type->data.structure.src_field_count = new_field_count; + new_type->data.structure.fields = realloc_type_struct_fields( + new_type->data.structure.fields, 0, new_field_count); + + IrInstGen *new_struct_ptr = ir_resolve_result(ira, source_instr, no_result_loc(), + new_type, nullptr, false, true); + + for (uint64_t i = 0; i < new_field_count; i += 1) { + TypeStructField *src_field = op1_type->data.structure.fields[i % op1_field_count]; + TypeStructField *new_field = new_type->data.structure.fields[i]; + + new_field->name = buf_sprintf("%" ZIG_PRI_u64, i); + new_field->type_entry = src_field->type_entry; + new_field->type_val = src_field->type_val; + new_field->src_index = i; + new_field->decl_node = src_field->decl_node; + new_field->init_val = src_field->init_val; + new_field->is_comptime = src_field->is_comptime; + } + + if ((err = type_resolve(ira->codegen, new_type, ResolveStatusZeroBitsKnown))) + return ira->codegen->invalid_inst_gen; + + ZigList const_ptrs = {}; + for (uint64_t i = 0; i < new_field_count; i += 1) { + TypeStructField *src_field = op1_type->data.structure.fields[i % op1_field_count]; + TypeStructField *dst_field = new_type->data.structure.fields[i]; + + IrInstGen *field_value = ir_analyze_struct_value_field_value( + ira, source_instr, op1, src_field); + if (type_is_invalid(field_value->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *dest_ptr = ir_analyze_struct_field_ptr( + ira, source_instr, dst_field, new_struct_ptr, new_type, true); + if (type_is_invalid(dest_ptr->value->type)) + return ira->codegen->invalid_inst_gen; + + if (instr_is_comptime(field_value)) { + const_ptrs.append(dest_ptr); + } + + IrInstGen *store_ptr_inst = ir_analyze_store_ptr( + ira, source_instr, dest_ptr, field_value, true); + if (type_is_invalid(store_ptr_inst->value->type)) + return ira->codegen->invalid_inst_gen; + } + + if (const_ptrs.length != new_field_count) { + new_struct_ptr->value->special = ConstValSpecialRuntime; + for (size_t i = 0; i < const_ptrs.length; i += 1) { + IrInstGen *elem_result_loc = const_ptrs.at(i); + assert(elem_result_loc->value->special == ConstValSpecialStatic); + if (elem_result_loc->value->type->data.pointer.inferred_struct_field != nullptr) { + // This field will be generated comptime; no need to do this. + continue; + } + IrInstGen *deref = ir_get_deref(ira, &elem_result_loc->base, elem_result_loc, nullptr); + if (!type_requires_comptime(ira->codegen, elem_result_loc->value->type->data.pointer.child_type)) { + elem_result_loc->value->special = ConstValSpecialRuntime; + } + IrInstGen *store_ptr_inst = ir_analyze_store_ptr( + ira, &elem_result_loc->base, elem_result_loc, deref, true); + if (type_is_invalid(store_ptr_inst->value->type)) + return ira->codegen->invalid_inst_gen; + } + } + + const_ptrs.deinit(); + + return ir_get_deref(ira, source_instr, new_struct_ptr, nullptr); +} + +static IrInstGen *ir_analyze_array_mult(IrAnalyze *ira, IrInstSrcBinOp *instruction) { + IrInstGen *op1 = instruction->op1->child; + if (type_is_invalid(op1->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *op2 = instruction->op2->child; + if (type_is_invalid(op2->value->type)) + return ira->codegen->invalid_inst_gen; + + bool want_ptr_to_array = false; + ZigType *array_type; + ZigValue *array_val; + if (op1->value->type->id == ZigTypeIdArray) { + array_type = op1->value->type; + array_val = ir_resolve_const(ira, op1, UndefOk); + if (array_val == nullptr) + return ira->codegen->invalid_inst_gen; + } else if (op1->value->type->id == ZigTypeIdPointer && + op1->value->type->data.pointer.ptr_len == PtrLenSingle && + op1->value->type->data.pointer.child_type->id == ZigTypeIdArray) + { + array_type = op1->value->type->data.pointer.child_type; + IrInstGen *array_inst = ir_get_deref(ira, &op1->base, op1, nullptr); + if (type_is_invalid(array_inst->value->type)) + return ira->codegen->invalid_inst_gen; + array_val = ir_resolve_const(ira, array_inst, UndefOk); + if (array_val == nullptr) + return ira->codegen->invalid_inst_gen; + want_ptr_to_array = true; + } else if (is_tuple(op1->value->type)) { + return ir_analyze_tuple_mult(ira, &instruction->base.base, op1, op2); + } else { + ir_add_error(ira, &op1->base, buf_sprintf("expected array type, found '%s'", buf_ptr(&op1->value->type->name))); + return ira->codegen->invalid_inst_gen; + } + + uint64_t mult_amt; + if (!ir_resolve_usize(ira, op2, &mult_amt)) + return ira->codegen->invalid_inst_gen; + + uint64_t old_array_len = array_type->data.array.len; + uint64_t new_array_len; + + if (mul_u64_overflow(old_array_len, mult_amt, &new_array_len)) { + ir_add_error(ira, &instruction->base.base, buf_sprintf("operation results in overflow")); + return ira->codegen->invalid_inst_gen; + } + + ZigType *child_type = array_type->data.array.child_type; + ZigType *result_array_type = get_array_type(ira->codegen, child_type, new_array_len, + array_type->data.array.sentinel); + + IrInstGen *array_result; + if (array_val->special == ConstValSpecialUndef || array_val->data.x_array.special == ConstArraySpecialUndef) { + array_result = ir_const_undef(ira, &instruction->base.base, result_array_type); + } else { + array_result = ir_const(ira, &instruction->base.base, result_array_type); + ZigValue *out_val = array_result->value; + + switch (type_has_one_possible_value(ira->codegen, result_array_type)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_inst_gen; + case OnePossibleValueYes: + goto skip_computation; + case OnePossibleValueNo: + break; + } + + // TODO optimize the buf case + expand_undef_array(ira->codegen, array_val); + size_t extra_null_term = (array_type->data.array.sentinel != nullptr) ? 1 : 0; + out_val->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(new_array_len + extra_null_term); + + uint64_t i = 0; + for (uint64_t x = 0; x < mult_amt; x += 1) { + for (uint64_t y = 0; y < old_array_len; y += 1) { + ZigValue *elem_dest_val = &out_val->data.x_array.data.s_none.elements[i]; + copy_const_val(ira->codegen, elem_dest_val, &array_val->data.x_array.data.s_none.elements[y]); + elem_dest_val->parent.id = ConstParentIdArray; + elem_dest_val->parent.data.p_array.array_val = out_val; + elem_dest_val->parent.data.p_array.elem_index = i; + i += 1; + } + } + assert(i == new_array_len); + + if (array_type->data.array.sentinel != nullptr) { + ZigValue *elem_dest_val = &out_val->data.x_array.data.s_none.elements[i]; + copy_const_val(ira->codegen, elem_dest_val, array_type->data.array.sentinel); + elem_dest_val->parent.id = ConstParentIdArray; + elem_dest_val->parent.data.p_array.array_val = out_val; + elem_dest_val->parent.data.p_array.elem_index = i; + i += 1; + } + } +skip_computation: + if (want_ptr_to_array) { + return ir_get_ref(ira, &instruction->base.base, array_result, true, false); + } else { + return array_result; + } +} + +static IrInstGen *ir_analyze_instruction_merge_err_sets(IrAnalyze *ira, + IrInstSrcMergeErrSets *instruction) +{ + ZigType *op1_type = ir_resolve_error_set_type(ira, &instruction->base.base, instruction->op1->child); + if (type_is_invalid(op1_type)) + return ira->codegen->invalid_inst_gen; + + ZigType *op2_type = ir_resolve_error_set_type(ira, &instruction->base.base, instruction->op2->child); + if (type_is_invalid(op2_type)) + return ira->codegen->invalid_inst_gen; + + if (!resolve_inferred_error_set(ira->codegen, op1_type, instruction->op1->child->base.source_node)) { + return ira->codegen->invalid_inst_gen; + } + + if (!resolve_inferred_error_set(ira->codegen, op2_type, instruction->op2->child->base.source_node)) { + return ira->codegen->invalid_inst_gen; + } + + if (type_is_global_error_set(op1_type) || + type_is_global_error_set(op2_type)) + { + return ir_const_type(ira, &instruction->base.base, ira->codegen->builtin_types.entry_global_error_set); + } + + size_t errors_count = ira->codegen->errors_by_index.length; + ErrorTableEntry **errors = heap::c_allocator.allocate(errors_count); + for (uint32_t i = 0, count = op1_type->data.error_set.err_count; i < count; i += 1) { + ErrorTableEntry *error_entry = op1_type->data.error_set.errors[i]; + assert(errors[error_entry->value] == nullptr); + errors[error_entry->value] = error_entry; + } + ZigType *result_type = get_error_set_union(ira->codegen, errors, op1_type, op2_type, instruction->type_name); + heap::c_allocator.deallocate(errors, errors_count); + + return ir_const_type(ira, &instruction->base.base, result_type); +} + + +static IrInstGen *ir_analyze_instruction_bin_op(IrAnalyze *ira, IrInstSrcBinOp *bin_op_instruction) { + IrBinOp op_id = bin_op_instruction->op_id; + switch (op_id) { + case IrBinOpInvalid: + zig_unreachable(); + case IrBinOpBoolOr: + case IrBinOpBoolAnd: + return ir_analyze_bin_op_bool(ira, bin_op_instruction); + case IrBinOpCmpEq: + case IrBinOpCmpNotEq: + case IrBinOpCmpLessThan: + case IrBinOpCmpGreaterThan: + case IrBinOpCmpLessOrEq: + case IrBinOpCmpGreaterOrEq: + return ir_analyze_bin_op_cmp(ira, bin_op_instruction); + case IrBinOpBitShiftLeftLossy: + case IrBinOpBitShiftLeftExact: + case IrBinOpBitShiftRightLossy: + case IrBinOpBitShiftRightExact: + return ir_analyze_bit_shift(ira, bin_op_instruction); + case IrBinOpBinOr: + case IrBinOpBinXor: + case IrBinOpBinAnd: + case IrBinOpAdd: + case IrBinOpAddWrap: + case IrBinOpSub: + case IrBinOpSubWrap: + case IrBinOpMult: + case IrBinOpMultWrap: + case IrBinOpDivUnspecified: + case IrBinOpDivTrunc: + case IrBinOpDivFloor: + case IrBinOpDivExact: + case IrBinOpRemUnspecified: + case IrBinOpRemRem: + case IrBinOpRemMod: + return ir_analyze_bin_op_math(ira, bin_op_instruction); + case IrBinOpArrayCat: + return ir_analyze_array_cat(ira, bin_op_instruction); + case IrBinOpArrayMult: + return ir_analyze_array_mult(ira, bin_op_instruction); + } + zig_unreachable(); +} + +static IrInstGen *ir_analyze_instruction_decl_var(IrAnalyze *ira, IrInstSrcDeclVar *decl_var_instruction) { + Error err; + ZigVar *var = decl_var_instruction->var; + + ZigType *explicit_type = nullptr; + IrInstGen *var_type = nullptr; + if (decl_var_instruction->var_type != nullptr) { + var_type = decl_var_instruction->var_type->child; + ZigType *proposed_type = ir_resolve_type(ira, var_type); + explicit_type = validate_var_type(ira->codegen, &var->decl_node->data.variable_declaration, proposed_type); + if (type_is_invalid(explicit_type)) { + var->var_type = ira->codegen->builtin_types.entry_invalid; + return ira->codegen->invalid_inst_gen; + } + } + + AstNode *source_node = decl_var_instruction->base.base.source_node; + + bool is_comptime_var = ir_get_var_is_comptime(var); + + bool var_class_requires_const = false; + + IrInstGen *var_ptr = decl_var_instruction->ptr->child; + // if this is null, a compiler error happened and did not initialize the variable. + // if there are no compile errors there may be a missing ir_expr_wrap in pass1 IR generation. + if (var_ptr == nullptr || type_is_invalid(var_ptr->value->type)) { + ir_assert(var_ptr != nullptr || ira->codegen->errors.length != 0, &decl_var_instruction->base.base); + var->var_type = ira->codegen->builtin_types.entry_invalid; + return ira->codegen->invalid_inst_gen; + } + + // The ir_build_var_decl_src call is supposed to pass a pointer to the allocation, not an initialization value. + ir_assert(var_ptr->value->type->id == ZigTypeIdPointer, &decl_var_instruction->base.base); + + ZigType *result_type = var_ptr->value->type->data.pointer.child_type; + if (type_is_invalid(result_type)) { + result_type = ira->codegen->builtin_types.entry_invalid; + } else if (result_type->id == ZigTypeIdUnreachable || result_type->id == ZigTypeIdOpaque) { + zig_unreachable(); + } + + ZigValue *init_val = nullptr; + if (instr_is_comptime(var_ptr) && var_ptr->value->data.x_ptr.mut != ConstPtrMutRuntimeVar) { + ZigValue *ptr_val = ir_resolve_const(ira, var_ptr, UndefBad); + if (ptr_val == nullptr) + return ira->codegen->invalid_inst_gen; + + init_val = const_ptr_pointee(ira, ira->codegen, ptr_val, decl_var_instruction->base.base.source_node); + if (init_val == nullptr) + return ira->codegen->invalid_inst_gen; + + if (is_comptime_var) { + if (var->gen_is_const) { + var->const_value = init_val; + } else { + var->const_value = ira->codegen->pass1_arena->create(); + copy_const_val(ira->codegen, var->const_value, init_val); + } + } + } + + switch (type_requires_comptime(ira->codegen, result_type)) { + case ReqCompTimeInvalid: + result_type = ira->codegen->builtin_types.entry_invalid; + break; + case ReqCompTimeYes: + var_class_requires_const = true; + if (!var->gen_is_const && !is_comptime_var) { + ir_add_error_node(ira, source_node, + buf_sprintf("variable of type '%s' must be const or comptime", + buf_ptr(&result_type->name))); + result_type = ira->codegen->builtin_types.entry_invalid; + } + break; + case ReqCompTimeNo: + if (init_val != nullptr && value_is_comptime(init_val)) { + if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, + decl_var_instruction->base.base.source_node, init_val, UndefOk))) + { + result_type = ira->codegen->builtin_types.entry_invalid; + } else if (init_val->type->id == ZigTypeIdFn && + init_val->special != ConstValSpecialUndef && + init_val->data.x_ptr.special == ConstPtrSpecialFunction && + init_val->data.x_ptr.data.fn.fn_entry->fn_inline == FnInlineAlways) + { + var_class_requires_const = true; + if (!var->src_is_const && !is_comptime_var) { + ErrorMsg *msg = ir_add_error_node(ira, source_node, + buf_sprintf("functions marked inline must be stored in const or comptime var")); + AstNode *proto_node = init_val->data.x_ptr.data.fn.fn_entry->proto_node; + add_error_note(ira->codegen, msg, proto_node, buf_sprintf("declared here")); + result_type = ira->codegen->builtin_types.entry_invalid; + } + } + } + break; + } + + while (var->next_var != nullptr) { + var = var->next_var; + } + + // This must be done after possibly creating a new variable above + var->ref_count = 0; + + var->ptr_instruction = var_ptr; + var->var_type = result_type; + assert(var->var_type); + + if (type_is_invalid(result_type)) { + return ir_const_void(ira, &decl_var_instruction->base.base); + } + + if (decl_var_instruction->align_value == nullptr) { + if ((err = type_resolve(ira->codegen, result_type, ResolveStatusAlignmentKnown))) { + var->var_type = ira->codegen->builtin_types.entry_invalid; + return ir_const_void(ira, &decl_var_instruction->base.base); + } + var->align_bytes = get_ptr_align(ira->codegen, var_ptr->value->type); + } else { + if (!ir_resolve_align(ira, decl_var_instruction->align_value->child, nullptr, &var->align_bytes)) { + var->var_type = ira->codegen->builtin_types.entry_invalid; + } + } + + if (init_val != nullptr && value_is_comptime(init_val)) { + // Resolve ConstPtrMutInfer + if (var->gen_is_const) { + var_ptr->value->data.x_ptr.mut = ConstPtrMutComptimeConst; + } else if (is_comptime_var) { + var_ptr->value->data.x_ptr.mut = ConstPtrMutComptimeVar; + } else { + // we need a runtime ptr but we have a comptime val. + // since it's a comptime val there are no instructions for it. + // we memcpy the init value here + IrInstGen *deref = ir_get_deref(ira, &var_ptr->base, var_ptr, nullptr); + if (type_is_invalid(deref->value->type)) { + var->var_type = ira->codegen->builtin_types.entry_invalid; + return ira->codegen->invalid_inst_gen; + } + // If this assertion trips, something is wrong with the IR instructions, because + // we expected the above deref to return a constant value, but it created a runtime + // instruction. + assert(deref->value->special != ConstValSpecialRuntime); + var_ptr->value->special = ConstValSpecialRuntime; + ir_analyze_store_ptr(ira, &var_ptr->base, var_ptr, deref, false); + } + if (instr_is_comptime(var_ptr) && (is_comptime_var || (var_class_requires_const && var->gen_is_const))) { + return ir_const_void(ira, &decl_var_instruction->base.base); + } + } else if (is_comptime_var) { + ir_add_error(ira, &decl_var_instruction->base.base, + buf_sprintf("cannot store runtime value in compile time variable")); + var->var_type = ira->codegen->builtin_types.entry_invalid; + return ira->codegen->invalid_inst_gen; + } + + ZigFn *fn_entry = ira->new_irb.exec->fn_entry; + if (fn_entry) + fn_entry->variable_list.append(var); + + return ir_build_var_decl_gen(ira, &decl_var_instruction->base.base, var, var_ptr); +} + +static IrInstGen *ir_analyze_instruction_export(IrAnalyze *ira, IrInstSrcExport *instruction) { + IrInstGen *target = instruction->target->child; + if (type_is_invalid(target->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *options = instruction->options->child; + if (type_is_invalid(options->value->type)) + return ira->codegen->invalid_inst_gen; + + ZigType *options_type = options->value->type; + assert(options_type->id == ZigTypeIdStruct); + + TypeStructField *name_field = find_struct_type_field(options_type, buf_create_from_str("name")); + ir_assert(name_field != nullptr, &instruction->base.base); + IrInstGen *name_inst = ir_analyze_struct_value_field_value(ira, &instruction->base.base, options, name_field); + if (type_is_invalid(name_inst->value->type)) + return ira->codegen->invalid_inst_gen; + + TypeStructField *linkage_field = find_struct_type_field(options_type, buf_create_from_str("linkage")); + ir_assert(linkage_field != nullptr, &instruction->base.base); + IrInstGen *linkage_inst = ir_analyze_struct_value_field_value(ira, &instruction->base.base, options, linkage_field); + if (type_is_invalid(linkage_inst->value->type)) + return ira->codegen->invalid_inst_gen; + + TypeStructField *section_field = find_struct_type_field(options_type, buf_create_from_str("section")); + ir_assert(section_field != nullptr, &instruction->base.base); + IrInstGen *section_inst = ir_analyze_struct_value_field_value(ira, &instruction->base.base, options, section_field); + if (type_is_invalid(section_inst->value->type)) + return ira->codegen->invalid_inst_gen; + + // The `section` field is optional, we have to unwrap it first + IrInstGen *non_null_check = ir_analyze_test_non_null(ira, &instruction->base.base, section_inst); + bool is_non_null; + if (!ir_resolve_bool(ira, non_null_check, &is_non_null)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *section_str_inst = nullptr; + if (is_non_null) { + section_str_inst = ir_analyze_optional_value_payload_value(ira, &instruction->base.base, section_inst, false); + if (type_is_invalid(section_str_inst->value->type)) + return ira->codegen->invalid_inst_gen; + } + + // Resolve all the comptime values + Buf *symbol_name = ir_resolve_str(ira, name_inst); + if (!symbol_name) + return ira->codegen->invalid_inst_gen; + + if (buf_len(symbol_name) < 1) { + ir_add_error(ira, &name_inst->base, + buf_sprintf("exported symbol name cannot be empty")); + return ira->codegen->invalid_inst_gen; + } + + GlobalLinkageId global_linkage_id; + if (!ir_resolve_global_linkage(ira, linkage_inst, &global_linkage_id)) + return ira->codegen->invalid_inst_gen; + + Buf *section_name = nullptr; + if (section_str_inst != nullptr && !(section_name = ir_resolve_str(ira, section_str_inst))) + return ira->codegen->invalid_inst_gen; + + // TODO: This function needs to be audited. + // It's not clear how all the different types are supposed to be handled. + // Need comprehensive tests for exporting one thing in one file and declaring an extern var + // in another file. + TldFn *tld_fn = heap::c_allocator.create(); + tld_fn->base.id = TldIdFn; + tld_fn->base.source_node = instruction->base.base.source_node; + + auto entry = ira->codegen->exported_symbol_names.put_unique(symbol_name, &tld_fn->base); + if (entry) { + AstNode *other_export_node = entry->value->source_node; + ErrorMsg *msg = ir_add_error(ira, &instruction->base.base, + buf_sprintf("exported symbol collision: '%s'", buf_ptr(symbol_name))); + add_error_note(ira->codegen, msg, other_export_node, buf_sprintf("other symbol is here")); + return ira->codegen->invalid_inst_gen; + } + + Error err; + bool want_var_export = false; + switch (target->value->type->id) { + case ZigTypeIdInvalid: + case ZigTypeIdUnreachable: + zig_unreachable(); + case ZigTypeIdFn: { + assert(target->value->data.x_ptr.special == ConstPtrSpecialFunction); + ZigFn *fn_entry = target->value->data.x_ptr.data.fn.fn_entry; + tld_fn->fn_entry = fn_entry; + CallingConvention cc = fn_entry->type_entry->data.fn.fn_type_id.cc; + switch (cc) { + case CallingConventionUnspecified: { + ErrorMsg *msg = ir_add_error(ira, &target->base, + buf_sprintf("exported function must specify calling convention")); + add_error_note(ira->codegen, msg, fn_entry->proto_node, buf_sprintf("declared here")); + } break; + case CallingConventionAsync: { + ErrorMsg *msg = ir_add_error(ira, &target->base, + buf_sprintf("exported function cannot be async")); + add_error_note(ira->codegen, msg, fn_entry->proto_node, buf_sprintf("declared here")); + } break; + 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(ira->codegen, fn_entry, buf_ptr(symbol_name), global_linkage_id, cc); + fn_entry->section_name = section_name; + break; + } + } break; + case ZigTypeIdStruct: + if (is_slice(target->value->type)) { + ir_add_error(ira, &target->base, + buf_sprintf("unable to export value of type '%s'", buf_ptr(&target->value->type->name))); + } else if (target->value->type->data.structure.layout != ContainerLayoutExtern) { + ErrorMsg *msg = ir_add_error(ira, &target->base, + buf_sprintf("exported struct value must be declared extern")); + add_error_note(ira->codegen, msg, target->value->type->data.structure.decl_node, buf_sprintf("declared here")); + } else { + want_var_export = true; + } + break; + case ZigTypeIdUnion: + if (target->value->type->data.unionation.layout != ContainerLayoutExtern) { + ErrorMsg *msg = ir_add_error(ira, &target->base, + buf_sprintf("exported union value must be declared extern")); + add_error_note(ira->codegen, msg, target->value->type->data.unionation.decl_node, buf_sprintf("declared here")); + } else { + want_var_export = true; + } + break; + case ZigTypeIdEnum: + if (target->value->type->data.enumeration.layout != ContainerLayoutExtern) { + ErrorMsg *msg = ir_add_error(ira, &target->base, + buf_sprintf("exported enum value must be declared extern")); + add_error_note(ira->codegen, msg, target->value->type->data.enumeration.decl_node, buf_sprintf("declared here")); + } else { + want_var_export = true; + } + break; + case ZigTypeIdArray: { + bool ok_type; + if ((err = type_allowed_in_extern(ira->codegen, target->value->type->data.array.child_type, &ok_type))) + return ira->codegen->invalid_inst_gen; + + if (!ok_type) { + ir_add_error(ira, &target->base, + buf_sprintf("array element type '%s' not extern-compatible", + buf_ptr(&target->value->type->data.array.child_type->name))); + } else { + want_var_export = true; + } + break; + } + case ZigTypeIdMetaType: { + ZigType *type_value = target->value->data.x_type; + switch (type_value->id) { + case ZigTypeIdInvalid: + zig_unreachable(); + case ZigTypeIdStruct: + if (is_slice(type_value)) { + ir_add_error(ira, &target->base, + buf_sprintf("unable to export type '%s'", buf_ptr(&type_value->name))); + } else if (type_value->data.structure.layout != ContainerLayoutExtern) { + ErrorMsg *msg = ir_add_error(ira, &target->base, + buf_sprintf("exported struct must be declared extern")); + add_error_note(ira->codegen, msg, type_value->data.structure.decl_node, buf_sprintf("declared here")); + } + break; + case ZigTypeIdUnion: + if (type_value->data.unionation.layout != ContainerLayoutExtern) { + ErrorMsg *msg = ir_add_error(ira, &target->base, + buf_sprintf("exported union must be declared extern")); + add_error_note(ira->codegen, msg, type_value->data.unionation.decl_node, buf_sprintf("declared here")); + } + break; + case ZigTypeIdEnum: + if (type_value->data.enumeration.layout != ContainerLayoutExtern) { + ErrorMsg *msg = ir_add_error(ira, &target->base, + buf_sprintf("exported enum must be declared extern")); + add_error_note(ira->codegen, msg, type_value->data.enumeration.decl_node, buf_sprintf("declared here")); + } + break; + case ZigTypeIdFn: { + if (type_value->data.fn.fn_type_id.cc == CallingConventionUnspecified) { + ir_add_error(ira, &target->base, + buf_sprintf("exported function type must specify calling convention")); + } + } break; + case ZigTypeIdInt: + case ZigTypeIdFloat: + case ZigTypeIdPointer: + case ZigTypeIdArray: + case ZigTypeIdBool: + case ZigTypeIdVector: + break; + case ZigTypeIdMetaType: + case ZigTypeIdVoid: + case ZigTypeIdUnreachable: + case ZigTypeIdComptimeFloat: + case ZigTypeIdComptimeInt: + case ZigTypeIdEnumLiteral: + case ZigTypeIdUndefined: + case ZigTypeIdNull: + case ZigTypeIdOptional: + case ZigTypeIdErrorUnion: + case ZigTypeIdErrorSet: + case ZigTypeIdBoundFn: + case ZigTypeIdOpaque: + case ZigTypeIdFnFrame: + case ZigTypeIdAnyFrame: + ir_add_error(ira, &target->base, + buf_sprintf("invalid export target '%s'", buf_ptr(&type_value->name))); + break; + } + } break; + case ZigTypeIdInt: + want_var_export = true; + break; + case ZigTypeIdVoid: + case ZigTypeIdBool: + case ZigTypeIdFloat: + case ZigTypeIdPointer: + case ZigTypeIdComptimeFloat: + case ZigTypeIdComptimeInt: + case ZigTypeIdUndefined: + case ZigTypeIdNull: + case ZigTypeIdOptional: + case ZigTypeIdErrorUnion: + case ZigTypeIdErrorSet: + case ZigTypeIdVector: + zig_panic("TODO export const value of type %s", buf_ptr(&target->value->type->name)); + case ZigTypeIdBoundFn: + case ZigTypeIdOpaque: + case ZigTypeIdEnumLiteral: + case ZigTypeIdFnFrame: + case ZigTypeIdAnyFrame: + ir_add_error(ira, &target->base, + buf_sprintf("invalid export target type '%s'", buf_ptr(&target->value->type->name))); + break; + } + + // TODO audit the various ways to use @export + if (want_var_export && target->id == IrInstGenIdLoadPtr) { + IrInstGenLoadPtr *load_ptr = reinterpret_cast(target); + if (load_ptr->ptr->id == IrInstGenIdVarPtr) { + IrInstGenVarPtr *var_ptr = reinterpret_cast(load_ptr->ptr); + ZigVar *var = var_ptr->var; + add_var_export(ira->codegen, var, buf_ptr(symbol_name), global_linkage_id); + var->section_name = section_name; + } + } + + return ir_const_void(ira, &instruction->base.base); +} + +static bool exec_has_err_ret_trace(CodeGen *g, IrExecutableSrc *exec) { + ZigFn *fn_entry = exec_fn_entry(exec); + return fn_entry != nullptr && fn_entry->calls_or_awaits_errorable_fn && g->have_err_ret_tracing; +} + +static IrInstGen *ir_analyze_instruction_error_return_trace(IrAnalyze *ira, + IrInstSrcErrorReturnTrace *instruction) +{ + ZigType *ptr_to_stack_trace_type = get_pointer_to_type(ira->codegen, get_stack_trace_type(ira->codegen), false); + if (instruction->optional == IrInstErrorReturnTraceNull) { + ZigType *optional_type = get_optional_type(ira->codegen, ptr_to_stack_trace_type); + if (!exec_has_err_ret_trace(ira->codegen, ira->old_irb.exec)) { + IrInstGen *result = ir_const(ira, &instruction->base.base, optional_type); + ZigValue *out_val = result->value; + assert(get_src_ptr_type(optional_type) != nullptr); + out_val->data.x_ptr.special = ConstPtrSpecialHardCodedAddr; + out_val->data.x_ptr.data.hard_coded_addr.addr = 0; + return result; + } + return ir_build_error_return_trace_gen(ira, instruction->base.base.scope, + instruction->base.base.source_node, instruction->optional, optional_type); + } else { + assert(ira->codegen->have_err_ret_tracing); + return ir_build_error_return_trace_gen(ira, instruction->base.base.scope, + instruction->base.base.source_node, instruction->optional, ptr_to_stack_trace_type); + } +} + +static IrInstGen *ir_analyze_instruction_error_union(IrAnalyze *ira, IrInstSrcErrorUnion *instruction) { + IrInstGen *result = ir_const(ira, &instruction->base.base, ira->codegen->builtin_types.entry_type); + result->value->special = ConstValSpecialLazy; + + LazyValueErrUnionType *lazy_err_union_type = heap::c_allocator.create(); + lazy_err_union_type->ira = ira; ira_ref(ira); + result->value->data.x_lazy = &lazy_err_union_type->base; + lazy_err_union_type->base.id = LazyValueIdErrUnionType; + + lazy_err_union_type->err_set_type = instruction->err_set->child; + if (ir_resolve_type_lazy(ira, lazy_err_union_type->err_set_type) == nullptr) + return ira->codegen->invalid_inst_gen; + + lazy_err_union_type->payload_type = instruction->payload->child; + if (ir_resolve_type_lazy(ira, lazy_err_union_type->payload_type) == nullptr) + return ira->codegen->invalid_inst_gen; + + return result; +} + +static IrInstGen *ir_analyze_alloca(IrAnalyze *ira, IrInst *source_inst, ZigType *var_type, + uint32_t align, const char *name_hint, bool force_comptime) +{ + Error err; + + ZigValue *pointee = ira->codegen->pass1_arena->create(); + pointee->special = ConstValSpecialUndef; + pointee->llvm_align = align; + + IrInstGenAlloca *result = ir_build_alloca_gen(ira, source_inst, align, name_hint); + result->base.value->special = ConstValSpecialStatic; + result->base.value->data.x_ptr.special = ConstPtrSpecialRef; + result->base.value->data.x_ptr.mut = force_comptime ? ConstPtrMutComptimeVar : ConstPtrMutInfer; + result->base.value->data.x_ptr.data.ref.pointee = pointee; + + bool var_type_has_bits; + if ((err = type_has_bits2(ira->codegen, var_type, &var_type_has_bits))) + return ira->codegen->invalid_inst_gen; + if (align != 0) { + if ((err = type_resolve(ira->codegen, var_type, ResolveStatusAlignmentKnown))) + return ira->codegen->invalid_inst_gen; + if (!var_type_has_bits) { + ir_add_error(ira, source_inst, + buf_sprintf("variable '%s' of zero-bit type '%s' has no in-memory representation, it cannot be aligned", + name_hint, buf_ptr(&var_type->name))); + return ira->codegen->invalid_inst_gen; + } + } + assert(result->base.value->data.x_ptr.special != ConstPtrSpecialInvalid); + + pointee->type = var_type; + result->base.value->type = get_pointer_to_type_extra(ira->codegen, var_type, false, false, + PtrLenSingle, align, 0, 0, false); + + if (!force_comptime) { + ZigFn *fn_entry = ira->new_irb.exec->fn_entry; + if (fn_entry != nullptr) { + fn_entry->alloca_gen_list.append(result); + } + } + return &result->base; +} + +static ZigType *ir_result_loc_expected_type(IrAnalyze *ira, IrInst *suspend_source_instr, + ResultLoc *result_loc) +{ + switch (result_loc->id) { + case ResultLocIdInvalid: + case ResultLocIdPeerParent: + zig_unreachable(); + case ResultLocIdNone: + case ResultLocIdVar: + case ResultLocIdBitCast: + case ResultLocIdCast: + return nullptr; + case ResultLocIdInstruction: + return result_loc->source_instruction->child->value->type; + case ResultLocIdReturn: + return ira->explicit_return_type; + case ResultLocIdPeer: + return reinterpret_cast(result_loc)->parent->resolved_type; + } + zig_unreachable(); +} + +static bool type_can_bit_cast(ZigType *t) { + switch (t->id) { + case ZigTypeIdInvalid: + zig_unreachable(); + case ZigTypeIdMetaType: + case ZigTypeIdOpaque: + case ZigTypeIdBoundFn: + case ZigTypeIdUnreachable: + case ZigTypeIdComptimeFloat: + case ZigTypeIdComptimeInt: + case ZigTypeIdEnumLiteral: + case ZigTypeIdUndefined: + case ZigTypeIdNull: + case ZigTypeIdPointer: + return false; + default: + // TODO list these types out explicitly, there are probably some other invalid ones here + return true; + } +} + +static void set_up_result_loc_for_inferred_comptime(IrAnalyze *ira, IrInstGen *ptr) { + ZigValue *undef_child = ira->codegen->pass1_arena->create(); + undef_child->type = ptr->value->type->data.pointer.child_type; + undef_child->special = ConstValSpecialUndef; + ptr->value->special = ConstValSpecialStatic; + ptr->value->data.x_ptr.mut = ConstPtrMutInfer; + ptr->value->data.x_ptr.special = ConstPtrSpecialRef; + ptr->value->data.x_ptr.data.ref.pointee = undef_child; +} + +static Error ir_result_has_type(IrAnalyze *ira, ResultLoc *result_loc, bool *out) { + switch (result_loc->id) { + case ResultLocIdInvalid: + case ResultLocIdPeerParent: + zig_unreachable(); + case ResultLocIdNone: + case ResultLocIdPeer: + *out = false; + return ErrorNone; + case ResultLocIdReturn: + case ResultLocIdInstruction: + case ResultLocIdBitCast: + *out = true; + return ErrorNone; + case ResultLocIdCast: { + ResultLocCast *result_cast = reinterpret_cast(result_loc); + ZigType *dest_type = ir_resolve_type(ira, result_cast->base.source_instruction->child); + if (type_is_invalid(dest_type)) + return ErrorSemanticAnalyzeFail; + *out = (dest_type != ira->codegen->builtin_types.entry_anytype); + return ErrorNone; + } + case ResultLocIdVar: + *out = reinterpret_cast(result_loc)->var->decl_node->data.variable_declaration.type != nullptr; + return ErrorNone; + } + zig_unreachable(); +} + +static IrInstGen *ir_resolve_no_result_loc(IrAnalyze *ira, IrInst *suspend_source_instr, + ResultLoc *result_loc, ZigType *value_type) +{ + if (type_is_invalid(value_type)) + return ira->codegen->invalid_inst_gen; + IrInstGenAlloca *alloca_gen = ir_build_alloca_gen(ira, suspend_source_instr, 0, ""); + alloca_gen->base.value->type = get_pointer_to_type_extra(ira->codegen, value_type, false, false, + PtrLenSingle, 0, 0, 0, false); + set_up_result_loc_for_inferred_comptime(ira, &alloca_gen->base); + ZigFn *fn_entry = ira->new_irb.exec->fn_entry; + if (fn_entry != nullptr && get_scope_typeof(suspend_source_instr->scope) == nullptr) { + fn_entry->alloca_gen_list.append(alloca_gen); + } + result_loc->written = true; + result_loc->resolved_loc = &alloca_gen->base; + return result_loc->resolved_loc; +} + +static bool result_loc_is_discard(ResultLoc *result_loc_pass1) { + if (result_loc_pass1->id == ResultLocIdInstruction && + result_loc_pass1->source_instruction->id == IrInstSrcIdConst) + { + IrInstSrcConst *const_inst = reinterpret_cast(result_loc_pass1->source_instruction); + if (value_is_comptime(const_inst->value) && + const_inst->value->type->id == ZigTypeIdPointer && + const_inst->value->data.x_ptr.special == ConstPtrSpecialDiscard) + { + return true; + } + } + return false; +} + +// when calling this function, at the callsite must check for result type noreturn and propagate it up +static IrInstGen *ir_resolve_result_raw(IrAnalyze *ira, IrInst *suspend_source_instr, + ResultLoc *result_loc, ZigType *value_type, IrInstGen *value, bool force_runtime, + bool allow_discard) +{ + Error err; + if (result_loc->resolved_loc != nullptr) { + // allow to redo the result location if the value is known and comptime and the previous one isn't + if (value == nullptr || !instr_is_comptime(value) || instr_is_comptime(result_loc->resolved_loc)) { + return result_loc->resolved_loc; + } + } + result_loc->gen_instruction = value; + result_loc->implicit_elem_type = value_type; + switch (result_loc->id) { + case ResultLocIdInvalid: + case ResultLocIdPeerParent: + zig_unreachable(); + case ResultLocIdNone: { + if (value != nullptr) { + return nullptr; + } + // need to return a result location and don't have one. use a stack allocation + return ir_resolve_no_result_loc(ira, suspend_source_instr, result_loc, value_type); + } + case ResultLocIdVar: { + ResultLocVar *result_loc_var = reinterpret_cast(result_loc); + assert(result_loc->source_instruction->id == IrInstSrcIdAlloca); + IrInstSrcAlloca *alloca_src = reinterpret_cast(result_loc->source_instruction); + + ZigVar *var = result_loc_var->var; + if (var->var_type != nullptr && !ir_get_var_is_comptime(var)) { + // This is at least the second time we've seen this variable declaration during analysis. + // This means that this is actually a different variable due to, e.g. an inline while loop. + // We make a new variable so that it can hold a different type, and so the debug info can + // be distinct. + ZigVar *new_var = create_local_var(ira->codegen, var->decl_node, var->child_scope, + buf_create_from_str(var->name), var->src_is_const, var->gen_is_const, + var->shadowable, var->is_comptime, true); + new_var->align_bytes = var->align_bytes; + + var->next_var = new_var; + var = new_var; + } + if (value_type->id == ZigTypeIdUnreachable || value_type->id == ZigTypeIdOpaque) { + ir_add_error(ira, &result_loc->source_instruction->base, + buf_sprintf("variable of type '%s' not allowed", buf_ptr(&value_type->name))); + return ira->codegen->invalid_inst_gen; + } + if (alloca_src->base.child == nullptr || var->ptr_instruction == nullptr) { + bool force_comptime; + if (!ir_resolve_comptime(ira, alloca_src->is_comptime->child, &force_comptime)) + return ira->codegen->invalid_inst_gen; + uint32_t align = 0; + if (alloca_src->align != nullptr && !ir_resolve_align(ira, alloca_src->align->child, nullptr, &align)) { + return ira->codegen->invalid_inst_gen; + } + IrInstGen *alloca_gen = ir_analyze_alloca(ira, &result_loc->source_instruction->base, value_type, + align, alloca_src->name_hint, force_comptime); + if (force_runtime) { + alloca_gen->value->data.x_ptr.mut = ConstPtrMutRuntimeVar; + alloca_gen->value->special = ConstValSpecialRuntime; + } + if (alloca_src->base.child != nullptr && !result_loc->written) { + alloca_src->base.child->base.ref_count = 0; + } + alloca_src->base.child = alloca_gen; + var->ptr_instruction = alloca_gen; + } + result_loc->written = true; + result_loc->resolved_loc = alloca_src->base.child; + return alloca_src->base.child; + } + case ResultLocIdInstruction: { + result_loc->written = true; + result_loc->resolved_loc = result_loc->source_instruction->child; + return result_loc->resolved_loc; + } + case ResultLocIdReturn: { + if (value != nullptr) { + reinterpret_cast(result_loc)->implicit_return_type_done = true; + ira->src_implicit_return_type_list.append(value); + } + result_loc->written = true; + result_loc->resolved_loc = ira->return_ptr; + return result_loc->resolved_loc; + } + case ResultLocIdPeer: { + ResultLocPeer *result_peer = reinterpret_cast(result_loc); + ResultLocPeerParent *peer_parent = result_peer->parent; + + if (peer_parent->peers.length == 1) { + IrInstGen *parent_result_loc = ir_resolve_result(ira, suspend_source_instr, peer_parent->parent, + value_type, value, force_runtime, true); + result_peer->suspend_pos.basic_block_index = SIZE_MAX; + result_peer->suspend_pos.instruction_index = SIZE_MAX; + if (parent_result_loc == nullptr || type_is_invalid(parent_result_loc->value->type) || + parent_result_loc->value->type->id == ZigTypeIdUnreachable) + { + return parent_result_loc; + } + result_loc->written = true; + result_loc->resolved_loc = parent_result_loc; + return result_loc->resolved_loc; + } + + bool is_condition_comptime; + if (!ir_resolve_comptime(ira, peer_parent->is_comptime->child, &is_condition_comptime)) + return ira->codegen->invalid_inst_gen; + if (is_condition_comptime) { + peer_parent->skipped = true; + return ir_resolve_result(ira, suspend_source_instr, peer_parent->parent, + value_type, value, force_runtime, true); + } + bool peer_parent_has_type; + if ((err = ir_result_has_type(ira, peer_parent->parent, &peer_parent_has_type))) + return ira->codegen->invalid_inst_gen; + if (peer_parent_has_type) { + peer_parent->skipped = true; + IrInstGen *parent_result_loc = ir_resolve_result(ira, suspend_source_instr, peer_parent->parent, + value_type, value, force_runtime || !is_condition_comptime, true); + if (parent_result_loc == nullptr || type_is_invalid(parent_result_loc->value->type) || + parent_result_loc->value->type->id == ZigTypeIdUnreachable) + { + return parent_result_loc; + } + peer_parent->parent->written = true; + result_loc->written = true; + result_loc->resolved_loc = parent_result_loc; + return result_loc->resolved_loc; + } + + if (peer_parent->resolved_type == nullptr) { + if (peer_parent->end_bb->suspend_instruction_ref == nullptr) { + peer_parent->end_bb->suspend_instruction_ref = suspend_source_instr; + } + IrInstGen *unreach_inst = ira_suspend(ira, suspend_source_instr, result_peer->next_bb, + &result_peer->suspend_pos); + if (result_peer->next_bb == nullptr) { + ir_start_next_bb(ira); + } + return unreach_inst; + } + + IrInstGen *parent_result_loc = ir_resolve_result(ira, suspend_source_instr, peer_parent->parent, + peer_parent->resolved_type, nullptr, force_runtime, true); + if (parent_result_loc == nullptr || type_is_invalid(parent_result_loc->value->type) || + parent_result_loc->value->type->id == ZigTypeIdUnreachable) + { + return parent_result_loc; + } + // because is_condition_comptime is false, we mark this a runtime pointer + parent_result_loc->value->special = ConstValSpecialRuntime; + result_loc->written = true; + result_loc->resolved_loc = parent_result_loc; + return result_loc->resolved_loc; + } + case ResultLocIdCast: { + ResultLocCast *result_cast = reinterpret_cast(result_loc); + ZigType *dest_type = ir_resolve_type(ira, result_cast->base.source_instruction->child); + if (type_is_invalid(dest_type)) + return ira->codegen->invalid_inst_gen; + + if (dest_type == ira->codegen->builtin_types.entry_anytype) { + return ir_resolve_no_result_loc(ira, suspend_source_instr, result_loc, value_type); + } + + IrInstGen *casted_value; + if (value != nullptr) { + casted_value = ir_implicit_cast2(ira, suspend_source_instr, value, dest_type); + if (type_is_invalid(casted_value->value->type)) + return ira->codegen->invalid_inst_gen; + dest_type = casted_value->value->type; + } else { + casted_value = nullptr; + } + + IrInstGen *parent_result_loc = ir_resolve_result(ira, suspend_source_instr, result_cast->parent, + dest_type, casted_value, force_runtime, true); + if (parent_result_loc == nullptr || type_is_invalid(parent_result_loc->value->type) || + parent_result_loc->value->type->id == ZigTypeIdUnreachable) + { + return parent_result_loc; + } + + ZigType *parent_ptr_type = parent_result_loc->value->type; + assert(parent_ptr_type->id == ZigTypeIdPointer); + + if ((err = type_resolve(ira->codegen, parent_ptr_type->data.pointer.child_type, + ResolveStatusAlignmentKnown))) + { + return ira->codegen->invalid_inst_gen; + } + uint64_t parent_ptr_align = get_ptr_align(ira->codegen, parent_ptr_type); + if ((err = type_resolve(ira->codegen, value_type, ResolveStatusAlignmentKnown))) { + return ira->codegen->invalid_inst_gen; + } + if (!type_has_bits(ira->codegen, value_type)) { + parent_ptr_align = 0; + } + // If we're casting from a sentinel-terminated array to a non-sentinel-terminated array, + // we actually need the result location pointer to *not* have a sentinel. Otherwise the generated + // memcpy will write an extra byte to the destination, and THAT'S NO GOOD. + ZigType *ptr_elem_type; + if (value_type->id == ZigTypeIdArray && value_type->data.array.sentinel != nullptr && + dest_type->id == ZigTypeIdArray && dest_type->data.array.sentinel == nullptr) + { + ptr_elem_type = get_array_type(ira->codegen, value_type->data.array.child_type, + value_type->data.array.len, nullptr); + } else { + ptr_elem_type = value_type; + } + ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, ptr_elem_type, + parent_ptr_type->data.pointer.is_const, parent_ptr_type->data.pointer.is_volatile, PtrLenSingle, + parent_ptr_align, 0, 0, parent_ptr_type->data.pointer.allow_zero); + + ConstCastOnly const_cast_result = types_match_const_cast_only(ira, + parent_result_loc->value->type, ptr_type, + result_cast->base.source_instruction->base.source_node, false); + if (const_cast_result.id == ConstCastResultIdInvalid) + return ira->codegen->invalid_inst_gen; + if (const_cast_result.id != ConstCastResultIdOk) { + if (allow_discard) { + return parent_result_loc; + } + // We will not be able to provide a result location for this value. Create + // a new result location. + result_cast->parent->written = false; + return ir_resolve_no_result_loc(ira, suspend_source_instr, result_loc, value_type); + } + + result_loc->written = true; + result_loc->resolved_loc = ir_analyze_ptr_cast(ira, suspend_source_instr, parent_result_loc, + &parent_result_loc->base, ptr_type, &result_cast->base.source_instruction->base, false, false); + return result_loc->resolved_loc; + } + case ResultLocIdBitCast: { + ResultLocBitCast *result_bit_cast = reinterpret_cast(result_loc); + ZigType *dest_type = ir_resolve_type(ira, result_bit_cast->base.source_instruction->child); + if (type_is_invalid(dest_type)) + return ira->codegen->invalid_inst_gen; + + ZigType *dest_cg_ptr_type; + if ((err = get_codegen_ptr_type(ira->codegen, dest_type, &dest_cg_ptr_type))) + return ira->codegen->invalid_inst_gen; + if (dest_cg_ptr_type != nullptr) { + ir_add_error(ira, &result_loc->source_instruction->base, + buf_sprintf("unable to @bitCast to pointer type '%s'", buf_ptr(&dest_type->name))); + return ira->codegen->invalid_inst_gen; + } + + if (!type_can_bit_cast(dest_type)) { + ir_add_error(ira, &result_loc->source_instruction->base, + buf_sprintf("unable to @bitCast to type '%s'", buf_ptr(&dest_type->name))); + return ira->codegen->invalid_inst_gen; + } + + ZigType *value_cg_ptr_type; + if ((err = get_codegen_ptr_type(ira->codegen, value_type, &value_cg_ptr_type))) + return ira->codegen->invalid_inst_gen; + if (value_cg_ptr_type != nullptr) { + ir_add_error(ira, suspend_source_instr, + buf_sprintf("unable to @bitCast from pointer type '%s'", buf_ptr(&value_type->name))); + return ira->codegen->invalid_inst_gen; + } + + if (!type_can_bit_cast(value_type)) { + ir_add_error(ira, suspend_source_instr, + buf_sprintf("unable to @bitCast from type '%s'", buf_ptr(&value_type->name))); + return ira->codegen->invalid_inst_gen; + } + + IrInstGen *bitcasted_value; + if (value != nullptr) { + bitcasted_value = ir_analyze_bit_cast(ira, &result_loc->source_instruction->base, value, dest_type); + dest_type = bitcasted_value->value->type; + } else { + bitcasted_value = nullptr; + } + + if (bitcasted_value != nullptr && type_is_invalid(bitcasted_value->value->type)) { + return bitcasted_value; + } + + bool parent_was_written = result_bit_cast->parent->written; + IrInstGen *parent_result_loc = ir_resolve_result(ira, suspend_source_instr, result_bit_cast->parent, + dest_type, bitcasted_value, force_runtime, true); + if (parent_result_loc == nullptr || type_is_invalid(parent_result_loc->value->type) || + parent_result_loc->value->type->id == ZigTypeIdUnreachable) + { + return parent_result_loc; + } + ZigType *parent_ptr_type = parent_result_loc->value->type; + assert(parent_ptr_type->id == ZigTypeIdPointer); + ZigType *child_type = parent_ptr_type->data.pointer.child_type; + + if (result_loc_is_discard(result_bit_cast->parent)) { + assert(allow_discard); + return parent_result_loc; + } + + if ((err = type_resolve(ira->codegen, child_type, ResolveStatusSizeKnown))) { + return ira->codegen->invalid_inst_gen; + } + + if ((err = type_resolve(ira->codegen, value_type, ResolveStatusSizeKnown))) { + return ira->codegen->invalid_inst_gen; + } + + if (child_type != ira->codegen->builtin_types.entry_anytype) { + if (type_size(ira->codegen, child_type) != type_size(ira->codegen, value_type)) { + // pointer cast won't work; we need a temporary location. + result_bit_cast->parent->written = parent_was_written; + result_loc->written = true; + result_loc->resolved_loc = ir_resolve_result(ira, suspend_source_instr, no_result_loc(), + value_type, bitcasted_value, force_runtime, true); + return result_loc->resolved_loc; + } + } + uint64_t parent_ptr_align = 0; + if (type_has_bits(ira->codegen, value_type)) parent_ptr_align = get_ptr_align(ira->codegen, parent_ptr_type); + ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, value_type, + parent_ptr_type->data.pointer.is_const, parent_ptr_type->data.pointer.is_volatile, PtrLenSingle, + parent_ptr_align, 0, 0, parent_ptr_type->data.pointer.allow_zero); + + result_loc->written = true; + result_loc->resolved_loc = ir_analyze_ptr_cast(ira, suspend_source_instr, parent_result_loc, + &parent_result_loc->base, ptr_type, &result_bit_cast->base.source_instruction->base, false, false); + return result_loc->resolved_loc; + } + } + zig_unreachable(); +} + +static IrInstGen *ir_resolve_result(IrAnalyze *ira, IrInst *suspend_source_instr, + ResultLoc *result_loc_pass1, ZigType *value_type, IrInstGen *value, bool force_runtime, + bool allow_discard) +{ + if (!allow_discard && result_loc_is_discard(result_loc_pass1)) { + result_loc_pass1 = no_result_loc(); + } + bool was_written = result_loc_pass1->written; + IrInstGen *result_loc = ir_resolve_result_raw(ira, suspend_source_instr, result_loc_pass1, value_type, + value, force_runtime, allow_discard); + if (result_loc == nullptr || result_loc->value->type->id == ZigTypeIdUnreachable || + type_is_invalid(result_loc->value->type)) + { + return result_loc; + } + + if ((force_runtime || (value != nullptr && !instr_is_comptime(value))) && + result_loc_pass1->written && result_loc->value->data.x_ptr.mut == ConstPtrMutInfer) + { + result_loc->value->special = ConstValSpecialRuntime; + } + + InferredStructField *isf = result_loc->value->type->data.pointer.inferred_struct_field; + if (isf != nullptr) { + TypeStructField *field; + IrInstGen *casted_ptr; + if (isf->already_resolved) { + field = find_struct_type_field(isf->inferred_struct_type, isf->field_name); + casted_ptr = result_loc; + } else { + isf->already_resolved = true; + // Now it's time to add the field to the struct type. + uint32_t old_field_count = isf->inferred_struct_type->data.structure.src_field_count; + uint32_t new_field_count = old_field_count + 1; + isf->inferred_struct_type->data.structure.src_field_count = new_field_count; + isf->inferred_struct_type->data.structure.fields = realloc_type_struct_fields( + isf->inferred_struct_type->data.structure.fields, old_field_count, new_field_count); + + field = isf->inferred_struct_type->data.structure.fields[old_field_count]; + field->name = isf->field_name; + field->type_entry = value_type; + field->type_val = create_const_type(ira->codegen, field->type_entry); + field->src_index = old_field_count; + field->decl_node = value ? value->base.source_node : suspend_source_instr->source_node; + if (value && instr_is_comptime(value)) { + ZigValue *val = ir_resolve_const(ira, value, UndefOk); + if (!val) + return ira->codegen->invalid_inst_gen; + field->is_comptime = true; + field->init_val = ira->codegen->pass1_arena->create(); + copy_const_val(ira->codegen, field->init_val, val); + return result_loc; + } + + ZigType *struct_ptr_type = get_pointer_to_type(ira->codegen, isf->inferred_struct_type, false); + if (instr_is_comptime(result_loc)) { + casted_ptr = ir_const(ira, suspend_source_instr, struct_ptr_type); + copy_const_val(ira->codegen, casted_ptr->value, result_loc->value); + casted_ptr->value->type = struct_ptr_type; + } else { + casted_ptr = result_loc; + } + if (instr_is_comptime(casted_ptr)) { + ZigValue *ptr_val = ir_resolve_const(ira, casted_ptr, UndefBad); + if (!ptr_val) + return ira->codegen->invalid_inst_gen; + if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { + ZigValue *struct_val = const_ptr_pointee(ira, ira->codegen, ptr_val, + suspend_source_instr->source_node); + struct_val->special = ConstValSpecialStatic; + struct_val->data.x_struct.fields = realloc_const_vals_ptrs(ira->codegen, + struct_val->data.x_struct.fields, old_field_count, new_field_count); + + ZigValue *field_val = struct_val->data.x_struct.fields[old_field_count]; + field_val->special = ConstValSpecialUndef; + field_val->type = field->type_entry; + field_val->parent.id = ConstParentIdStruct; + field_val->parent.data.p_struct.struct_val = struct_val; + field_val->parent.data.p_struct.field_index = old_field_count; + } + } + } + + result_loc = ir_analyze_struct_field_ptr(ira, suspend_source_instr, field, casted_ptr, + isf->inferred_struct_type, true); + result_loc_pass1->resolved_loc = result_loc; + } + + if (was_written) { + return result_loc; + } + + ir_assert(result_loc->value->type->id == ZigTypeIdPointer, suspend_source_instr); + ZigType *actual_elem_type = result_loc->value->type->data.pointer.child_type; + if (actual_elem_type->id == ZigTypeIdOptional && value_type->id != ZigTypeIdOptional && + value_type->id != ZigTypeIdNull && value_type->id != ZigTypeIdUndefined) + { + bool same_comptime_repr = types_have_same_zig_comptime_repr(ira->codegen, actual_elem_type, value_type); + if (!same_comptime_repr) { + result_loc_pass1->written = was_written; + return ir_analyze_unwrap_optional_payload(ira, suspend_source_instr, result_loc, false, true); + } + } else if (actual_elem_type->id == ZigTypeIdErrorUnion && value_type->id != ZigTypeIdErrorUnion && + value_type->id != ZigTypeIdUndefined) + { + if (value_type->id == ZigTypeIdErrorSet) { + return ir_analyze_unwrap_err_code(ira, suspend_source_instr, result_loc, true); + } else { + IrInstGen *unwrapped_err_ptr = ir_analyze_unwrap_error_payload(ira, suspend_source_instr, + result_loc, false, true); + ZigType *actual_payload_type = actual_elem_type->data.error_union.payload_type; + if (actual_payload_type->id == ZigTypeIdOptional && value_type->id != ZigTypeIdOptional && + value_type->id != ZigTypeIdNull && value_type->id != ZigTypeIdUndefined) + { + return ir_analyze_unwrap_optional_payload(ira, suspend_source_instr, unwrapped_err_ptr, false, true); + } else { + return unwrapped_err_ptr; + } + } + } + return result_loc; +} + +static IrInstGen *ir_analyze_instruction_resolve_result(IrAnalyze *ira, IrInstSrcResolveResult *instruction) { + ZigType *implicit_elem_type; + if (instruction->ty == nullptr) { + if (instruction->result_loc->id == ResultLocIdCast) { + implicit_elem_type = ir_resolve_type(ira, + instruction->result_loc->source_instruction->child); + if (type_is_invalid(implicit_elem_type)) + return ira->codegen->invalid_inst_gen; + } else if (instruction->result_loc->id == ResultLocIdReturn) { + implicit_elem_type = ira->explicit_return_type; + if (type_is_invalid(implicit_elem_type)) + return ira->codegen->invalid_inst_gen; + } else { + implicit_elem_type = ira->codegen->builtin_types.entry_anytype; + } + if (implicit_elem_type == ira->codegen->builtin_types.entry_anytype) { + Buf *bare_name = buf_alloc(); + Buf *name = get_anon_type_name(ira->codegen, nullptr, container_string(ContainerKindStruct), + instruction->base.base.scope, instruction->base.base.source_node, bare_name); + + StructSpecial struct_special = StructSpecialInferredStruct; + if (instruction->base.base.source_node->type == NodeTypeContainerInitExpr && + instruction->base.base.source_node->data.container_init_expr.kind == ContainerInitKindArray) + { + struct_special = StructSpecialInferredTuple; + } + + ZigType *inferred_struct_type = get_partial_container_type(ira->codegen, + instruction->base.base.scope, ContainerKindStruct, instruction->base.base.source_node, + buf_ptr(name), bare_name, ContainerLayoutAuto); + inferred_struct_type->data.structure.special = struct_special; + inferred_struct_type->data.structure.resolve_status = ResolveStatusBeingInferred; + implicit_elem_type = inferred_struct_type; + } + } else { + implicit_elem_type = ir_resolve_type(ira, instruction->ty->child); + if (type_is_invalid(implicit_elem_type)) + return ira->codegen->invalid_inst_gen; + } + IrInstGen *result_loc = ir_resolve_result(ira, &instruction->base.base, instruction->result_loc, + implicit_elem_type, nullptr, false, true); + if (result_loc != nullptr) + return result_loc; + + ZigFn *fn = ira->new_irb.exec->fn_entry; + if (fn != nullptr && fn->type_entry->data.fn.fn_type_id.cc == CallingConventionAsync && + instruction->result_loc->id == ResultLocIdReturn) + { + result_loc = ir_resolve_result(ira, &instruction->base.base, no_result_loc(), + implicit_elem_type, nullptr, false, true); + if (result_loc != nullptr && + (type_is_invalid(result_loc->value->type) || result_loc->value->type->id == ZigTypeIdUnreachable)) + { + return result_loc; + } + result_loc->value->special = ConstValSpecialRuntime; + return result_loc; + } + + IrInstGen *result = ir_const(ira, &instruction->base.base, implicit_elem_type); + result->value->special = ConstValSpecialUndef; + IrInstGen *ptr = ir_get_ref(ira, &instruction->base.base, result, false, false); + ptr->value->data.x_ptr.mut = ConstPtrMutComptimeVar; + return ptr; +} + +static void ir_reset_result(ResultLoc *result_loc) { + result_loc->written = false; + result_loc->resolved_loc = nullptr; + result_loc->gen_instruction = nullptr; + result_loc->implicit_elem_type = nullptr; + switch (result_loc->id) { + case ResultLocIdInvalid: + zig_unreachable(); + case ResultLocIdPeerParent: { + ResultLocPeerParent *peer_parent = reinterpret_cast(result_loc); + peer_parent->skipped = false; + peer_parent->done_resuming = false; + peer_parent->resolved_type = nullptr; + for (size_t i = 0; i < peer_parent->peers.length; i += 1) { + ir_reset_result(&peer_parent->peers.at(i)->base); + } + break; + } + case ResultLocIdVar: { + IrInstSrcAlloca *alloca_src = reinterpret_cast(result_loc->source_instruction); + alloca_src->base.child = nullptr; + break; + } + case ResultLocIdReturn: + reinterpret_cast(result_loc)->implicit_return_type_done = false; + break; + case ResultLocIdPeer: + case ResultLocIdNone: + case ResultLocIdInstruction: + case ResultLocIdBitCast: + case ResultLocIdCast: + break; + } +} + +static IrInstGen *ir_analyze_instruction_reset_result(IrAnalyze *ira, IrInstSrcResetResult *instruction) { + ir_reset_result(instruction->result_loc); + return ir_const_void(ira, &instruction->base.base); +} + +static IrInstGen *get_async_call_result_loc(IrAnalyze *ira, IrInst* source_instr, + ZigType *fn_ret_type, bool is_async_call_builtin, IrInstGen **args_ptr, size_t args_len, + IrInstGen *ret_ptr_uncasted) +{ + ir_assert(is_async_call_builtin, source_instr); + if (type_is_invalid(ret_ptr_uncasted->value->type)) + return ira->codegen->invalid_inst_gen; + if (ret_ptr_uncasted->value->type->id == ZigTypeIdVoid) { + // Result location will be inside the async frame. + return nullptr; + } + return ir_implicit_cast(ira, ret_ptr_uncasted, get_pointer_to_type(ira->codegen, fn_ret_type, false)); +} + +static IrInstGen *ir_analyze_async_call(IrAnalyze *ira, IrInst* source_instr, ZigFn *fn_entry, + ZigType *fn_type, IrInstGen *fn_ref, IrInstGen **casted_args, size_t arg_count, + IrInstGen *casted_new_stack, bool is_async_call_builtin, IrInstGen *ret_ptr_uncasted, + ResultLoc *call_result_loc) +{ + if (fn_entry == nullptr) { + if (fn_type->data.fn.fn_type_id.cc != CallingConventionAsync) { + ir_add_error(ira, &fn_ref->base, + buf_sprintf("expected async function, found '%s'", buf_ptr(&fn_type->name))); + return ira->codegen->invalid_inst_gen; + } + if (casted_new_stack == nullptr) { + ir_add_error(ira, &fn_ref->base, buf_sprintf("function is not comptime-known; @asyncCall required")); + return ira->codegen->invalid_inst_gen; + } + } + if (casted_new_stack != nullptr) { + ZigType *fn_ret_type = fn_type->data.fn.fn_type_id.return_type; + IrInstGen *ret_ptr = get_async_call_result_loc(ira, source_instr, fn_ret_type, is_async_call_builtin, + casted_args, arg_count, ret_ptr_uncasted); + if (ret_ptr != nullptr && type_is_invalid(ret_ptr->value->type)) + return ira->codegen->invalid_inst_gen; + + ZigType *anyframe_type = get_any_frame_type(ira->codegen, fn_ret_type); + + IrInstGenCall *call_gen = ir_build_call_gen(ira, source_instr, fn_entry, fn_ref, + arg_count, casted_args, CallModifierAsync, casted_new_stack, + is_async_call_builtin, ret_ptr, anyframe_type); + return &call_gen->base; + } else { + ZigType *frame_type = get_fn_frame_type(ira->codegen, fn_entry); + IrInstGen *result_loc = ir_resolve_result(ira, source_instr, call_result_loc, + frame_type, nullptr, true, false); + if (type_is_invalid(result_loc->value->type) || result_loc->value->type->id == ZigTypeIdUnreachable) { + return result_loc; + } + result_loc = ir_implicit_cast2(ira, source_instr, result_loc, + get_pointer_to_type(ira->codegen, frame_type, false)); + if (type_is_invalid(result_loc->value->type)) + return ira->codegen->invalid_inst_gen; + return &ir_build_call_gen(ira, source_instr, fn_entry, fn_ref, arg_count, + casted_args, CallModifierAsync, casted_new_stack, + is_async_call_builtin, result_loc, frame_type)->base; + } +} +static bool ir_analyze_fn_call_inline_arg(IrAnalyze *ira, AstNode *fn_proto_node, + IrInstGen *arg, Scope **exec_scope, size_t *next_proto_i) +{ + AstNode *param_decl_node = fn_proto_node->data.fn_proto.params.at(*next_proto_i); + assert(param_decl_node->type == NodeTypeParamDecl); + + IrInstGen *casted_arg; + if (param_decl_node->data.param_decl.anytype_token == nullptr) { + AstNode *param_type_node = param_decl_node->data.param_decl.type; + ZigType *param_type = ir_analyze_type_expr(ira, *exec_scope, param_type_node); + if (type_is_invalid(param_type)) + return false; + + casted_arg = ir_implicit_cast(ira, arg, param_type); + if (type_is_invalid(casted_arg->value->type)) + return false; + } else { + casted_arg = arg; + } + + ZigValue *arg_val = ir_resolve_const(ira, casted_arg, UndefOk); + if (!arg_val) + return false; + + Buf *param_name = param_decl_node->data.param_decl.name; + ZigVar *var = add_variable(ira->codegen, param_decl_node, + *exec_scope, param_name, true, arg_val, nullptr, arg_val->type); + *exec_scope = var->child_scope; + *next_proto_i += 1; + + return true; +} + +static bool ir_analyze_fn_call_generic_arg(IrAnalyze *ira, AstNode *fn_proto_node, + IrInstGen *arg, IrInst *arg_src, Scope **child_scope, size_t *next_proto_i, + GenericFnTypeId *generic_id, FnTypeId *fn_type_id, IrInstGen **casted_args, + ZigFn *impl_fn) +{ + AstNode *param_decl_node = fn_proto_node->data.fn_proto.params.at(*next_proto_i); + assert(param_decl_node->type == NodeTypeParamDecl); + bool is_var_args = param_decl_node->data.param_decl.is_var_args; + bool arg_part_of_generic_id = false; + IrInstGen *casted_arg; + if (is_var_args) { + arg_part_of_generic_id = true; + casted_arg = arg; + } else { + if (param_decl_node->data.param_decl.anytype_token == nullptr) { + AstNode *param_type_node = param_decl_node->data.param_decl.type; + ZigType *param_type = ir_analyze_type_expr(ira, *child_scope, param_type_node); + if (type_is_invalid(param_type)) + return false; + + casted_arg = ir_implicit_cast2(ira, arg_src, arg, param_type); + if (type_is_invalid(casted_arg->value->type)) + return false; + } else { + arg_part_of_generic_id = true; + casted_arg = arg; + } + } + + bool comptime_arg = param_decl_node->data.param_decl.is_comptime; + if (!comptime_arg) { + switch (type_requires_comptime(ira->codegen, casted_arg->value->type)) { + case ReqCompTimeInvalid: + return false; + case ReqCompTimeYes: + comptime_arg = true; + break; + case ReqCompTimeNo: + break; + } + } + + ZigValue *arg_val; + + if (comptime_arg) { + arg_part_of_generic_id = true; + arg_val = ir_resolve_const(ira, casted_arg, UndefBad); + if (!arg_val) + return false; + } else { + arg_val = create_const_runtime(ira->codegen, casted_arg->value->type); + } + if (arg_part_of_generic_id) { + copy_const_val(ira->codegen, &generic_id->params[generic_id->param_count], arg_val); + generic_id->param_count += 1; + } + + Buf *param_name = param_decl_node->data.param_decl.name; + if (!param_name) return false; + if (!is_var_args) { + ZigVar *var = add_variable(ira->codegen, param_decl_node, + *child_scope, param_name, true, arg_val, nullptr, arg_val->type); + *child_scope = var->child_scope; + var->shadowable = !comptime_arg; + + *next_proto_i += 1; + } else if (casted_arg->value->type->id == ZigTypeIdComptimeInt || + casted_arg->value->type->id == ZigTypeIdComptimeFloat) + { + ir_add_error(ira, &casted_arg->base, + buf_sprintf("compiler bug: integer and float literals in var args function must be casted. https://github.com/ziglang/zig/issues/557")); + return false; + } + + if (!comptime_arg) { + casted_args[fn_type_id->param_count] = casted_arg; + FnTypeParamInfo *param_info = &fn_type_id->param_info[fn_type_id->param_count]; + param_info->type = casted_arg->value->type; + param_info->is_noalias = param_decl_node->data.param_decl.is_noalias; + impl_fn->param_source_nodes[fn_type_id->param_count] = param_decl_node; + fn_type_id->param_count += 1; + } + + return true; +} + +static IrInstGen *ir_get_var_ptr(IrAnalyze *ira, IrInst *source_instr, ZigVar *var) { + while (var->next_var != nullptr) { + var = var->next_var; + } + + if (var->var_type == nullptr || type_is_invalid(var->var_type)) + return ira->codegen->invalid_inst_gen; + + bool is_volatile = false; + ZigType *var_ptr_type = get_pointer_to_type_extra(ira->codegen, var->var_type, + var->src_is_const, is_volatile, PtrLenSingle, var->align_bytes, 0, 0, false); + + if (var->ptr_instruction != nullptr) { + return ir_implicit_cast(ira, var->ptr_instruction, var_ptr_type); + } + + bool comptime_var_mem = ir_get_var_is_comptime(var); + bool linkage_makes_it_runtime = var->decl_node->data.variable_declaration.is_extern; + + IrInstGen *result = ir_build_var_ptr_gen(ira, source_instr, var); + result->value->type = var_ptr_type; + + if (!linkage_makes_it_runtime && !var->is_thread_local && value_is_comptime(var->const_value)) { + ZigValue *val = var->const_value; + switch (val->special) { + case ConstValSpecialRuntime: + break; + case ConstValSpecialStatic: // fallthrough + case ConstValSpecialLazy: // fallthrough + case ConstValSpecialUndef: { + ConstPtrMut ptr_mut; + if (comptime_var_mem) { + ptr_mut = ConstPtrMutComptimeVar; + } else if (var->gen_is_const) { + ptr_mut = ConstPtrMutComptimeConst; + } else { + assert(!comptime_var_mem); + ptr_mut = ConstPtrMutRuntimeVar; + } + result->value->special = ConstValSpecialStatic; + result->value->data.x_ptr.mut = ptr_mut; + result->value->data.x_ptr.special = ConstPtrSpecialRef; + result->value->data.x_ptr.data.ref.pointee = val; + return result; + } + } + } + + bool in_fn_scope = (scope_fn_entry(var->parent_scope) != nullptr); + result->value->data.rh_ptr = in_fn_scope ? RuntimeHintPtrStack : RuntimeHintPtrNonStack; + + return result; +} + +// This function is called when a comptime value becomes accessible at runtime. +static void mark_comptime_value_escape(IrAnalyze *ira, IrInst* source_instr, ZigValue *val) { + ir_assert(value_is_comptime(val), source_instr); + if (val->special == ConstValSpecialUndef) + return; + + if (val->type->id == ZigTypeIdFn && val->type->data.fn.fn_type_id.cc == CallingConventionUnspecified) { + ir_assert(val->data.x_ptr.special == ConstPtrSpecialFunction, source_instr); + if (val->data.x_ptr.data.fn.fn_entry->non_async_node == nullptr) { + val->data.x_ptr.data.fn.fn_entry->non_async_node = source_instr->source_node; + } + } +} + +static IrInstGen *ir_analyze_store_ptr(IrAnalyze *ira, IrInst* source_instr, + IrInstGen *ptr, IrInstGen *uncasted_value, bool allow_write_through_const) +{ + assert(ptr->value->type->id == ZigTypeIdPointer); + + if (ptr->value->data.x_ptr.special == ConstPtrSpecialDiscard) { + if (uncasted_value->value->type->id == ZigTypeIdErrorUnion || + uncasted_value->value->type->id == ZigTypeIdErrorSet) + { + ir_add_error(ira, source_instr, buf_sprintf("error is discarded")); + return ira->codegen->invalid_inst_gen; + } + return ir_const_void(ira, source_instr); + } + + if (ptr->value->type->data.pointer.is_const && !allow_write_through_const) { + ir_add_error(ira, source_instr, buf_sprintf("cannot assign to constant")); + return ira->codegen->invalid_inst_gen; + } + + ZigType *child_type = ptr->value->type->data.pointer.child_type; + IrInstGen *value = ir_implicit_cast(ira, uncasted_value, child_type); + if (type_is_invalid(value->value->type)) + return ira->codegen->invalid_inst_gen; + + switch (type_has_one_possible_value(ira->codegen, child_type)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_inst_gen; + case OnePossibleValueYes: + return ir_const_void(ira, source_instr); + case OnePossibleValueNo: + break; + } + + if (instr_is_comptime(ptr) && ptr->value->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { + if (!allow_write_through_const && ptr->value->data.x_ptr.mut == ConstPtrMutComptimeConst) { + ir_add_error(ira, source_instr, buf_sprintf("cannot assign to constant")); + return ira->codegen->invalid_inst_gen; + } + if ((allow_write_through_const && ptr->value->data.x_ptr.mut == ConstPtrMutComptimeConst) || + ptr->value->data.x_ptr.mut == ConstPtrMutComptimeVar || + ptr->value->data.x_ptr.mut == ConstPtrMutInfer) + { + if (instr_is_comptime(value)) { + ZigValue *dest_val = const_ptr_pointee(ira, ira->codegen, ptr->value, source_instr->source_node); + if (dest_val == nullptr) + return ira->codegen->invalid_inst_gen; + if (dest_val->special != ConstValSpecialRuntime) { + copy_const_val(ira->codegen, dest_val, value->value); + + if (ptr->value->data.x_ptr.mut == ConstPtrMutComptimeVar && + !ira->new_irb.current_basic_block->must_be_comptime_source_instr) + { + ira->new_irb.current_basic_block->must_be_comptime_source_instr = source_instr; + } + return ir_const_void(ira, source_instr); + } + } + if (ptr->value->data.x_ptr.mut == ConstPtrMutInfer) { + ptr->value->special = ConstValSpecialRuntime; + } else { + ir_add_error(ira, source_instr, + buf_sprintf("cannot store runtime value in compile time variable")); + ZigValue *dest_val = const_ptr_pointee_unchecked(ira->codegen, ptr->value); + dest_val->type = ira->codegen->builtin_types.entry_invalid; + + return ira->codegen->invalid_inst_gen; + } + } + } + + if (ptr->value->type->data.pointer.inferred_struct_field != nullptr && + child_type == ira->codegen->builtin_types.entry_anytype) + { + child_type = ptr->value->type->data.pointer.inferred_struct_field->inferred_struct_type; + } + + switch (type_requires_comptime(ira->codegen, child_type)) { + case ReqCompTimeInvalid: + return ira->codegen->invalid_inst_gen; + case ReqCompTimeYes: + switch (type_has_one_possible_value(ira->codegen, ptr->value->type)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_inst_gen; + case OnePossibleValueNo: + ir_add_error(ira, source_instr, + buf_sprintf("cannot store runtime value in type '%s'", buf_ptr(&child_type->name))); + return ira->codegen->invalid_inst_gen; + case OnePossibleValueYes: + return ir_const_void(ira, source_instr); + } + zig_unreachable(); + case ReqCompTimeNo: + break; + } + + if (instr_is_comptime(value)) { + mark_comptime_value_escape(ira, source_instr, value->value); + } + + // If this is a store to a pointer with a runtime-known vector index, + // we have to figure out the IrInstGen which represents the index and + // emit a IrInstGenVectorStoreElem, or emit a compile error + // explaining why it is impossible for this store to work. Which is that + // the pointer address is of the vector; without the element index being known + // we cannot properly perform the insertion. + if (ptr->value->type->data.pointer.vector_index == VECTOR_INDEX_RUNTIME) { + if (ptr->id == IrInstGenIdElemPtr) { + IrInstGenElemPtr *elem_ptr = (IrInstGenElemPtr *)ptr; + return ir_build_vector_store_elem(ira, source_instr, elem_ptr->array_ptr, + elem_ptr->elem_index, value); + } + ir_add_error(ira, &ptr->base, + buf_sprintf("unable to determine vector element index of type '%s'", + buf_ptr(&ptr->value->type->name))); + return ira->codegen->invalid_inst_gen; + } + + return ir_build_store_ptr_gen(ira, source_instr, ptr, value); +} + +static IrInstGen *analyze_casted_new_stack(IrAnalyze *ira, IrInst* source_instr, + IrInstGen *new_stack, IrInst *new_stack_src, bool is_async_call_builtin, ZigFn *fn_entry) +{ + if (new_stack == nullptr) + return nullptr; + + if (!is_async_call_builtin && + arch_stack_pointer_register_name(ira->codegen->zig_target->arch) == nullptr) + { + ir_add_error(ira, source_instr, + buf_sprintf("target arch '%s' does not support calling with a new stack", + target_arch_name(ira->codegen->zig_target->arch))); + } + + if (is_async_call_builtin && + fn_entry != nullptr && new_stack->value->type->id == ZigTypeIdPointer && + new_stack->value->type->data.pointer.child_type->id == ZigTypeIdFnFrame) + { + ZigType *needed_frame_type = get_pointer_to_type(ira->codegen, + get_fn_frame_type(ira->codegen, fn_entry), false); + return ir_implicit_cast(ira, new_stack, needed_frame_type); + } else { + ZigType *u8_ptr = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, + false, false, PtrLenUnknown, target_fn_align(ira->codegen->zig_target), 0, 0, false); + ZigType *u8_slice = get_slice_type(ira->codegen, u8_ptr); + ira->codegen->need_frame_size_prefix_data = true; + return ir_implicit_cast2(ira, new_stack_src, new_stack, u8_slice); + } +} + +static IrInstGen *ir_analyze_fn_call(IrAnalyze *ira, IrInst* source_instr, + ZigFn *fn_entry, ZigType *fn_type, IrInstGen *fn_ref, + IrInstGen *first_arg_ptr, IrInst *first_arg_ptr_src, CallModifier modifier, + IrInstGen *new_stack, IrInst *new_stack_src, bool is_async_call_builtin, + IrInstGen **args_ptr, size_t args_len, IrInstGen *ret_ptr, ResultLoc *call_result_loc) +{ + Error err; + FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; + size_t first_arg_1_or_0 = first_arg_ptr ? 1 : 0; + + // for extern functions, the var args argument is not counted. + // for zig functions, it is. + size_t var_args_1_or_0; + if (fn_type_id->cc == CallingConventionC) { + var_args_1_or_0 = 0; + } else { + var_args_1_or_0 = fn_type_id->is_var_args ? 1 : 0; + } + size_t src_param_count = fn_type_id->param_count - var_args_1_or_0; + size_t call_param_count = args_len + first_arg_1_or_0; + AstNode *source_node = source_instr->source_node; + + AstNode *fn_proto_node = fn_entry ? fn_entry->proto_node : nullptr;; + + if (fn_type_id->cc == CallingConventionNaked) { + ErrorMsg *msg = ir_add_error(ira, &fn_ref->base, buf_sprintf("unable to call function with naked calling convention")); + if (fn_proto_node) { + add_error_note(ira->codegen, msg, fn_proto_node, buf_sprintf("declared here")); + } + return ira->codegen->invalid_inst_gen; + } + + if (fn_type_id->is_var_args) { + if (call_param_count < src_param_count) { + ErrorMsg *msg = ir_add_error_node(ira, source_node, + buf_sprintf("expected at least %" ZIG_PRI_usize " argument(s), found %" ZIG_PRI_usize "", + src_param_count, call_param_count)); + if (fn_proto_node) { + add_error_note(ira->codegen, msg, fn_proto_node, + buf_sprintf("declared here")); + } + return ira->codegen->invalid_inst_gen; + } + } else if (src_param_count != call_param_count) { + ErrorMsg *msg = ir_add_error_node(ira, source_node, + buf_sprintf("expected %" ZIG_PRI_usize " argument(s), found %" ZIG_PRI_usize "", + src_param_count, call_param_count)); + if (fn_proto_node) { + add_error_note(ira->codegen, msg, fn_proto_node, + buf_sprintf("declared here")); + } + return ira->codegen->invalid_inst_gen; + } + + if (modifier == CallModifierCompileTime) { + // If we are evaluating an extern function in a TypeOf call, we can return an undefined value + // of its return type. + if (fn_entry != nullptr && get_scope_typeof(source_instr->scope) != nullptr && + fn_proto_node->data.fn_proto.is_extern) { + + assert(fn_entry->body_node == nullptr); + AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type; + ZigType *return_type = ir_analyze_type_expr(ira, source_instr->scope, return_type_node); + if (type_is_invalid(return_type)) + return ira->codegen->invalid_inst_gen; + + return ir_const_undef(ira, source_instr, return_type); + } + + // No special handling is needed for compile time evaluation of generic functions. + if (!fn_entry || fn_entry->body_node == nullptr) { + ir_add_error(ira, &fn_ref->base, buf_sprintf("unable to evaluate constant expression")); + return ira->codegen->invalid_inst_gen; + } + + if (!ir_emit_backward_branch(ira, source_instr)) + return ira->codegen->invalid_inst_gen; + + // Fork a scope of the function with known values for the parameters. + Scope *exec_scope = &fn_entry->fndef_scope->base; + + size_t next_proto_i = 0; + if (first_arg_ptr) { + assert(first_arg_ptr->value->type->id == ZigTypeIdPointer); + + bool first_arg_known_bare = false; + if (fn_type_id->next_param_index >= 1) { + ZigType *param_type = fn_type_id->param_info[next_proto_i].type; + if (type_is_invalid(param_type)) + return ira->codegen->invalid_inst_gen; + first_arg_known_bare = param_type->id != ZigTypeIdPointer; + } + + IrInstGen *first_arg; + if (!first_arg_known_bare) { + first_arg = first_arg_ptr; + } else { + first_arg = ir_get_deref(ira, &first_arg_ptr->base, first_arg_ptr, nullptr); + if (type_is_invalid(first_arg->value->type)) + return ira->codegen->invalid_inst_gen; + } + + if (!ir_analyze_fn_call_inline_arg(ira, fn_proto_node, first_arg, &exec_scope, &next_proto_i)) + return ira->codegen->invalid_inst_gen; + } + + for (size_t call_i = 0; call_i < args_len; call_i += 1) { + IrInstGen *old_arg = args_ptr[call_i]; + + if (!ir_analyze_fn_call_inline_arg(ira, fn_proto_node, old_arg, &exec_scope, &next_proto_i)) + return ira->codegen->invalid_inst_gen; + } + + AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type; + if (return_type_node == nullptr) { + ir_add_error(ira, &fn_ref->base, + buf_sprintf("TODO implement inferred return types https://github.com/ziglang/zig/issues/447")); + return ira->codegen->invalid_inst_gen; + } + ZigType *specified_return_type = ir_analyze_type_expr(ira, exec_scope, return_type_node); + if (type_is_invalid(specified_return_type)) + return ira->codegen->invalid_inst_gen; + ZigType *return_type; + ZigType *inferred_err_set_type = nullptr; + if (fn_proto_node->data.fn_proto.auto_err_set) { + inferred_err_set_type = get_auto_err_set_type(ira->codegen, fn_entry); + if ((err = type_resolve(ira->codegen, specified_return_type, ResolveStatusSizeKnown))) + return ira->codegen->invalid_inst_gen; + return_type = get_error_union_type(ira->codegen, inferred_err_set_type, specified_return_type); + } else { + return_type = specified_return_type; + } + + bool cacheable = fn_eval_cacheable(exec_scope, return_type); + ZigValue *result = nullptr; + if (cacheable) { + auto entry = ira->codegen->memoized_fn_eval_table.maybe_get(exec_scope); + if (entry) + result = entry->value; + } + + if (result == nullptr) { + // Analyze the fn body block like any other constant expression. + AstNode *body_node = fn_entry->body_node; + ZigValue *result_ptr; + create_result_ptr(ira->codegen, return_type, &result, &result_ptr); + + if ((err = ir_eval_const_value(ira->codegen, exec_scope, body_node, result_ptr, + ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, + fn_entry, nullptr, source_instr->source_node, nullptr, ira->new_irb.exec, return_type_node, + UndefOk))) + { + return ira->codegen->invalid_inst_gen; + } + + if (inferred_err_set_type != nullptr) { + inferred_err_set_type->data.error_set.incomplete = false; + if (result->type->id == ZigTypeIdErrorUnion) { + ErrorTableEntry *err = result->data.x_err_union.error_set->data.x_err_set; + if (err != nullptr) { + inferred_err_set_type->data.error_set.err_count = 1; + inferred_err_set_type->data.error_set.errors = heap::c_allocator.create(); + inferred_err_set_type->data.error_set.errors[0] = err; + } + ZigType *fn_inferred_err_set_type = result->type->data.error_union.err_set_type; + inferred_err_set_type->data.error_set.err_count = fn_inferred_err_set_type->data.error_set.err_count; + inferred_err_set_type->data.error_set.errors = fn_inferred_err_set_type->data.error_set.errors; + } else if (result->type->id == ZigTypeIdErrorSet) { + inferred_err_set_type->data.error_set.err_count = result->type->data.error_set.err_count; + inferred_err_set_type->data.error_set.errors = result->type->data.error_set.errors; + } + } + + if (cacheable) { + ira->codegen->memoized_fn_eval_table.put(exec_scope, result); + } + + if (type_is_invalid(result->type)) { + return ira->codegen->invalid_inst_gen; + } + } + + IrInstGen *new_instruction = ir_const_move(ira, source_instr, result); + return ir_finish_anal(ira, new_instruction); + } + + if (fn_type->data.fn.is_generic) { + if (!fn_entry) { + ir_add_error(ira, &fn_ref->base, + buf_sprintf("calling a generic function requires compile-time known function value")); + return ira->codegen->invalid_inst_gen; + } + + size_t new_fn_arg_count = first_arg_1_or_0 + args_len; + + IrInstGen **casted_args = heap::c_allocator.allocate(new_fn_arg_count); + + // Fork a scope of the function with known values for the parameters. + Scope *parent_scope = fn_entry->fndef_scope->base.parent; + ZigFn *impl_fn = create_fn(ira->codegen, fn_proto_node); + impl_fn->param_source_nodes = heap::c_allocator.allocate(new_fn_arg_count); + buf_init_from_buf(&impl_fn->symbol_name, &fn_entry->symbol_name); + impl_fn->fndef_scope = create_fndef_scope(ira->codegen, impl_fn->body_node, parent_scope, impl_fn); + impl_fn->child_scope = &impl_fn->fndef_scope->base; + FnTypeId inst_fn_type_id = {0}; + init_fn_type_id(&inst_fn_type_id, fn_proto_node, fn_type_id->cc, new_fn_arg_count); + inst_fn_type_id.param_count = 0; + inst_fn_type_id.is_var_args = false; + + // TODO maybe GenericFnTypeId can be replaced with using the child_scope directly + // as the key in generic_table + GenericFnTypeId *generic_id = heap::c_allocator.create(); + generic_id->fn_entry = fn_entry; + generic_id->param_count = 0; + generic_id->params = ira->codegen->pass1_arena->allocate(new_fn_arg_count); + size_t next_proto_i = 0; + + if (first_arg_ptr) { + assert(first_arg_ptr->value->type->id == ZigTypeIdPointer); + + bool first_arg_known_bare = false; + if (fn_type_id->next_param_index >= 1) { + ZigType *param_type = fn_type_id->param_info[next_proto_i].type; + if (type_is_invalid(param_type)) + return ira->codegen->invalid_inst_gen; + first_arg_known_bare = param_type->id != ZigTypeIdPointer; + } + + IrInstGen *first_arg; + if (!first_arg_known_bare) { + first_arg = first_arg_ptr; + } else { + first_arg = ir_get_deref(ira, &first_arg_ptr->base, first_arg_ptr, nullptr); + if (type_is_invalid(first_arg->value->type)) + return ira->codegen->invalid_inst_gen; + } + + if (!ir_analyze_fn_call_generic_arg(ira, fn_proto_node, first_arg, first_arg_ptr_src, + &impl_fn->child_scope, &next_proto_i, generic_id, &inst_fn_type_id, casted_args, impl_fn)) + { + return ira->codegen->invalid_inst_gen; + } + } + + ZigFn *parent_fn_entry = ira->new_irb.exec->fn_entry; + assert(parent_fn_entry); + for (size_t call_i = 0; call_i < args_len; call_i += 1) { + IrInstGen *arg = args_ptr[call_i]; + + AstNode *param_decl_node = fn_proto_node->data.fn_proto.params.at(next_proto_i); + assert(param_decl_node->type == NodeTypeParamDecl); + + if (!ir_analyze_fn_call_generic_arg(ira, fn_proto_node, arg, &arg->base, &impl_fn->child_scope, + &next_proto_i, generic_id, &inst_fn_type_id, casted_args, impl_fn)) + { + return ira->codegen->invalid_inst_gen; + } + } + + if (fn_proto_node->data.fn_proto.align_expr != nullptr) { + ZigValue *align_result; + ZigValue *result_ptr; + create_result_ptr(ira->codegen, get_align_amt_type(ira->codegen), &align_result, &result_ptr); + if ((err = ir_eval_const_value(ira->codegen, impl_fn->child_scope, + fn_proto_node->data.fn_proto.align_expr, result_ptr, + ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, + nullptr, nullptr, fn_proto_node->data.fn_proto.align_expr, nullptr, ira->new_irb.exec, + nullptr, UndefBad))) + { + return ira->codegen->invalid_inst_gen; + } + IrInstGenConst *const_instruction = ir_create_inst_noval(&ira->new_irb, + impl_fn->child_scope, fn_proto_node->data.fn_proto.align_expr); + const_instruction->base.value = align_result; + + uint32_t align_bytes = 0; + ir_resolve_align(ira, &const_instruction->base, nullptr, &align_bytes); + impl_fn->align_bytes = align_bytes; + inst_fn_type_id.alignment = align_bytes; + } + + if (fn_proto_node->data.fn_proto.return_anytype_token == nullptr) { + AstNode *return_type_node = fn_proto_node->data.fn_proto.return_type; + ZigType *specified_return_type = ir_analyze_type_expr(ira, impl_fn->child_scope, return_type_node); + if (type_is_invalid(specified_return_type)) + return ira->codegen->invalid_inst_gen; + + if(!is_valid_return_type(specified_return_type)){ + ErrorMsg *msg = ir_add_error(ira, source_instr, + buf_sprintf("call to generic function with %s return type '%s' not allowed", type_id_name(specified_return_type->id), buf_ptr(&specified_return_type->name))); + add_error_note(ira->codegen, msg, fn_proto_node, buf_sprintf("function declared here")); + + Tld *tld = find_decl(ira->codegen, &fn_entry->fndef_scope->base, &specified_return_type->name); + if (tld != nullptr) { + add_error_note(ira->codegen, msg, tld->source_node, buf_sprintf("type declared here")); + } + return ira->codegen->invalid_inst_gen; + } + + if (fn_proto_node->data.fn_proto.auto_err_set) { + ZigType *inferred_err_set_type = get_auto_err_set_type(ira->codegen, impl_fn); + if ((err = type_resolve(ira->codegen, specified_return_type, ResolveStatusSizeKnown))) + return ira->codegen->invalid_inst_gen; + inst_fn_type_id.return_type = get_error_union_type(ira->codegen, inferred_err_set_type, specified_return_type); + } else { + inst_fn_type_id.return_type = specified_return_type; + } + + switch (type_requires_comptime(ira->codegen, specified_return_type)) { + case ReqCompTimeYes: + // Throw out our work and call the function as if it were comptime. + return ir_analyze_fn_call(ira, source_instr, fn_entry, fn_type, fn_ref, first_arg_ptr, + first_arg_ptr_src, CallModifierCompileTime, new_stack, new_stack_src, is_async_call_builtin, + args_ptr, args_len, ret_ptr, call_result_loc); + case ReqCompTimeInvalid: + return ira->codegen->invalid_inst_gen; + case ReqCompTimeNo: + break; + } + } + + auto existing_entry = ira->codegen->generic_table.put_unique(generic_id, impl_fn); + if (existing_entry) { + // throw away all our work and use the existing function + impl_fn = existing_entry->value; + } else { + // finish instantiating the function + impl_fn->type_entry = get_fn_type(ira->codegen, &inst_fn_type_id); + if (type_is_invalid(impl_fn->type_entry)) + return ira->codegen->invalid_inst_gen; + + impl_fn->ir_executable->source_node = source_instr->source_node; + impl_fn->ir_executable->parent_exec = ira->new_irb.exec; + impl_fn->analyzed_executable.source_node = source_instr->source_node; + impl_fn->analyzed_executable.parent_exec = ira->new_irb.exec; + impl_fn->analyzed_executable.backward_branch_quota = ira->new_irb.exec->backward_branch_quota; + impl_fn->analyzed_executable.is_generic_instantiation = true; + + ira->codegen->fn_defs.append(impl_fn); + } + + FnTypeId *impl_fn_type_id = &impl_fn->type_entry->data.fn.fn_type_id; + + if (fn_type_can_fail(impl_fn_type_id)) { + parent_fn_entry->calls_or_awaits_errorable_fn = true; + } + + IrInstGen *casted_new_stack = analyze_casted_new_stack(ira, source_instr, new_stack, + new_stack_src, is_async_call_builtin, impl_fn); + if (casted_new_stack != nullptr && type_is_invalid(casted_new_stack->value->type)) + return ira->codegen->invalid_inst_gen; + + size_t impl_param_count = impl_fn_type_id->param_count; + if (modifier == CallModifierAsync) { + IrInstGen *result = ir_analyze_async_call(ira, source_instr, impl_fn, impl_fn->type_entry, + nullptr, casted_args, impl_param_count, casted_new_stack, is_async_call_builtin, ret_ptr, + call_result_loc); + return ir_finish_anal(ira, result); + } + + IrInstGen *result_loc; + if (handle_is_ptr(ira->codegen, impl_fn_type_id->return_type)) { + result_loc = ir_resolve_result(ira, source_instr, call_result_loc, + impl_fn_type_id->return_type, nullptr, true, false); + if (result_loc != nullptr) { + if (type_is_invalid(result_loc->value->type) || result_loc->value->type->id == ZigTypeIdUnreachable) { + return result_loc; + } + if (result_loc->value->type->data.pointer.is_const) { + ir_add_error(ira, source_instr, buf_sprintf("cannot assign to constant")); + return ira->codegen->invalid_inst_gen; + } + + IrInstGen *dummy_value = ir_const(ira, source_instr, impl_fn_type_id->return_type); + dummy_value->value->special = ConstValSpecialRuntime; + IrInstGen *dummy_result = ir_implicit_cast2(ira, source_instr, + dummy_value, result_loc->value->type->data.pointer.child_type); + if (type_is_invalid(dummy_result->value->type)) + return ira->codegen->invalid_inst_gen; + ZigType *res_child_type = result_loc->value->type->data.pointer.child_type; + if (res_child_type == ira->codegen->builtin_types.entry_anytype) { + res_child_type = impl_fn_type_id->return_type; + } + if (!handle_is_ptr(ira->codegen, res_child_type)) { + ir_reset_result(call_result_loc); + result_loc = nullptr; + } + } + } else if (is_async_call_builtin) { + result_loc = get_async_call_result_loc(ira, source_instr, impl_fn_type_id->return_type, + is_async_call_builtin, args_ptr, args_len, ret_ptr); + if (result_loc != nullptr && type_is_invalid(result_loc->value->type)) + return ira->codegen->invalid_inst_gen; + } else { + result_loc = nullptr; + } + + if (impl_fn_type_id->cc == CallingConventionAsync && + parent_fn_entry->inferred_async_node == nullptr && + modifier != CallModifierNoSuspend) + { + parent_fn_entry->inferred_async_node = fn_ref->base.source_node; + parent_fn_entry->inferred_async_fn = impl_fn; + } + + IrInstGenCall *new_call_instruction = ir_build_call_gen(ira, source_instr, + impl_fn, nullptr, impl_param_count, casted_args, modifier, casted_new_stack, + is_async_call_builtin, result_loc, impl_fn_type_id->return_type); + + if (get_scope_typeof(source_instr->scope) == nullptr) { + parent_fn_entry->call_list.append(new_call_instruction); + } + + return ir_finish_anal(ira, &new_call_instruction->base); + } + + ZigFn *parent_fn_entry = ira->new_irb.exec->fn_entry; + assert(fn_type_id->return_type != nullptr); + assert(parent_fn_entry != nullptr); + if (fn_type_can_fail(fn_type_id)) { + parent_fn_entry->calls_or_awaits_errorable_fn = true; + } + + + IrInstGen **casted_args = heap::c_allocator.allocate(call_param_count); + size_t next_arg_index = 0; + if (first_arg_ptr) { + assert(first_arg_ptr->value->type->id == ZigTypeIdPointer); + + ZigType *param_type = fn_type_id->param_info[next_arg_index].type; + if (type_is_invalid(param_type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *first_arg; + if (param_type->id == ZigTypeIdPointer) { + first_arg = first_arg_ptr; + } else { + first_arg = ir_get_deref(ira, &first_arg_ptr->base, first_arg_ptr, nullptr); + if (type_is_invalid(first_arg->value->type)) + return ira->codegen->invalid_inst_gen; + } + + IrInstGen *casted_arg = ir_implicit_cast2(ira, first_arg_ptr_src, first_arg, param_type); + if (type_is_invalid(casted_arg->value->type)) + return ira->codegen->invalid_inst_gen; + + casted_args[next_arg_index] = casted_arg; + next_arg_index += 1; + } + for (size_t call_i = 0; call_i < args_len; call_i += 1) { + IrInstGen *old_arg = args_ptr[call_i]; + if (type_is_invalid(old_arg->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *casted_arg; + if (next_arg_index < src_param_count) { + ZigType *param_type = fn_type_id->param_info[next_arg_index].type; + if (type_is_invalid(param_type)) + return ira->codegen->invalid_inst_gen; + casted_arg = ir_implicit_cast(ira, old_arg, param_type); + if (type_is_invalid(casted_arg->value->type)) + return ira->codegen->invalid_inst_gen; + } else { + casted_arg = old_arg; + } + + casted_args[next_arg_index] = casted_arg; + next_arg_index += 1; + } + + assert(next_arg_index == call_param_count); + + ZigType *return_type = fn_type_id->return_type; + if (type_is_invalid(return_type)) + return ira->codegen->invalid_inst_gen; + + if (fn_entry != nullptr && fn_entry->fn_inline == FnInlineAlways && modifier == CallModifierNeverInline) { + ir_add_error(ira, source_instr, + buf_sprintf("no-inline call of inline function")); + return ira->codegen->invalid_inst_gen; + } + + IrInstGen *casted_new_stack = analyze_casted_new_stack(ira, source_instr, new_stack, new_stack_src, + is_async_call_builtin, fn_entry); + if (casted_new_stack != nullptr && type_is_invalid(casted_new_stack->value->type)) + return ira->codegen->invalid_inst_gen; + + if (modifier == CallModifierAsync) { + IrInstGen *result = ir_analyze_async_call(ira, source_instr, fn_entry, fn_type, fn_ref, + casted_args, call_param_count, casted_new_stack, is_async_call_builtin, ret_ptr, call_result_loc); + return ir_finish_anal(ira, result); + } + + if (fn_type_id->cc == CallingConventionAsync && + parent_fn_entry->inferred_async_node == nullptr && + modifier != CallModifierNoSuspend) + { + parent_fn_entry->inferred_async_node = fn_ref->base.source_node; + parent_fn_entry->inferred_async_fn = fn_entry; + } + + IrInstGen *result_loc; + if (handle_is_ptr(ira->codegen, return_type)) { + result_loc = ir_resolve_result(ira, source_instr, call_result_loc, + return_type, nullptr, true, false); + if (result_loc != nullptr) { + if (type_is_invalid(result_loc->value->type) || result_loc->value->type->id == ZigTypeIdUnreachable) { + return result_loc; + } + if (result_loc->value->type->data.pointer.is_const) { + ir_add_error(ira, source_instr, buf_sprintf("cannot assign to constant")); + return ira->codegen->invalid_inst_gen; + } + + ZigType *expected_return_type = result_loc->value->type->data.pointer.child_type; + + IrInstGen *dummy_value = ir_const(ira, source_instr, return_type); + dummy_value->value->special = ConstValSpecialRuntime; + IrInstGen *dummy_result = ir_implicit_cast2(ira, source_instr, + dummy_value, expected_return_type); + if (type_is_invalid(dummy_result->value->type)) { + if ((return_type->id == ZigTypeIdErrorUnion || return_type->id == ZigTypeIdErrorSet) && + expected_return_type->id != ZigTypeIdErrorUnion && expected_return_type->id != ZigTypeIdErrorSet) + { + if (call_result_loc->id == ResultLocIdReturn) { + add_error_note(ira->codegen, ira->new_irb.exec->first_err_trace_msg, + ira->explicit_return_type_source_node, buf_sprintf("function cannot return an error")); + } else { + add_error_note(ira->codegen, ira->new_irb.exec->first_err_trace_msg, result_loc->base.source_node, + buf_sprintf("cannot store an error in type '%s'", buf_ptr(&expected_return_type->name))); + } + } + return ira->codegen->invalid_inst_gen; + } + if (expected_return_type == ira->codegen->builtin_types.entry_anytype) { + expected_return_type = return_type; + } + if (!handle_is_ptr(ira->codegen, expected_return_type)) { + ir_reset_result(call_result_loc); + result_loc = nullptr; + } + } + } else if (is_async_call_builtin) { + result_loc = get_async_call_result_loc(ira, source_instr, return_type, is_async_call_builtin, + args_ptr, args_len, ret_ptr); + if (result_loc != nullptr && type_is_invalid(result_loc->value->type)) + return ira->codegen->invalid_inst_gen; + } else { + result_loc = nullptr; + } + + IrInstGenCall *new_call_instruction = ir_build_call_gen(ira, source_instr, fn_entry, fn_ref, + call_param_count, casted_args, modifier, casted_new_stack, + is_async_call_builtin, result_loc, return_type); + if (get_scope_typeof(source_instr->scope) == nullptr) { + parent_fn_entry->call_list.append(new_call_instruction); + } + return ir_finish_anal(ira, &new_call_instruction->base); +} + +static IrInstGen *ir_analyze_fn_call_src(IrAnalyze *ira, IrInstSrcCall *call_instruction, + ZigFn *fn_entry, ZigType *fn_type, IrInstGen *fn_ref, + IrInstGen *first_arg_ptr, IrInst *first_arg_ptr_src, CallModifier modifier) +{ + IrInstGen *new_stack = nullptr; + IrInst *new_stack_src = nullptr; + if (call_instruction->new_stack) { + new_stack = call_instruction->new_stack->child; + if (type_is_invalid(new_stack->value->type)) + return ira->codegen->invalid_inst_gen; + new_stack_src = &call_instruction->new_stack->base; + } + IrInstGen **args_ptr = heap::c_allocator.allocate(call_instruction->arg_count); + for (size_t i = 0; i < call_instruction->arg_count; i += 1) { + args_ptr[i] = call_instruction->args[i]->child; + if (type_is_invalid(args_ptr[i]->value->type)) + return ira->codegen->invalid_inst_gen; + } + IrInstGen *ret_ptr = nullptr; + if (call_instruction->ret_ptr != nullptr) { + ret_ptr = call_instruction->ret_ptr->child; + if (type_is_invalid(ret_ptr->value->type)) + return ira->codegen->invalid_inst_gen; + } + IrInstGen *result = ir_analyze_fn_call(ira, &call_instruction->base.base, fn_entry, fn_type, fn_ref, + first_arg_ptr, first_arg_ptr_src, modifier, new_stack, new_stack_src, + call_instruction->is_async_call_builtin, args_ptr, call_instruction->arg_count, ret_ptr, + call_instruction->result_loc); + heap::c_allocator.deallocate(args_ptr, call_instruction->arg_count); + return result; +} + +static IrInstGen *ir_analyze_call_extra(IrAnalyze *ira, IrInst* source_instr, + IrInstSrc *pass1_options, IrInstSrc *pass1_fn_ref, IrInstGen **args_ptr, size_t args_len, + ResultLoc *result_loc) +{ + IrInstGen *options = pass1_options->child; + if (type_is_invalid(options->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *fn_ref = pass1_fn_ref->child; + if (type_is_invalid(fn_ref->value->type)) + return ira->codegen->invalid_inst_gen; + + TypeStructField *modifier_field = find_struct_type_field(options->value->type, buf_create_from_str("modifier")); + ir_assert(modifier_field != nullptr, source_instr); + IrInstGen *modifier_inst = ir_analyze_struct_value_field_value(ira, source_instr, options, modifier_field); + ZigValue *modifier_val = ir_resolve_const(ira, modifier_inst, UndefBad); + if (modifier_val == nullptr) + return ira->codegen->invalid_inst_gen; + CallModifier modifier = (CallModifier)bigint_as_u32(&modifier_val->data.x_enum_tag); + + if (ir_should_inline(ira->old_irb.exec, source_instr->scope)) { + switch (modifier) { + case CallModifierBuiltin: + zig_unreachable(); + case CallModifierAsync: + ir_add_error(ira, source_instr, buf_sprintf("TODO: comptime @call with async modifier")); + return ira->codegen->invalid_inst_gen; + case CallModifierCompileTime: + case CallModifierNone: + case CallModifierAlwaysInline: + case CallModifierAlwaysTail: + case CallModifierNoSuspend: + modifier = CallModifierCompileTime; + break; + case CallModifierNeverInline: + ir_add_error(ira, source_instr, + buf_sprintf("unable to perform 'never_inline' call at compile-time")); + return ira->codegen->invalid_inst_gen; + case CallModifierNeverTail: + ir_add_error(ira, source_instr, + buf_sprintf("unable to perform 'never_tail' call at compile-time")); + return ira->codegen->invalid_inst_gen; + } + } + + IrInstGen *first_arg_ptr = nullptr; + IrInst *first_arg_ptr_src = nullptr; + ZigFn *fn = nullptr; + if (instr_is_comptime(fn_ref)) { + if (fn_ref->value->type->id == ZigTypeIdBoundFn) { + assert(fn_ref->value->special == ConstValSpecialStatic); + fn = fn_ref->value->data.x_bound_fn.fn; + first_arg_ptr = fn_ref->value->data.x_bound_fn.first_arg; + first_arg_ptr_src = fn_ref->value->data.x_bound_fn.first_arg_src; + if (type_is_invalid(first_arg_ptr->value->type)) + return ira->codegen->invalid_inst_gen; + } else { + fn = ir_resolve_fn(ira, fn_ref); + } + } + + // Some modifiers require the callee to be comptime-known + switch (modifier) { + case CallModifierCompileTime: + case CallModifierAlwaysInline: + case CallModifierAsync: + if (fn == nullptr) { + ir_add_error(ira, &modifier_inst->base, + buf_sprintf("the specified modifier requires a comptime-known function")); + return ira->codegen->invalid_inst_gen; + } + ZIG_FALLTHROUGH; + default: + break; + } + + ZigType *fn_type = (fn != nullptr) ? fn->type_entry : fn_ref->value->type; + + TypeStructField *stack_field = find_struct_type_field(options->value->type, buf_create_from_str("stack")); + ir_assert(stack_field != nullptr, source_instr); + IrInstGen *opt_stack = ir_analyze_struct_value_field_value(ira, source_instr, options, stack_field); + if (type_is_invalid(opt_stack->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *stack_is_non_null_inst = ir_analyze_test_non_null(ira, source_instr, opt_stack); + bool stack_is_non_null; + if (!ir_resolve_bool(ira, stack_is_non_null_inst, &stack_is_non_null)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *stack = nullptr; + IrInst *stack_src = nullptr; + if (stack_is_non_null) { + stack = ir_analyze_optional_value_payload_value(ira, source_instr, opt_stack, false); + if (type_is_invalid(stack->value->type)) + return ira->codegen->invalid_inst_gen; + stack_src = &stack->base; + } + + return ir_analyze_fn_call(ira, source_instr, fn, fn_type, fn_ref, first_arg_ptr, first_arg_ptr_src, + modifier, stack, stack_src, false, args_ptr, args_len, nullptr, result_loc); +} + +static IrInstGen *ir_analyze_async_call_extra(IrAnalyze *ira, IrInst* source_instr, CallModifier modifier, + IrInstSrc *pass1_fn_ref, IrInstSrc *ret_ptr, IrInstSrc *new_stack, IrInstGen **args_ptr, size_t args_len, ResultLoc *result_loc) +{ + IrInstGen *fn_ref = pass1_fn_ref->child; + if (type_is_invalid(fn_ref->value->type)) + return ira->codegen->invalid_inst_gen; + + if (ir_should_inline(ira->old_irb.exec, source_instr->scope)) { + ir_add_error(ira, source_instr, buf_sprintf("TODO: comptime @asyncCall")); + return ira->codegen->invalid_inst_gen; + } + + IrInstGen *first_arg_ptr = nullptr; + IrInst *first_arg_ptr_src = nullptr; + ZigFn *fn = nullptr; + if (instr_is_comptime(fn_ref)) { + if (fn_ref->value->type->id == ZigTypeIdBoundFn) { + assert(fn_ref->value->special == ConstValSpecialStatic); + fn = fn_ref->value->data.x_bound_fn.fn; + first_arg_ptr = fn_ref->value->data.x_bound_fn.first_arg; + first_arg_ptr_src = fn_ref->value->data.x_bound_fn.first_arg_src; + if (type_is_invalid(first_arg_ptr->value->type)) + return ira->codegen->invalid_inst_gen; + } else { + fn = ir_resolve_fn(ira, fn_ref); + } + } + + IrInstGen *ret_ptr_uncasted = nullptr; + if (ret_ptr != nullptr) { + ret_ptr_uncasted = ret_ptr->child; + if (type_is_invalid(ret_ptr_uncasted->value->type)) + return ira->codegen->invalid_inst_gen; + } + + ZigType *fn_type = (fn != nullptr) ? fn->type_entry : fn_ref->value->type; + IrInstGen *casted_new_stack = analyze_casted_new_stack(ira, source_instr, new_stack->child, + &new_stack->base, true, fn); + if (casted_new_stack != nullptr && type_is_invalid(casted_new_stack->value->type)) + return ira->codegen->invalid_inst_gen; + + return ir_analyze_fn_call(ira, source_instr, fn, fn_type, fn_ref, first_arg_ptr, first_arg_ptr_src, + modifier, casted_new_stack, &new_stack->base, true, args_ptr, args_len, ret_ptr_uncasted, result_loc); +} + +static bool ir_extract_tuple_call_args(IrAnalyze *ira, IrInst *source_instr, IrInstGen *args, IrInstGen ***args_ptr, size_t *args_len) { + ZigType *args_type = args->value->type; + if (type_is_invalid(args_type)) + return false; + + if (args_type->id != ZigTypeIdStruct) { + ir_add_error(ira, &args->base, + buf_sprintf("expected tuple or struct, found '%s'", buf_ptr(&args_type->name))); + return false; + } + + if (is_tuple(args_type)) { + *args_len = args_type->data.structure.src_field_count; + *args_ptr = heap::c_allocator.allocate(*args_len); + for (size_t i = 0; i < *args_len; i += 1) { + TypeStructField *arg_field = args_type->data.structure.fields[i]; + (*args_ptr)[i] = ir_analyze_struct_value_field_value(ira, source_instr, args, arg_field); + if (type_is_invalid((*args_ptr)[i]->value->type)) + return false; + } + } else { + ir_add_error(ira, &args->base, buf_sprintf("TODO: struct args")); + return false; + } + return true; +} + +static IrInstGen *ir_analyze_instruction_call_extra(IrAnalyze *ira, IrInstSrcCallExtra *instruction) { + IrInstGen *args = instruction->args->child; + IrInstGen **args_ptr = nullptr; + size_t args_len = 0; + if (!ir_extract_tuple_call_args(ira, &instruction->base.base, args, &args_ptr, &args_len)) { + return ira->codegen->invalid_inst_gen; + } + + IrInstGen *result = ir_analyze_call_extra(ira, &instruction->base.base, instruction->options, + instruction->fn_ref, args_ptr, args_len, instruction->result_loc); + heap::c_allocator.deallocate(args_ptr, args_len); + return result; +} + +static IrInstGen *ir_analyze_instruction_async_call_extra(IrAnalyze *ira, IrInstSrcAsyncCallExtra *instruction) { + IrInstGen *args = instruction->args->child; + IrInstGen **args_ptr = nullptr; + size_t args_len = 0; + if (!ir_extract_tuple_call_args(ira, &instruction->base.base, args, &args_ptr, &args_len)) { + return ira->codegen->invalid_inst_gen; + } + + IrInstGen *result = ir_analyze_async_call_extra(ira, &instruction->base.base, instruction->modifier, + instruction->fn_ref, instruction->ret_ptr, instruction->new_stack, args_ptr, args_len, instruction->result_loc); + heap::c_allocator.deallocate(args_ptr, args_len); + return result; +} + +static IrInstGen *ir_analyze_instruction_call_args(IrAnalyze *ira, IrInstSrcCallArgs *instruction) { + IrInstGen **args_ptr = heap::c_allocator.allocate(instruction->args_len); + for (size_t i = 0; i < instruction->args_len; i += 1) { + args_ptr[i] = instruction->args_ptr[i]->child; + if (type_is_invalid(args_ptr[i]->value->type)) + return ira->codegen->invalid_inst_gen; + } + + IrInstGen *result = ir_analyze_call_extra(ira, &instruction->base.base, instruction->options, + instruction->fn_ref, args_ptr, instruction->args_len, instruction->result_loc); + heap::c_allocator.deallocate(args_ptr, instruction->args_len); + return result; +} + +static IrInstGen *ir_analyze_instruction_call(IrAnalyze *ira, IrInstSrcCall *call_instruction) { + IrInstGen *fn_ref = call_instruction->fn_ref->child; + if (type_is_invalid(fn_ref->value->type)) + return ira->codegen->invalid_inst_gen; + + bool is_comptime = (call_instruction->modifier == CallModifierCompileTime) || + ir_should_inline(ira->old_irb.exec, call_instruction->base.base.scope); + CallModifier modifier = is_comptime ? CallModifierCompileTime : call_instruction->modifier; + + if (is_comptime || instr_is_comptime(fn_ref)) { + if (fn_ref->value->type->id == ZigTypeIdMetaType) { + ZigType *ty = ir_resolve_type(ira, fn_ref); + if (ty == nullptr) + return ira->codegen->invalid_inst_gen; + ErrorMsg *msg = ir_add_error(ira, &fn_ref->base, + buf_sprintf("type '%s' not a function", buf_ptr(&ty->name))); + add_error_note(ira->codegen, msg, call_instruction->base.base.source_node, + buf_sprintf("use @as builtin for type coercion")); + return ira->codegen->invalid_inst_gen; + } else if (fn_ref->value->type->id == ZigTypeIdFn) { + ZigFn *fn_table_entry = ir_resolve_fn(ira, fn_ref); + ZigType *fn_type = fn_table_entry ? fn_table_entry->type_entry : fn_ref->value->type; + CallModifier modifier = is_comptime ? CallModifierCompileTime : call_instruction->modifier; + return ir_analyze_fn_call_src(ira, call_instruction, fn_table_entry, fn_type, + fn_ref, nullptr, nullptr, modifier); + } else if (fn_ref->value->type->id == ZigTypeIdBoundFn) { + assert(fn_ref->value->special == ConstValSpecialStatic); + ZigFn *fn_table_entry = fn_ref->value->data.x_bound_fn.fn; + IrInstGen *first_arg_ptr = fn_ref->value->data.x_bound_fn.first_arg; + IrInst *first_arg_ptr_src = fn_ref->value->data.x_bound_fn.first_arg_src; + CallModifier modifier = is_comptime ? CallModifierCompileTime : call_instruction->modifier; + return ir_analyze_fn_call_src(ira, call_instruction, fn_table_entry, fn_table_entry->type_entry, + fn_ref, first_arg_ptr, first_arg_ptr_src, modifier); + } else { + ir_add_error(ira, &fn_ref->base, + buf_sprintf("type '%s' not a function", buf_ptr(&fn_ref->value->type->name))); + return ira->codegen->invalid_inst_gen; + } + } + + if (fn_ref->value->type->id == ZigTypeIdFn) { + return ir_analyze_fn_call_src(ira, call_instruction, nullptr, fn_ref->value->type, + fn_ref, nullptr, nullptr, modifier); + } else { + ir_add_error(ira, &fn_ref->base, + buf_sprintf("type '%s' not a function", buf_ptr(&fn_ref->value->type->name))); + return ira->codegen->invalid_inst_gen; + } +} + +// out_val->type must be the type to read the pointer as +// if the type is different than the actual type then it does a comptime byte reinterpretation +static Error ir_read_const_ptr(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, + ZigValue *out_val, ZigValue *ptr_val) +{ + Error err; + assert(out_val->type != nullptr); + + ZigValue *pointee = const_ptr_pointee_unchecked(codegen, ptr_val); + src_assert(pointee->type != nullptr, source_node); + + if ((err = type_resolve(codegen, pointee->type, ResolveStatusSizeKnown))) + return ErrorSemanticAnalyzeFail; + if ((err = type_resolve(codegen, out_val->type, ResolveStatusSizeKnown))) + return ErrorSemanticAnalyzeFail; + + size_t src_size = type_size(codegen, pointee->type); + size_t dst_size = type_size(codegen, out_val->type); + + if (dst_size <= src_size) { + if (src_size == dst_size && types_have_same_zig_comptime_repr(codegen, out_val->type, pointee->type)) { + copy_const_val(codegen, out_val, pointee); + return ErrorNone; + } + Buf buf = BUF_INIT; + buf_resize(&buf, src_size); + buf_write_value_bytes(codegen, (uint8_t*)buf_ptr(&buf), pointee); + if ((err = buf_read_value_bytes(ira, codegen, source_node, (uint8_t*)buf_ptr(&buf), out_val))) + return err; + buf_deinit(&buf); + return ErrorNone; + } + + switch (ptr_val->data.x_ptr.special) { + case ConstPtrSpecialInvalid: + zig_unreachable(); + case ConstPtrSpecialNull: + if (dst_size == 0) + return ErrorNone; + opt_ir_add_error_node(ira, codegen, source_node, + buf_sprintf("attempt to read %" ZIG_PRI_usize " bytes from null pointer", + dst_size)); + return ErrorSemanticAnalyzeFail; + case ConstPtrSpecialRef: { + opt_ir_add_error_node(ira, codegen, source_node, + buf_sprintf("attempt to read %" ZIG_PRI_usize " bytes from pointer to %s which is %" ZIG_PRI_usize " bytes", + dst_size, buf_ptr(&pointee->type->name), src_size)); + return ErrorSemanticAnalyzeFail; + } + case ConstPtrSpecialSubArray: { + ZigValue *array_val = ptr_val->data.x_ptr.data.base_array.array_val; + assert(array_val->type->id == ZigTypeIdArray); + if (array_val->data.x_array.special != ConstArraySpecialNone) + zig_panic("TODO"); + if (dst_size > src_size) { + size_t elem_index = ptr_val->data.x_ptr.data.base_array.elem_index; + opt_ir_add_error_node(ira, codegen, source_node, + buf_sprintf("attempt to read %" ZIG_PRI_usize " bytes from %s at index %" ZIG_PRI_usize " which is %" ZIG_PRI_usize " bytes", + dst_size, buf_ptr(&array_val->type->name), elem_index, src_size)); + return ErrorSemanticAnalyzeFail; + } + size_t elem_size = src_size; + size_t elem_count = (dst_size % elem_size == 0) ? (dst_size / elem_size) : (dst_size / elem_size + 1); + Buf buf = BUF_INIT; + buf_resize(&buf, elem_count * elem_size); + for (size_t i = 0; i < elem_count; i += 1) { + ZigValue *elem_val = &array_val->data.x_array.data.s_none.elements[i]; + buf_write_value_bytes(codegen, (uint8_t*)buf_ptr(&buf) + (i * elem_size), elem_val); + } + if ((err = buf_read_value_bytes(ira, codegen, source_node, (uint8_t*)buf_ptr(&buf), out_val))) + return err; + buf_deinit(&buf); + return ErrorNone; + } + case ConstPtrSpecialBaseArray: { + ZigValue *array_val = ptr_val->data.x_ptr.data.base_array.array_val; + assert(array_val->type->id == ZigTypeIdArray); + if (array_val->data.x_array.special != ConstArraySpecialNone) + zig_panic("TODO"); + size_t elem_size = src_size; + size_t elem_index = ptr_val->data.x_ptr.data.base_array.elem_index; + src_size = elem_size * (array_val->type->data.array.len - elem_index); + if (dst_size > src_size) { + opt_ir_add_error_node(ira, codegen, source_node, + buf_sprintf("attempt to read %" ZIG_PRI_usize " bytes from %s at index %" ZIG_PRI_usize " which is %" ZIG_PRI_usize " bytes", + dst_size, buf_ptr(&array_val->type->name), elem_index, src_size)); + return ErrorSemanticAnalyzeFail; + } + size_t elem_count = (dst_size % elem_size == 0) ? (dst_size / elem_size) : (dst_size / elem_size + 1); + Buf buf = BUF_INIT; + buf_resize(&buf, elem_count * elem_size); + for (size_t i = 0; i < elem_count; i += 1) { + ZigValue *elem_val = &array_val->data.x_array.data.s_none.elements[elem_index + i]; + buf_write_value_bytes(codegen, (uint8_t*)buf_ptr(&buf) + (i * elem_size), elem_val); + } + if ((err = buf_read_value_bytes(ira, codegen, source_node, (uint8_t*)buf_ptr(&buf), out_val))) + return err; + buf_deinit(&buf); + return ErrorNone; + } + case ConstPtrSpecialBaseStruct: + case ConstPtrSpecialBaseErrorUnionCode: + case ConstPtrSpecialBaseErrorUnionPayload: + case ConstPtrSpecialBaseOptionalPayload: + case ConstPtrSpecialDiscard: + case ConstPtrSpecialHardCodedAddr: + case ConstPtrSpecialFunction: + zig_panic("TODO"); + } + zig_unreachable(); +} + +static IrInstGen *ir_analyze_optional_type(IrAnalyze *ira, IrInstSrcUnOp *instruction) { + IrInstGen *result = ir_const(ira, &instruction->base.base, ira->codegen->builtin_types.entry_type); + result->value->special = ConstValSpecialLazy; + + LazyValueOptType *lazy_opt_type = heap::c_allocator.create(); + lazy_opt_type->ira = ira; ira_ref(ira); + result->value->data.x_lazy = &lazy_opt_type->base; + lazy_opt_type->base.id = LazyValueIdOptType; + + lazy_opt_type->payload_type = instruction->value->child; + if (ir_resolve_type_lazy(ira, lazy_opt_type->payload_type) == nullptr) + return ira->codegen->invalid_inst_gen; + + return result; +} + +static ErrorMsg *ir_eval_negation_scalar(IrAnalyze *ira, IrInst* source_instr, ZigType *scalar_type, + ZigValue *operand_val, ZigValue *scalar_out_val, bool is_wrap_op) +{ + bool is_float = (scalar_type->id == ZigTypeIdFloat || scalar_type->id == ZigTypeIdComptimeFloat); + + bool ok_type = ((scalar_type->id == ZigTypeIdInt && scalar_type->data.integral.is_signed) || + scalar_type->id == ZigTypeIdComptimeInt || (is_float && !is_wrap_op)); + + if (!ok_type) { + const char *fmt = is_wrap_op ? "invalid wrapping negation type: '%s'" : "invalid negation type: '%s'"; + return ir_add_error(ira, source_instr, buf_sprintf(fmt, buf_ptr(&scalar_type->name))); + } + + if (is_float) { + float_negate(scalar_out_val, operand_val); + } else if (is_wrap_op) { + bigint_negate_wrap(&scalar_out_val->data.x_bigint, &operand_val->data.x_bigint, + scalar_type->data.integral.bit_count); + } else { + bigint_negate(&scalar_out_val->data.x_bigint, &operand_val->data.x_bigint); + } + + scalar_out_val->type = scalar_type; + scalar_out_val->special = ConstValSpecialStatic; + + if (is_wrap_op || is_float || scalar_type->id == ZigTypeIdComptimeInt) { + return nullptr; + } + + if (!bigint_fits_in_bits(&scalar_out_val->data.x_bigint, scalar_type->data.integral.bit_count, true)) { + return ir_add_error(ira, source_instr, buf_sprintf("negation caused overflow")); + } + return nullptr; +} + +static IrInstGen *ir_analyze_negation(IrAnalyze *ira, IrInstSrcUnOp *instruction) { + IrInstGen *value = instruction->value->child; + ZigType *expr_type = value->value->type; + if (type_is_invalid(expr_type)) + return ira->codegen->invalid_inst_gen; + + bool is_wrap_op = (instruction->op_id == IrUnOpNegationWrap); + + switch (expr_type->id) { + case ZigTypeIdComptimeInt: + case ZigTypeIdFloat: + case ZigTypeIdComptimeFloat: + case ZigTypeIdVector: + break; + case ZigTypeIdInt: + if (is_wrap_op || expr_type->data.integral.is_signed) + break; + ZIG_FALLTHROUGH; + default: + ir_add_error(ira, &instruction->base.base, + buf_sprintf("negation of type '%s'", buf_ptr(&expr_type->name))); + return ira->codegen->invalid_inst_gen; + } + + ZigType *scalar_type = (expr_type->id == ZigTypeIdVector) ? expr_type->data.vector.elem_type : expr_type; + + if (instr_is_comptime(value)) { + ZigValue *operand_val = ir_resolve_const(ira, value, UndefBad); + if (!operand_val) + return ira->codegen->invalid_inst_gen; + + IrInstGen *result_instruction = ir_const(ira, &instruction->base.base, expr_type); + ZigValue *out_val = result_instruction->value; + if (expr_type->id == ZigTypeIdVector) { + expand_undef_array(ira->codegen, operand_val); + out_val->special = ConstValSpecialUndef; + expand_undef_array(ira->codegen, out_val); + size_t len = expr_type->data.vector.len; + for (size_t i = 0; i < len; i += 1) { + ZigValue *scalar_operand_val = &operand_val->data.x_array.data.s_none.elements[i]; + ZigValue *scalar_out_val = &out_val->data.x_array.data.s_none.elements[i]; + assert(scalar_operand_val->type == scalar_type); + assert(scalar_out_val->type == scalar_type); + ErrorMsg *msg = ir_eval_negation_scalar(ira, &instruction->base.base, scalar_type, + scalar_operand_val, scalar_out_val, is_wrap_op); + if (msg != nullptr) { + add_error_note(ira->codegen, msg, instruction->base.base.source_node, + buf_sprintf("when computing vector element at index %" ZIG_PRI_usize, i)); + return ira->codegen->invalid_inst_gen; + } + } + out_val->type = expr_type; + out_val->special = ConstValSpecialStatic; + } else { + if (ir_eval_negation_scalar(ira, &instruction->base.base, scalar_type, operand_val, out_val, + is_wrap_op) != nullptr) + { + return ira->codegen->invalid_inst_gen; + } + } + return result_instruction; + } + + if (is_wrap_op) { + return ir_build_negation_wrapping(ira, &instruction->base.base, value, expr_type); + } else { + return ir_build_negation(ira, &instruction->base.base, value, expr_type); + } +} + +static IrInstGen *ir_analyze_bin_not(IrAnalyze *ira, IrInstSrcUnOp *instruction) { + IrInstGen *value = instruction->value->child; + ZigType *expr_type = value->value->type; + if (type_is_invalid(expr_type)) + return ira->codegen->invalid_inst_gen; + + ZigType *scalar_type = (expr_type->id == ZigTypeIdVector) ? + expr_type->data.vector.elem_type : expr_type; + + if (scalar_type->id != ZigTypeIdInt) { + ir_add_error(ira, &instruction->base.base, + buf_sprintf("unable to perform binary not operation on type '%s'", buf_ptr(&expr_type->name))); + return ira->codegen->invalid_inst_gen; + } + + if (instr_is_comptime(value)) { + ZigValue *expr_val = ir_resolve_const(ira, value, UndefBad); + if (expr_val == nullptr) + return ira->codegen->invalid_inst_gen; + + IrInstGen *result = ir_const(ira, &instruction->base.base, expr_type); + + if (expr_type->id == ZigTypeIdVector) { + expand_undef_array(ira->codegen, expr_val); + result->value->special = ConstValSpecialUndef; + expand_undef_array(ira->codegen, result->value); + + for (size_t i = 0; i < expr_type->data.vector.len; i++) { + ZigValue *src_val = &expr_val->data.x_array.data.s_none.elements[i]; + ZigValue *dst_val = &result->value->data.x_array.data.s_none.elements[i]; + + dst_val->type = scalar_type; + dst_val->special = ConstValSpecialStatic; + bigint_not(&dst_val->data.x_bigint, &src_val->data.x_bigint, + scalar_type->data.integral.bit_count, scalar_type->data.integral.is_signed); + } + } else { + bigint_not(&result->value->data.x_bigint, &expr_val->data.x_bigint, + scalar_type->data.integral.bit_count, scalar_type->data.integral.is_signed); + } + + return result; + } + + return ir_build_binary_not(ira, &instruction->base.base, value, expr_type); +} + +static IrInstGen *ir_analyze_instruction_un_op(IrAnalyze *ira, IrInstSrcUnOp *instruction) { + IrUnOp op_id = instruction->op_id; + switch (op_id) { + case IrUnOpInvalid: + zig_unreachable(); + case IrUnOpBinNot: + return ir_analyze_bin_not(ira, instruction); + case IrUnOpNegation: + case IrUnOpNegationWrap: + return ir_analyze_negation(ira, instruction); + case IrUnOpDereference: { + IrInstGen *ptr = instruction->value->child; + if (type_is_invalid(ptr->value->type)) + return ira->codegen->invalid_inst_gen; + ZigType *ptr_type = ptr->value->type; + if (ptr_type->id == ZigTypeIdPointer && ptr_type->data.pointer.ptr_len == PtrLenUnknown) { + ir_add_error_node(ira, instruction->base.base.source_node, + buf_sprintf("index syntax required for unknown-length pointer type '%s'", + buf_ptr(&ptr_type->name))); + return ira->codegen->invalid_inst_gen; + } + + IrInstGen *result = ir_get_deref(ira, &instruction->base.base, ptr, instruction->result_loc); + if (type_is_invalid(result->value->type)) + return ira->codegen->invalid_inst_gen; + + // If the result needs to be an lvalue, type check it + if (instruction->lval != LValNone && result->value->type->id != ZigTypeIdPointer) { + ir_add_error(ira, &instruction->base.base, + buf_sprintf("attempt to dereference non-pointer type '%s'", buf_ptr(&result->value->type->name))); + return ira->codegen->invalid_inst_gen; + } + + return result; + } + case IrUnOpOptional: + return ir_analyze_optional_type(ira, instruction); + } + zig_unreachable(); +} + +static void ir_push_resume(IrAnalyze *ira, IrSuspendPosition pos) { + IrBasicBlockSrc *old_bb = ira->old_irb.exec->basic_block_list.at(pos.basic_block_index); + if (old_bb->in_resume_stack) return; + ira->resume_stack.append(pos); + old_bb->in_resume_stack = true; +} + +static void ir_push_resume_block(IrAnalyze *ira, IrBasicBlockSrc *old_bb) { + if (ira->resume_stack.length != 0) { + ir_push_resume(ira, {old_bb->index, 0}); + } +} + +static IrInstGen *ir_analyze_instruction_br(IrAnalyze *ira, IrInstSrcBr *br_instruction) { + IrBasicBlockSrc *old_dest_block = br_instruction->dest_block; + + bool is_comptime; + if (!ir_resolve_comptime(ira, br_instruction->is_comptime->child, &is_comptime)) + return ir_unreach_error(ira); + + if (is_comptime || (old_dest_block->ref_count == 1 && old_dest_block->suspend_instruction_ref == nullptr)) + return ir_inline_bb(ira, &br_instruction->base.base, old_dest_block); + + IrBasicBlockGen *new_bb = ir_get_new_bb_runtime(ira, old_dest_block, &br_instruction->base.base); + if (new_bb == nullptr) + return ir_unreach_error(ira); + + ir_push_resume_block(ira, old_dest_block); + + IrInstGen *result = ir_build_br_gen(ira, &br_instruction->base.base, new_bb); + return ir_finish_anal(ira, result); +} + +static IrInstGen *ir_analyze_instruction_cond_br(IrAnalyze *ira, IrInstSrcCondBr *cond_br_instruction) { + IrInstGen *condition = cond_br_instruction->condition->child; + if (type_is_invalid(condition->value->type)) + return ir_unreach_error(ira); + + bool is_comptime; + if (!ir_resolve_comptime(ira, cond_br_instruction->is_comptime->child, &is_comptime)) + return ir_unreach_error(ira); + + ZigType *bool_type = ira->codegen->builtin_types.entry_bool; + IrInstGen *casted_condition = ir_implicit_cast(ira, condition, bool_type); + if (type_is_invalid(casted_condition->value->type)) + return ir_unreach_error(ira); + + if (is_comptime || instr_is_comptime(casted_condition)) { + bool cond_is_true; + if (!ir_resolve_bool(ira, casted_condition, &cond_is_true)) + return ir_unreach_error(ira); + + IrBasicBlockSrc *old_dest_block = cond_is_true ? + cond_br_instruction->then_block : cond_br_instruction->else_block; + + if (is_comptime || (old_dest_block->ref_count == 1 && old_dest_block->suspend_instruction_ref == nullptr)) + return ir_inline_bb(ira, &cond_br_instruction->base.base, old_dest_block); + + IrBasicBlockGen *new_dest_block = ir_get_new_bb_runtime(ira, old_dest_block, &cond_br_instruction->base.base); + if (new_dest_block == nullptr) + return ir_unreach_error(ira); + + ir_push_resume_block(ira, old_dest_block); + + IrInstGen *result = ir_build_br_gen(ira, &cond_br_instruction->base.base, new_dest_block); + return ir_finish_anal(ira, result); + } + + assert(cond_br_instruction->then_block != cond_br_instruction->else_block); + IrBasicBlockGen *new_then_block = ir_get_new_bb_runtime(ira, cond_br_instruction->then_block, &cond_br_instruction->base.base); + if (new_then_block == nullptr) + return ir_unreach_error(ira); + + IrBasicBlockGen *new_else_block = ir_get_new_bb_runtime(ira, cond_br_instruction->else_block, &cond_br_instruction->base.base); + if (new_else_block == nullptr) + return ir_unreach_error(ira); + + ir_push_resume_block(ira, cond_br_instruction->else_block); + ir_push_resume_block(ira, cond_br_instruction->then_block); + + IrInstGen *result = ir_build_cond_br_gen(ira, &cond_br_instruction->base.base, + casted_condition, new_then_block, new_else_block); + return ir_finish_anal(ira, result); +} + +static IrInstGen *ir_analyze_instruction_unreachable(IrAnalyze *ira, + IrInstSrcUnreachable *unreachable_instruction) +{ + IrInstGen *result = ir_build_unreachable_gen(ira, &unreachable_instruction->base.base); + return ir_finish_anal(ira, result); +} + +static IrInstGen *ir_analyze_instruction_phi(IrAnalyze *ira, IrInstSrcPhi *phi_instruction) { + Error err; + + if (ira->const_predecessor_bb) { + for (size_t i = 0; i < phi_instruction->incoming_count; i += 1) { + IrBasicBlockSrc *predecessor = phi_instruction->incoming_blocks[i]; + if (predecessor != ira->const_predecessor_bb) + continue; + IrInstGen *value = phi_instruction->incoming_values[i]->child; + assert(value->value->type); + if (type_is_invalid(value->value->type)) + return ira->codegen->invalid_inst_gen; + + if (value->value->special != ConstValSpecialRuntime) { + IrInstGen *result = ir_const(ira, &phi_instruction->base.base, nullptr); + copy_const_val(ira->codegen, result->value, value->value); + return result; + } else { + return value; + } + } + zig_unreachable(); + } + + ResultLocPeerParent *peer_parent = phi_instruction->peer_parent; + if (peer_parent != nullptr && !peer_parent->skipped && !peer_parent->done_resuming && + peer_parent->peers.length >= 2) + { + if (peer_parent->resolved_type == nullptr) { + IrInstGen **instructions = heap::c_allocator.allocate(peer_parent->peers.length); + for (size_t i = 0; i < peer_parent->peers.length; i += 1) { + ResultLocPeer *this_peer = peer_parent->peers.at(i); + + IrInstGen *gen_instruction = this_peer->base.gen_instruction; + if (gen_instruction == nullptr) { + // unreachable instructions will cause implicit_elem_type to be null + if (this_peer->base.implicit_elem_type == nullptr) { + instructions[i] = ir_const_unreachable(ira, &this_peer->base.source_instruction->base); + } else { + instructions[i] = ir_const(ira, &this_peer->base.source_instruction->base, + this_peer->base.implicit_elem_type); + instructions[i]->value->special = ConstValSpecialRuntime; + } + } else { + instructions[i] = gen_instruction; + } + + } + ZigType *expected_type = ir_result_loc_expected_type(ira, &phi_instruction->base.base, peer_parent->parent); + peer_parent->resolved_type = ir_resolve_peer_types(ira, + peer_parent->base.source_instruction->base.source_node, expected_type, instructions, + peer_parent->peers.length); + if (type_is_invalid(peer_parent->resolved_type)) + return ira->codegen->invalid_inst_gen; + + // the logic below assumes there are no instructions in the new current basic block yet + ir_assert(ira->new_irb.current_basic_block->instruction_list.length == 0, &phi_instruction->base.base); + + // In case resolving the parent activates a suspend, do it now + IrInstGen *parent_result_loc = ir_resolve_result(ira, &phi_instruction->base.base, peer_parent->parent, + peer_parent->resolved_type, nullptr, false, true); + if (parent_result_loc != nullptr && + (type_is_invalid(parent_result_loc->value->type) || parent_result_loc->value->type->id == ZigTypeIdUnreachable)) + { + return parent_result_loc; + } + // If the above code generated any instructions in the current basic block, we need + // to move them to the peer parent predecessor. + ZigList instrs_to_move = {}; + while (ira->new_irb.current_basic_block->instruction_list.length != 0) { + instrs_to_move.append(ira->new_irb.current_basic_block->instruction_list.pop()); + } + if (instrs_to_move.length != 0) { + IrBasicBlockGen *predecessor = peer_parent->base.source_instruction->child->owner_bb; + IrInstGen *branch_instruction = predecessor->instruction_list.pop(); + ir_assert(branch_instruction->value->type->id == ZigTypeIdUnreachable, &phi_instruction->base.base); + while (instrs_to_move.length != 0) { + predecessor->instruction_list.append(instrs_to_move.pop()); + } + predecessor->instruction_list.append(branch_instruction); + } + } + + IrSuspendPosition suspend_pos; + ira_suspend(ira, &phi_instruction->base.base, nullptr, &suspend_pos); + ir_push_resume(ira, suspend_pos); + + for (size_t i = 0; i < peer_parent->peers.length; i += 1) { + ResultLocPeer *opposite_peer = peer_parent->peers.at(peer_parent->peers.length - i - 1); + if (opposite_peer->base.implicit_elem_type != nullptr && + opposite_peer->base.implicit_elem_type->id != ZigTypeIdUnreachable) + { + ir_push_resume(ira, opposite_peer->suspend_pos); + } + } + + peer_parent->done_resuming = true; + return ira_resume(ira); + } + + ZigList new_incoming_blocks = {0}; + ZigList new_incoming_values = {0}; + + for (size_t i = 0; i < phi_instruction->incoming_count; i += 1) { + IrBasicBlockSrc *predecessor = phi_instruction->incoming_blocks[i]; + if (predecessor->ref_count == 0) + continue; + + + IrInstSrc *old_value = phi_instruction->incoming_values[i]; + assert(old_value); + IrInstGen *new_value = old_value->child; + if (!new_value || new_value->value->type->id == ZigTypeIdUnreachable || predecessor->child == nullptr) + continue; + + if (type_is_invalid(new_value->value->type)) + return ira->codegen->invalid_inst_gen; + + + assert(predecessor->child); + new_incoming_blocks.append(predecessor->child); + new_incoming_values.append(new_value); + } + + if (new_incoming_blocks.length == 0) { + IrInstGen *result = ir_build_unreachable_gen(ira, &phi_instruction->base.base); + return ir_finish_anal(ira, result); + } + + if (new_incoming_blocks.length == 1) { + return new_incoming_values.at(0); + } + + ZigType *resolved_type = nullptr; + if (peer_parent != nullptr) { + bool peer_parent_has_type; + if ((err = ir_result_has_type(ira, peer_parent->parent, &peer_parent_has_type))) + return ira->codegen->invalid_inst_gen; + if (peer_parent_has_type) { + if (peer_parent->parent->id == ResultLocIdReturn) { + resolved_type = ira->explicit_return_type; + } else if (peer_parent->parent->id == ResultLocIdCast) { + resolved_type = ir_resolve_type(ira, peer_parent->parent->source_instruction->child); + } else if (peer_parent->parent->resolved_loc) { + ZigType *resolved_loc_ptr_type = peer_parent->parent->resolved_loc->value->type; + ir_assert(resolved_loc_ptr_type->id == ZigTypeIdPointer, &phi_instruction->base.base); + resolved_type = resolved_loc_ptr_type->data.pointer.child_type; + } + + if (resolved_type != nullptr && type_is_invalid(resolved_type)) + return ira->codegen->invalid_inst_gen; + } + } + + if (resolved_type == nullptr) { + resolved_type = ir_resolve_peer_types(ira, phi_instruction->base.base.source_node, nullptr, + new_incoming_values.items, new_incoming_values.length); + if (type_is_invalid(resolved_type)) + return ira->codegen->invalid_inst_gen; + } + + switch (type_has_one_possible_value(ira->codegen, resolved_type)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_inst_gen; + case OnePossibleValueYes: + return ir_const_move(ira, &phi_instruction->base.base, + get_the_one_possible_value(ira->codegen, resolved_type)); + case OnePossibleValueNo: + break; + } + + switch (type_requires_comptime(ira->codegen, resolved_type)) { + case ReqCompTimeInvalid: + return ira->codegen->invalid_inst_gen; + case ReqCompTimeYes: + ir_add_error(ira, &phi_instruction->base.base, + buf_sprintf("values of type '%s' must be comptime known", buf_ptr(&resolved_type->name))); + return ira->codegen->invalid_inst_gen; + case ReqCompTimeNo: + break; + } + + bool all_stack_ptrs = (resolved_type->id == ZigTypeIdPointer); + + // cast all values to the resolved type. however we can't put cast instructions in front of the phi instruction. + // so we go back and insert the casts as the last instruction in the corresponding predecessor blocks, and + // then make sure the branch instruction is preserved. + IrBasicBlockGen *cur_bb = ira->new_irb.current_basic_block; + for (size_t i = 0; i < new_incoming_values.length; i += 1) { + IrInstGen *new_value = new_incoming_values.at(i); + IrBasicBlockGen *predecessor = new_incoming_blocks.at(i); + ir_assert(predecessor->instruction_list.length != 0, &phi_instruction->base.base); + IrInstGen *branch_instruction = predecessor->instruction_list.pop(); + ir_set_cursor_at_end_gen(&ira->new_irb, predecessor); + IrInstGen *casted_value = ir_implicit_cast(ira, new_value, resolved_type); + if (type_is_invalid(casted_value->value->type)) { + return ira->codegen->invalid_inst_gen; + } + new_incoming_values.items[i] = casted_value; + predecessor->instruction_list.append(branch_instruction); + + if (all_stack_ptrs && (casted_value->value->special != ConstValSpecialRuntime || + casted_value->value->data.rh_ptr != RuntimeHintPtrStack)) + { + all_stack_ptrs = false; + } + } + ir_set_cursor_at_end_gen(&ira->new_irb, cur_bb); + + IrInstGen *result = ir_build_phi_gen(ira, &phi_instruction->base.base, + new_incoming_blocks.length, new_incoming_blocks.items, new_incoming_values.items, resolved_type); + + if (all_stack_ptrs) { + assert(result->value->special == ConstValSpecialRuntime); + result->value->data.rh_ptr = RuntimeHintPtrStack; + } + + return result; +} + +static IrInstGen *ir_analyze_instruction_var_ptr(IrAnalyze *ira, IrInstSrcVarPtr *instruction) { + ZigVar *var = instruction->var; + IrInstGen *result = ir_get_var_ptr(ira, &instruction->base.base, var); + if (instruction->crossed_fndef_scope != nullptr && !instr_is_comptime(result)) { + ErrorMsg *msg = ir_add_error(ira, &instruction->base.base, + buf_sprintf("'%s' not accessible from inner function", var->name)); + add_error_note(ira->codegen, msg, instruction->crossed_fndef_scope->base.source_node, + buf_sprintf("crossed function definition here")); + add_error_note(ira->codegen, msg, var->decl_node, + buf_sprintf("declared here")); + return ira->codegen->invalid_inst_gen; + } + return result; +} + +static ZigType *adjust_ptr_align(CodeGen *g, ZigType *ptr_type, uint32_t new_align) { + assert(ptr_type->id == ZigTypeIdPointer); + return get_pointer_to_type_extra2(g, + ptr_type->data.pointer.child_type, + ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, + ptr_type->data.pointer.ptr_len, + new_align, + ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes, + ptr_type->data.pointer.allow_zero, + ptr_type->data.pointer.vector_index, + ptr_type->data.pointer.inferred_struct_field, + ptr_type->data.pointer.sentinel); +} + +static ZigType *adjust_ptr_sentinel(CodeGen *g, ZigType *ptr_type, ZigValue *new_sentinel) { + assert(ptr_type->id == ZigTypeIdPointer); + return get_pointer_to_type_extra2(g, + ptr_type->data.pointer.child_type, + ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, + ptr_type->data.pointer.ptr_len, + ptr_type->data.pointer.explicit_alignment, + ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes, + ptr_type->data.pointer.allow_zero, + ptr_type->data.pointer.vector_index, + ptr_type->data.pointer.inferred_struct_field, + new_sentinel); +} + +static ZigType *adjust_slice_align(CodeGen *g, ZigType *slice_type, uint32_t new_align) { + assert(is_slice(slice_type)); + ZigType *ptr_type = adjust_ptr_align(g, slice_type->data.structure.fields[slice_ptr_index]->type_entry, + new_align); + return get_slice_type(g, ptr_type); +} + +static ZigType *adjust_ptr_len(CodeGen *g, ZigType *ptr_type, PtrLen ptr_len) { + assert(ptr_type->id == ZigTypeIdPointer); + return get_pointer_to_type_extra2(g, + ptr_type->data.pointer.child_type, + ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, + ptr_len, + ptr_type->data.pointer.explicit_alignment, + ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes, + ptr_type->data.pointer.allow_zero, + ptr_type->data.pointer.vector_index, + ptr_type->data.pointer.inferred_struct_field, + (ptr_len != PtrLenUnknown) ? nullptr : ptr_type->data.pointer.sentinel); +} + +static ZigType *adjust_ptr_allow_zero(CodeGen *g, ZigType *ptr_type, bool allow_zero) { + assert(ptr_type->id == ZigTypeIdPointer); + return get_pointer_to_type_extra2(g, + ptr_type->data.pointer.child_type, + ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, + ptr_type->data.pointer.ptr_len, + ptr_type->data.pointer.explicit_alignment, + ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes, + allow_zero, + ptr_type->data.pointer.vector_index, + ptr_type->data.pointer.inferred_struct_field, + ptr_type->data.pointer.sentinel); +} + +static ZigType *adjust_ptr_const(CodeGen *g, ZigType *ptr_type, bool is_const) { + assert(ptr_type->id == ZigTypeIdPointer); + return get_pointer_to_type_extra2(g, + ptr_type->data.pointer.child_type, + is_const, ptr_type->data.pointer.is_volatile, + ptr_type->data.pointer.ptr_len, + ptr_type->data.pointer.explicit_alignment, + ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes, + ptr_type->data.pointer.allow_zero, + ptr_type->data.pointer.vector_index, + ptr_type->data.pointer.inferred_struct_field, + ptr_type->data.pointer.sentinel); +} + +static Error compute_elem_align(IrAnalyze *ira, ZigType *elem_type, uint32_t base_ptr_align, + uint64_t elem_index, uint32_t *result) +{ + Error err; + + if (base_ptr_align == 0) { + *result = 0; + return ErrorNone; + } + + // figure out the largest alignment possible + if ((err = type_resolve(ira->codegen, elem_type, ResolveStatusSizeKnown))) + return err; + + uint64_t elem_size = type_size(ira->codegen, elem_type); + uint64_t abi_align = get_abi_alignment(ira->codegen, elem_type); + uint64_t ptr_align = base_ptr_align; + + uint64_t chosen_align = abi_align; + if (ptr_align >= abi_align) { + while (ptr_align > abi_align) { + if ((elem_index * elem_size) % ptr_align == 0) { + chosen_align = ptr_align; + break; + } + ptr_align >>= 1; + } + } else if (elem_size >= ptr_align && elem_size % ptr_align == 0) { + chosen_align = ptr_align; + } else { + // can't get here because guaranteed elem_size >= abi_align + zig_unreachable(); + } + + *result = chosen_align; + return ErrorNone; +} + +static IrInstGen *ir_analyze_instruction_elem_ptr(IrAnalyze *ira, IrInstSrcElemPtr *elem_ptr_instruction) { + Error err; + IrInstGen *array_ptr = elem_ptr_instruction->array_ptr->child; + if (type_is_invalid(array_ptr->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *elem_index = elem_ptr_instruction->elem_index->child; + if (type_is_invalid(elem_index->value->type)) + return ira->codegen->invalid_inst_gen; + + ZigValue *orig_array_ptr_val = array_ptr->value; + + ZigType *ptr_type = orig_array_ptr_val->type; + assert(ptr_type->id == ZigTypeIdPointer); + + ZigType *array_type = ptr_type->data.pointer.child_type; + + // At first return_type will be the pointer type we want to return, except with an optimistic alignment. + // We will adjust return_type's alignment before returning it. + ZigType *return_type; + + if (type_is_invalid(array_type)) + return ira->codegen->invalid_inst_gen; + + if (array_type->id == ZigTypeIdPointer && + array_type->data.pointer.ptr_len == PtrLenSingle && + array_type->data.pointer.child_type->id == ZigTypeIdArray) + { + IrInstGen *ptr_value = ir_get_deref(ira, &elem_ptr_instruction->base.base, + array_ptr, nullptr); + if (type_is_invalid(ptr_value->value->type)) + return ira->codegen->invalid_inst_gen; + + array_type = array_type->data.pointer.child_type; + ptr_type = ptr_type->data.pointer.child_type; + + orig_array_ptr_val = ptr_value->value; + } + + if (array_type->id == ZigTypeIdArray) { + if(array_type->data.array.len == 0 && array_type->data.array.sentinel == nullptr){ + ir_add_error(ira, &elem_ptr_instruction->base.base, buf_sprintf("accessing a zero length array is not allowed")); + return ira->codegen->invalid_inst_gen; + } + + ZigType *child_type = array_type->data.array.child_type; + if (ptr_type->data.pointer.host_int_bytes == 0) { + return_type = get_pointer_to_type_extra(ira->codegen, child_type, + ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, + elem_ptr_instruction->ptr_len, + ptr_type->data.pointer.explicit_alignment, 0, 0, false); + } else { + uint64_t elem_val_scalar; + if (!ir_resolve_usize(ira, elem_index, &elem_val_scalar)) + return ira->codegen->invalid_inst_gen; + + size_t bit_width = type_size_bits(ira->codegen, child_type); + size_t bit_offset = bit_width * elem_val_scalar; + + return_type = get_pointer_to_type_extra(ira->codegen, child_type, + ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, + elem_ptr_instruction->ptr_len, + 1, (uint32_t)bit_offset, ptr_type->data.pointer.host_int_bytes, false); + } + } else if (array_type->id == ZigTypeIdPointer) { + if (array_type->data.pointer.ptr_len == PtrLenSingle) { + ir_add_error_node(ira, elem_ptr_instruction->base.base.source_node, + buf_sprintf("index of single-item pointer")); + return ira->codegen->invalid_inst_gen; + } + return_type = adjust_ptr_len(ira->codegen, array_type, elem_ptr_instruction->ptr_len); + } else if (is_slice(array_type)) { + return_type = adjust_ptr_len(ira->codegen, array_type->data.structure.fields[slice_ptr_index]->type_entry, + elem_ptr_instruction->ptr_len); + } else if (array_type->id == ZigTypeIdVector) { + // This depends on whether the element index is comptime, so it is computed later. + return_type = nullptr; + } else if (elem_ptr_instruction->init_array_type_source_node != nullptr && + array_type->id == ZigTypeIdStruct && + array_type->data.structure.resolve_status == ResolveStatusBeingInferred) + { + ZigType *usize = ira->codegen->builtin_types.entry_usize; + IrInstGen *casted_elem_index = ir_implicit_cast(ira, elem_index, usize); + if (type_is_invalid(casted_elem_index->value->type)) + return ira->codegen->invalid_inst_gen; + ir_assert(instr_is_comptime(casted_elem_index), &elem_ptr_instruction->base.base); + Buf *field_name = buf_alloc(); + bigint_append_buf(field_name, &casted_elem_index->value->data.x_bigint, 10); + return ir_analyze_inferred_field_ptr(ira, field_name, &elem_ptr_instruction->base.base, + array_ptr, array_type); + } else if (is_tuple(array_type)) { + uint64_t elem_index_scalar; + if (!ir_resolve_usize(ira, elem_index, &elem_index_scalar)) + return ira->codegen->invalid_inst_gen; + if (elem_index_scalar >= array_type->data.structure.src_field_count) { + ir_add_error(ira, &elem_ptr_instruction->base.base, buf_sprintf( + "field index %" ZIG_PRI_u64 " outside tuple '%s' which has %" PRIu32 " fields", + elem_index_scalar, buf_ptr(&array_type->name), + array_type->data.structure.src_field_count)); + return ira->codegen->invalid_inst_gen; + } + TypeStructField *field = array_type->data.structure.fields[elem_index_scalar]; + return ir_analyze_struct_field_ptr(ira, &elem_ptr_instruction->base.base, field, array_ptr, + array_type, false); + } else { + ir_add_error_node(ira, elem_ptr_instruction->base.base.source_node, + buf_sprintf("array access of non-array type '%s'", buf_ptr(&array_type->name))); + return ira->codegen->invalid_inst_gen; + } + + ZigType *usize = ira->codegen->builtin_types.entry_usize; + IrInstGen *casted_elem_index = ir_implicit_cast(ira, elem_index, usize); + if (type_is_invalid(casted_elem_index->value->type)) + return ira->codegen->invalid_inst_gen; + + bool safety_check_on = elem_ptr_instruction->safety_check_on; + if (instr_is_comptime(casted_elem_index)) { + ZigValue *index_val = ir_resolve_const(ira, casted_elem_index, UndefBad); + if (index_val == nullptr) + return ira->codegen->invalid_inst_gen; + uint64_t index = bigint_as_u64(&index_val->data.x_bigint); + + if (array_type->id == ZigTypeIdArray) { + uint64_t array_len = array_type->data.array.len + + (array_type->data.array.sentinel != nullptr); + if (index >= array_len) { + ir_add_error_node(ira, elem_ptr_instruction->base.base.source_node, + buf_sprintf("index %" ZIG_PRI_u64 " outside array of size %" ZIG_PRI_u64, + index, array_len)); + return ira->codegen->invalid_inst_gen; + } + safety_check_on = false; + } else if (array_type->id == ZigTypeIdVector) { + uint64_t vector_len = array_type->data.vector.len; + if (index >= vector_len) { + ir_add_error_node(ira, elem_ptr_instruction->base.base.source_node, + buf_sprintf("index %" ZIG_PRI_u64 " outside vector of size %" ZIG_PRI_u64, + index, vector_len)); + return ira->codegen->invalid_inst_gen; + } + safety_check_on = false; + } + + if (array_type->id == ZigTypeIdVector) { + ZigType *elem_type = array_type->data.vector.elem_type; + uint32_t host_vec_len = array_type->data.vector.len; + return_type = get_pointer_to_type_extra2(ira->codegen, elem_type, + ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, + elem_ptr_instruction->ptr_len, + get_ptr_align(ira->codegen, ptr_type), 0, host_vec_len, false, (uint32_t)index, + nullptr, nullptr); + } else if (return_type->data.pointer.explicit_alignment != 0) { + uint32_t chosen_align; + if ((err = compute_elem_align(ira, return_type->data.pointer.child_type, + return_type->data.pointer.explicit_alignment, index, &chosen_align))) + { + return ira->codegen->invalid_inst_gen; + } + return_type = adjust_ptr_align(ira->codegen, return_type, chosen_align); + } + + // TODO The `array_type->id == ZigTypeIdArray` exception here should not be an exception; + // the `orig_array_ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar` clause should be omitted completely. + // However there are bugs to fix before this improvement can be made. + if (orig_array_ptr_val->special != ConstValSpecialRuntime && + orig_array_ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr && + (orig_array_ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar || array_type->id == ZigTypeIdArray)) + { + if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, + elem_ptr_instruction->base.base.source_node, orig_array_ptr_val, UndefBad))) + { + return ira->codegen->invalid_inst_gen; + } + + ZigValue *array_ptr_val = const_ptr_pointee(ira, ira->codegen, orig_array_ptr_val, + elem_ptr_instruction->base.base.source_node); + if (array_ptr_val == nullptr) + return ira->codegen->invalid_inst_gen; + + if (array_ptr_val->special == ConstValSpecialUndef && + elem_ptr_instruction->init_array_type_source_node != nullptr) + { + if (array_type->id == ZigTypeIdArray || array_type->id == ZigTypeIdVector) { + array_ptr_val->data.x_array.special = ConstArraySpecialNone; + array_ptr_val->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(array_type->data.array.len); + array_ptr_val->special = ConstValSpecialStatic; + for (size_t i = 0; i < array_type->data.array.len; i += 1) { + ZigValue *elem_val = &array_ptr_val->data.x_array.data.s_none.elements[i]; + elem_val->special = ConstValSpecialUndef; + elem_val->type = array_type->data.array.child_type; + elem_val->parent.id = ConstParentIdArray; + elem_val->parent.data.p_array.array_val = array_ptr_val; + elem_val->parent.data.p_array.elem_index = i; + } + } else if (is_slice(array_type)) { + ir_assert(array_ptr->value->type->id == ZigTypeIdPointer, &elem_ptr_instruction->base.base); + ZigType *actual_array_type = array_ptr->value->type->data.pointer.child_type; + + if (type_is_invalid(actual_array_type)) + return ira->codegen->invalid_inst_gen; + if (actual_array_type->id != ZigTypeIdArray) { + ir_add_error_node(ira, elem_ptr_instruction->init_array_type_source_node, + buf_sprintf("array literal requires address-of operator to coerce to slice type '%s'", + buf_ptr(&actual_array_type->name))); + return ira->codegen->invalid_inst_gen; + } + + ZigValue *array_init_val = ira->codegen->pass1_arena->create(); + array_init_val->special = ConstValSpecialStatic; + array_init_val->type = actual_array_type; + array_init_val->data.x_array.special = ConstArraySpecialNone; + array_init_val->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(actual_array_type->data.array.len); + array_init_val->special = ConstValSpecialStatic; + for (size_t i = 0; i < actual_array_type->data.array.len; i += 1) { + ZigValue *elem_val = &array_init_val->data.x_array.data.s_none.elements[i]; + elem_val->special = ConstValSpecialUndef; + elem_val->type = actual_array_type->data.array.child_type; + elem_val->parent.id = ConstParentIdArray; + elem_val->parent.data.p_array.array_val = array_init_val; + elem_val->parent.data.p_array.elem_index = i; + } + + init_const_slice(ira->codegen, array_ptr_val, array_init_val, 0, actual_array_type->data.array.len, + false); + array_ptr_val->data.x_struct.fields[slice_ptr_index]->data.x_ptr.mut = ConstPtrMutInfer; + } else { + ir_add_error_node(ira, elem_ptr_instruction->init_array_type_source_node, + buf_sprintf("expected array type or [_], found '%s'", + buf_ptr(&array_type->name))); + return ira->codegen->invalid_inst_gen; + } + } + + if (array_ptr_val->special != ConstValSpecialRuntime && + (array_type->id != ZigTypeIdPointer || + array_ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr)) + { + if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, + elem_ptr_instruction->base.base.source_node, array_ptr_val, UndefOk))) + { + return ira->codegen->invalid_inst_gen; + } + if (array_type->id == ZigTypeIdPointer) { + IrInstGen *result = ir_const(ira, &elem_ptr_instruction->base.base, return_type); + ZigValue *out_val = result->value; + out_val->data.x_ptr.mut = array_ptr_val->data.x_ptr.mut; + size_t new_index; + size_t mem_size; + size_t old_size; + switch (array_ptr_val->data.x_ptr.special) { + case ConstPtrSpecialInvalid: + case ConstPtrSpecialDiscard: + zig_unreachable(); + case ConstPtrSpecialRef: + if (array_ptr_val->data.x_ptr.data.ref.pointee->type->id == ZigTypeIdArray) { + ZigValue *array_val = array_ptr_val->data.x_ptr.data.ref.pointee; + new_index = index; + ZigType *array_type = array_val->type; + mem_size = array_type->data.array.len; + if (array_type->data.array.sentinel != nullptr) { + mem_size += 1; + } + old_size = mem_size; + + out_val->data.x_ptr.special = ConstPtrSpecialBaseArray; + out_val->data.x_ptr.data.base_array.array_val = array_val; + out_val->data.x_ptr.data.base_array.elem_index = new_index; + } else { + mem_size = 1; + old_size = 1; + new_index = index; + + out_val->data.x_ptr.special = ConstPtrSpecialRef; + out_val->data.x_ptr.data.ref.pointee = array_ptr_val->data.x_ptr.data.ref.pointee; + } + break; + case ConstPtrSpecialBaseArray: + case ConstPtrSpecialSubArray: + { + size_t offset = array_ptr_val->data.x_ptr.data.base_array.elem_index; + new_index = offset + index; + ZigType *array_type = array_ptr_val->data.x_ptr.data.base_array.array_val->type; + mem_size = array_type->data.array.len; + if (array_type->data.array.sentinel != nullptr) { + mem_size += 1; + } + old_size = mem_size - offset; + + assert(array_ptr_val->data.x_ptr.data.base_array.array_val); + + out_val->data.x_ptr.special = ConstPtrSpecialBaseArray; + out_val->data.x_ptr.data.base_array.array_val = + array_ptr_val->data.x_ptr.data.base_array.array_val; + out_val->data.x_ptr.data.base_array.elem_index = new_index; + + break; + } + case ConstPtrSpecialBaseStruct: + zig_panic("TODO elem ptr on a const inner struct"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO elem ptr on a const inner error union code"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO elem ptr on a const inner error union payload"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO elem ptr on a const inner optional payload"); + case ConstPtrSpecialHardCodedAddr: + zig_unreachable(); + case ConstPtrSpecialFunction: + zig_panic("TODO element ptr of a function casted to a ptr"); + case ConstPtrSpecialNull: + zig_panic("TODO elem ptr on a null pointer"); + } + if (new_index >= mem_size) { + ir_add_error_node(ira, elem_ptr_instruction->base.base.source_node, + buf_sprintf("index %" ZIG_PRI_u64 " outside pointer of size %" ZIG_PRI_usize "", index, old_size)); + return ira->codegen->invalid_inst_gen; + } + return result; + } else if (is_slice(array_type)) { + ZigValue *ptr_field = array_ptr_val->data.x_struct.fields[slice_ptr_index]; + ir_assert(ptr_field != nullptr, &elem_ptr_instruction->base.base); + if (ptr_field->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) { + return ir_build_elem_ptr_gen(ira, elem_ptr_instruction->base.base.scope, + elem_ptr_instruction->base.base.source_node, array_ptr, casted_elem_index, false, + return_type); + } + ZigValue *len_field = array_ptr_val->data.x_struct.fields[slice_len_index]; + IrInstGen *result = ir_const(ira, &elem_ptr_instruction->base.base, return_type); + ZigValue *out_val = result->value; + ZigType *slice_ptr_type = array_type->data.structure.fields[slice_ptr_index]->type_entry; + uint64_t slice_len = bigint_as_u64(&len_field->data.x_bigint); + uint64_t full_slice_len = slice_len + + ((slice_ptr_type->data.pointer.sentinel != nullptr) ? 1 : 0); + if (index >= full_slice_len) { + ir_add_error_node(ira, elem_ptr_instruction->base.base.source_node, + buf_sprintf("index %" ZIG_PRI_u64 " outside slice of size %" ZIG_PRI_u64, + index, slice_len)); + return ira->codegen->invalid_inst_gen; + } + out_val->data.x_ptr.mut = ptr_field->data.x_ptr.mut; + switch (ptr_field->data.x_ptr.special) { + case ConstPtrSpecialInvalid: + case ConstPtrSpecialDiscard: + zig_unreachable(); + case ConstPtrSpecialRef: + out_val->data.x_ptr.special = ConstPtrSpecialRef; + out_val->data.x_ptr.data.ref.pointee = ptr_field->data.x_ptr.data.ref.pointee; + break; + case ConstPtrSpecialSubArray: + case ConstPtrSpecialBaseArray: + { + size_t offset = ptr_field->data.x_ptr.data.base_array.elem_index; + uint64_t new_index = offset + index; + if (ptr_field->data.x_ptr.data.base_array.array_val->data.x_array.special != + ConstArraySpecialBuf) + { + ir_assert(new_index < + ptr_field->data.x_ptr.data.base_array.array_val->type->data.array.len, + &elem_ptr_instruction->base.base); + } + out_val->data.x_ptr.special = ConstPtrSpecialBaseArray; + out_val->data.x_ptr.data.base_array.array_val = + ptr_field->data.x_ptr.data.base_array.array_val; + out_val->data.x_ptr.data.base_array.elem_index = new_index; + break; + } + case ConstPtrSpecialBaseStruct: + zig_panic("TODO elem ptr on a slice backed by const inner struct"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO elem ptr on a slice backed by const inner error union code"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO elem ptr on a slice backed by const inner error union payload"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO elem ptr on a slice backed by const optional payload"); + case ConstPtrSpecialHardCodedAddr: + zig_unreachable(); + case ConstPtrSpecialFunction: + zig_panic("TODO elem ptr on a slice that was ptrcast from a function"); + case ConstPtrSpecialNull: + zig_panic("TODO elem ptr on a slice has a null pointer"); + } + return result; + } else if (array_type->id == ZigTypeIdArray || array_type->id == ZigTypeIdVector) { + IrInstGen *result; + if (orig_array_ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { + result = ir_build_elem_ptr_gen(ira, elem_ptr_instruction->base.base.scope, + elem_ptr_instruction->base.base.source_node, array_ptr, casted_elem_index, + false, return_type); + result->value->special = ConstValSpecialStatic; + } else { + result = ir_const(ira, &elem_ptr_instruction->base.base, return_type); + } + ZigValue *out_val = result->value; + out_val->data.x_ptr.special = ConstPtrSpecialBaseArray; + out_val->data.x_ptr.mut = orig_array_ptr_val->data.x_ptr.mut; + out_val->data.x_ptr.data.base_array.array_val = array_ptr_val; + out_val->data.x_ptr.data.base_array.elem_index = index; + return result; + } else { + zig_unreachable(); + } + } + } + } else if (array_type->id == ZigTypeIdVector) { + // runtime known element index + ZigType *elem_type = array_type->data.vector.elem_type; + uint32_t host_vec_len = array_type->data.vector.len; + return_type = get_pointer_to_type_extra2(ira->codegen, elem_type, + ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, + elem_ptr_instruction->ptr_len, + get_ptr_align(ira->codegen, ptr_type), 0, host_vec_len, false, VECTOR_INDEX_RUNTIME, + nullptr, nullptr); + } else { + // runtime known element index + switch (type_requires_comptime(ira->codegen, return_type)) { + case ReqCompTimeYes: + ir_add_error(ira, &elem_index->base, + buf_sprintf("values of type '%s' must be comptime known, but index value is runtime known", + buf_ptr(&return_type->data.pointer.child_type->name))); + return ira->codegen->invalid_inst_gen; + case ReqCompTimeInvalid: + return ira->codegen->invalid_inst_gen; + case ReqCompTimeNo: + break; + } + + if (return_type->data.pointer.explicit_alignment != 0) { + if ((err = type_resolve(ira->codegen, return_type->data.pointer.child_type, ResolveStatusSizeKnown))) + return ira->codegen->invalid_inst_gen; + + uint64_t elem_size = type_size(ira->codegen, return_type->data.pointer.child_type); + uint64_t abi_align = get_abi_alignment(ira->codegen, return_type->data.pointer.child_type); + uint64_t ptr_align = get_ptr_align(ira->codegen, return_type); + if (ptr_align < abi_align) { + if (elem_size >= ptr_align && elem_size % ptr_align == 0) { + return_type = adjust_ptr_align(ira->codegen, return_type, ptr_align); + } else { + // can't get here because guaranteed elem_size >= abi_align + zig_unreachable(); + } + } else { + return_type = adjust_ptr_align(ira->codegen, return_type, abi_align); + } + } + } + + return ir_build_elem_ptr_gen(ira, elem_ptr_instruction->base.base.scope, + elem_ptr_instruction->base.base.source_node, array_ptr, casted_elem_index, safety_check_on, return_type); +} + +static IrInstGen *ir_analyze_container_member_access_inner(IrAnalyze *ira, + ZigType *bare_struct_type, Buf *field_name, IrInst* source_instr, + IrInstGen *container_ptr, IrInst *container_ptr_src, ZigType *container_type) +{ + if (!is_slice(bare_struct_type)) { + ScopeDecls *container_scope = get_container_scope(bare_struct_type); + assert(container_scope != nullptr); + auto tld = find_container_decl(ira->codegen, container_scope, field_name); + if (tld) { + if (tld->id == TldIdFn) { + resolve_top_level_decl(ira->codegen, tld, source_instr->source_node, false); + if (tld->resolution == TldResolutionInvalid) + return ira->codegen->invalid_inst_gen; + if (tld->resolution == TldResolutionResolving) + return ir_error_dependency_loop(ira, source_instr); + + if (tld->visib_mod == VisibModPrivate && + tld->import != get_scope_import(source_instr->scope)) + { + ErrorMsg *msg = ir_add_error(ira, source_instr, + buf_sprintf("'%s' is private", buf_ptr(field_name))); + add_error_note(ira->codegen, msg, tld->source_node, buf_sprintf("declared here")); + return ira->codegen->invalid_inst_gen; + } + + TldFn *tld_fn = (TldFn *)tld; + ZigFn *fn_entry = tld_fn->fn_entry; + assert(fn_entry != nullptr); + + if (type_is_invalid(fn_entry->type_entry)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *bound_fn_value = ir_const_bound_fn(ira, source_instr, fn_entry, container_ptr, + container_ptr_src); + return ir_get_ref(ira, source_instr, bound_fn_value, true, false); + } else if (tld->id == TldIdVar) { + resolve_top_level_decl(ira->codegen, tld, source_instr->source_node, false); + if (tld->resolution == TldResolutionInvalid) + return ira->codegen->invalid_inst_gen; + if (tld->resolution == TldResolutionResolving) + return ir_error_dependency_loop(ira, source_instr); + + TldVar *tld_var = (TldVar *)tld; + ZigVar *var = tld_var->var; + assert(var != nullptr); + + if (type_is_invalid(var->var_type)) + return ira->codegen->invalid_inst_gen; + + if (var->const_value->type->id == ZigTypeIdFn) { + ir_assert(var->const_value->data.x_ptr.special == ConstPtrSpecialFunction, source_instr); + ZigFn *fn = var->const_value->data.x_ptr.data.fn.fn_entry; + IrInstGen *bound_fn_value = ir_const_bound_fn(ira, source_instr, fn, container_ptr, + container_ptr_src); + return ir_get_ref(ira, source_instr, bound_fn_value, true, false); + } + } + } + } + const char *prefix_name; + if (is_slice(bare_struct_type)) { + prefix_name = ""; + } else if (bare_struct_type->id == ZigTypeIdStruct) { + prefix_name = "struct "; + } else if (bare_struct_type->id == ZigTypeIdEnum) { + prefix_name = "enum "; + } else if (bare_struct_type->id == ZigTypeIdUnion) { + prefix_name = "union "; + } else { + prefix_name = ""; + } + ir_add_error_node(ira, source_instr->source_node, + buf_sprintf("no member named '%s' in %s'%s'", buf_ptr(field_name), prefix_name, buf_ptr(&bare_struct_type->name))); + return ira->codegen->invalid_inst_gen; +} + +static void memoize_field_init_val(CodeGen *codegen, ZigType *container_type, TypeStructField *field) { + if (field->init_val != nullptr) return; + if (field->decl_node == nullptr) return; + if (field->decl_node->type != NodeTypeStructField) return; + AstNode *init_node = field->decl_node->data.struct_field.value; + if (init_node == nullptr) return; + // scope is not the scope of the struct init, it's the scope of the struct type decl + Scope *analyze_scope = &get_container_scope(container_type)->base; + // memoize it + field->init_val = analyze_const_value(codegen, analyze_scope, init_node, + field->type_entry, nullptr, UndefOk); +} + +static IrInstGen *ir_analyze_struct_field_ptr(IrAnalyze *ira, IrInst* source_instr, + TypeStructField *field, IrInstGen *struct_ptr, ZigType *struct_type, bool initializing) +{ + Error err; + ZigType *field_type = resolve_struct_field_type(ira->codegen, field); + if (field_type == nullptr) + return ira->codegen->invalid_inst_gen; + if (field->is_comptime) { + IrInstGen *elem = ir_const(ira, source_instr, field_type); + memoize_field_init_val(ira->codegen, struct_type, field); + if(field->init_val != nullptr && type_is_invalid(field->init_val->type)){ + return ira->codegen->invalid_inst_gen; + } + copy_const_val(ira->codegen, elem->value, field->init_val); + return ir_get_ref2(ira, source_instr, elem, field_type, true, false); + } + switch (type_has_one_possible_value(ira->codegen, field_type)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_inst_gen; + case OnePossibleValueYes: { + IrInstGen *elem = ir_const_move(ira, source_instr, + get_the_one_possible_value(ira->codegen, field_type)); + return ir_get_ref(ira, source_instr, elem, + struct_ptr->value->type->data.pointer.is_const, + struct_ptr->value->type->data.pointer.is_volatile); + } + case OnePossibleValueNo: + break; + } + bool is_const = struct_ptr->value->type->data.pointer.is_const; + bool is_volatile = struct_ptr->value->type->data.pointer.is_volatile; + ZigType *ptr_type; + if (is_anon_container(struct_type)) { + ptr_type = get_pointer_to_type_extra(ira->codegen, field_type, + is_const, is_volatile, PtrLenSingle, 0, 0, 0, false); + } else { + ResolveStatus needed_resolve_status = + (struct_type->data.structure.layout == ContainerLayoutAuto) ? + ResolveStatusZeroBitsKnown : ResolveStatusSizeKnown; + if ((err = type_resolve(ira->codegen, struct_type, needed_resolve_status))) + return ira->codegen->invalid_inst_gen; + assert(struct_ptr->value->type->id == ZigTypeIdPointer); + uint32_t ptr_bit_offset = struct_ptr->value->type->data.pointer.bit_offset_in_host; + uint32_t ptr_host_int_bytes = struct_ptr->value->type->data.pointer.host_int_bytes; + uint32_t host_int_bytes_for_result_type = (ptr_host_int_bytes == 0) ? + get_host_int_bytes(ira->codegen, struct_type, field) : ptr_host_int_bytes; + ptr_type = get_pointer_to_type_extra(ira->codegen, field_type, + is_const, is_volatile, PtrLenSingle, field->align, + (uint32_t)(ptr_bit_offset + field->bit_offset_in_host), + (uint32_t)host_int_bytes_for_result_type, false); + } + if (instr_is_comptime(struct_ptr)) { + ZigValue *ptr_val = ir_resolve_const(ira, struct_ptr, UndefBad); + if (!ptr_val) + return ira->codegen->invalid_inst_gen; + + if (ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { + ZigValue *struct_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node); + if (struct_val == nullptr) + return ira->codegen->invalid_inst_gen; + if (type_is_invalid(struct_val->type)) + return ira->codegen->invalid_inst_gen; + + // This to allow lazy values to be resolved. + if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, + source_instr->source_node, struct_val, UndefOk))) + { + return ira->codegen->invalid_inst_gen; + } + if (initializing && struct_val->special == ConstValSpecialUndef) { + struct_val->data.x_struct.fields = alloc_const_vals_ptrs(ira->codegen, struct_type->data.structure.src_field_count); + struct_val->special = ConstValSpecialStatic; + for (size_t i = 0; i < struct_type->data.structure.src_field_count; i += 1) { + ZigValue *field_val = struct_val->data.x_struct.fields[i]; + field_val->special = ConstValSpecialUndef; + field_val->type = resolve_struct_field_type(ira->codegen, + struct_type->data.structure.fields[i]); + field_val->parent.id = ConstParentIdStruct; + field_val->parent.data.p_struct.struct_val = struct_val; + field_val->parent.data.p_struct.field_index = i; + } + } + IrInstGen *result; + if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { + result = ir_build_struct_field_ptr(ira, source_instr, struct_ptr, field, ptr_type); + result->value->special = ConstValSpecialStatic; + } else { + result = ir_const(ira, source_instr, ptr_type); + } + ZigValue *const_val = result->value; + const_val->data.x_ptr.special = ConstPtrSpecialBaseStruct; + const_val->data.x_ptr.mut = ptr_val->data.x_ptr.mut; + const_val->data.x_ptr.data.base_struct.struct_val = struct_val; + const_val->data.x_ptr.data.base_struct.field_index = field->src_index; + return result; + } + } + return ir_build_struct_field_ptr(ira, source_instr, struct_ptr, field, ptr_type); +} + +static IrInstGen *ir_analyze_inferred_field_ptr(IrAnalyze *ira, Buf *field_name, + IrInst* source_instr, IrInstGen *container_ptr, ZigType *container_type) +{ + // The type of the field is not available until a store using this pointer happens. + // So, here we create a special pointer type which has the inferred struct type and + // field name encoded in the type. Later, when there is a store via this pointer, + // the field type will then be available, and the field will be added to the inferred + // struct. + + ZigType *container_ptr_type = container_ptr->value->type; + ir_assert(container_ptr_type->id == ZigTypeIdPointer, source_instr); + + InferredStructField *inferred_struct_field = heap::c_allocator.create(); + inferred_struct_field->inferred_struct_type = container_type; + inferred_struct_field->field_name = field_name; + + ZigType *elem_type = ira->codegen->builtin_types.entry_anytype; + ZigType *field_ptr_type = get_pointer_to_type_extra2(ira->codegen, elem_type, + container_ptr_type->data.pointer.is_const, container_ptr_type->data.pointer.is_volatile, + PtrLenSingle, 0, 0, 0, false, VECTOR_INDEX_NONE, inferred_struct_field, nullptr); + + if (instr_is_comptime(container_ptr)) { + ZigValue *ptr_val = ir_resolve_const(ira, container_ptr, UndefBad); + if (ptr_val == nullptr) + return ira->codegen->invalid_inst_gen; + + IrInstGen *result; + if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { + result = ir_build_cast(ira, source_instr, container_ptr_type, container_ptr, CastOpNoop); + } else { + result = ir_const(ira, source_instr, field_ptr_type); + } + copy_const_val(ira->codegen, result->value, ptr_val); + result->value->type = field_ptr_type; + return result; + } + + return ir_build_cast(ira, source_instr, field_ptr_type, container_ptr, CastOpNoop); +} + +static IrInstGen *ir_analyze_container_field_ptr(IrAnalyze *ira, Buf *field_name, + IrInst* source_instr, IrInstGen *container_ptr, IrInst *container_ptr_src, + ZigType *container_type, bool initializing) +{ + Error err; + + ZigType *bare_type = container_ref_type(container_type); + + if (initializing && bare_type->id == ZigTypeIdStruct && + bare_type->data.structure.resolve_status == ResolveStatusBeingInferred) + { + return ir_analyze_inferred_field_ptr(ira, field_name, source_instr, container_ptr, bare_type); + } + + // Tracks wether we should return an undefined value of the correct type. + // We do this if the container pointer is undefined and we are in a TypeOf call. + bool return_undef = container_ptr->value->special == ConstValSpecialUndef && \ + get_scope_typeof(source_instr->scope) != nullptr; + + if ((err = type_resolve(ira->codegen, bare_type, ResolveStatusZeroBitsKnown))) + return ira->codegen->invalid_inst_gen; + + assert(container_ptr->value->type->id == ZigTypeIdPointer); + if (bare_type->id == ZigTypeIdStruct) { + TypeStructField *field = find_struct_type_field(bare_type, field_name); + if (field != nullptr) { + if (return_undef) { + ZigType *field_ptr_type = get_pointer_to_type(ira->codegen, resolve_struct_field_type(ira->codegen, field), + container_ptr->value->type->data.pointer.is_const); + return ir_const_undef(ira, source_instr, field_ptr_type); + } + + return ir_analyze_struct_field_ptr(ira, source_instr, field, container_ptr, bare_type, initializing); + } else { + return ir_analyze_container_member_access_inner(ira, bare_type, field_name, + source_instr, container_ptr, container_ptr_src, container_type); + } + } + + if (bare_type->id == ZigTypeIdEnum) { + return ir_analyze_container_member_access_inner(ira, bare_type, field_name, + source_instr, container_ptr, container_ptr_src, container_type); + } + + if (bare_type->id == ZigTypeIdUnion) { + bool is_const = container_ptr->value->type->data.pointer.is_const; + bool is_volatile = container_ptr->value->type->data.pointer.is_volatile; + + TypeUnionField *field = find_union_type_field(bare_type, field_name); + if (field == nullptr) { + return ir_analyze_container_member_access_inner(ira, bare_type, field_name, + source_instr, container_ptr, container_ptr_src, container_type); + } + + ZigType *field_type = resolve_union_field_type(ira->codegen, field); + if (field_type == nullptr) + return ira->codegen->invalid_inst_gen; + + ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, field_type, + is_const, is_volatile, PtrLenSingle, 0, 0, 0, false); + if (instr_is_comptime(container_ptr)) { + ZigValue *ptr_val = ir_resolve_const(ira, container_ptr, UndefBad); + if (!ptr_val) + return ira->codegen->invalid_inst_gen; + + if (ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar && + ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { + ZigValue *union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node); + if (union_val == nullptr) + return ira->codegen->invalid_inst_gen; + if (type_is_invalid(union_val->type)) + return ira->codegen->invalid_inst_gen; + + if (initializing) { + ZigValue *payload_val = ira->codegen->pass1_arena->create(); + payload_val->special = ConstValSpecialUndef; + payload_val->type = field_type; + payload_val->parent.id = ConstParentIdUnion; + payload_val->parent.data.p_union.union_val = union_val; + + union_val->special = ConstValSpecialStatic; + bigint_init_bigint(&union_val->data.x_union.tag, &field->enum_field->value); + union_val->data.x_union.payload = payload_val; + } else if (bare_type->data.unionation.layout != ContainerLayoutExtern) { + TypeUnionField *actual_field = find_union_field_by_tag(bare_type, &union_val->data.x_union.tag); + if (actual_field == nullptr) + zig_unreachable(); + + if (field != actual_field) { + ir_add_error_node(ira, source_instr->source_node, + buf_sprintf("accessing union field '%s' while field '%s' is set", buf_ptr(field_name), + buf_ptr(actual_field->name))); + return ira->codegen->invalid_inst_gen; + } + } + + ZigValue *payload_val = union_val->data.x_union.payload; + + IrInstGen *result; + if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { + result = ir_build_union_field_ptr(ira, source_instr, container_ptr, field, true, + initializing, ptr_type); + result->value->special = ConstValSpecialStatic; + } else { + result = ir_const(ira, source_instr, ptr_type); + } + ZigValue *const_val = result->value; + const_val->data.x_ptr.special = ConstPtrSpecialRef; + const_val->data.x_ptr.mut = container_ptr->value->data.x_ptr.mut; + const_val->data.x_ptr.data.ref.pointee = payload_val; + return result; + } + } + + return ir_build_union_field_ptr(ira, source_instr, container_ptr, field, true, initializing, ptr_type); + } + + zig_unreachable(); +} + +static void add_link_lib_symbol(IrAnalyze *ira, Buf *lib_name, Buf *symbol_name, AstNode *source_node) { + const char *msg = stage2_add_link_lib(&ira->codegen->stage1, buf_ptr(lib_name), buf_len(lib_name), + buf_ptr(symbol_name), buf_len(symbol_name)); + if (msg != nullptr) { + ir_add_error_node(ira, source_node, buf_create_from_str(msg)); + ira->codegen->reported_bad_link_libc_error = true; + } +} + +static IrInstGen *ir_error_dependency_loop(IrAnalyze *ira, IrInst* source_instr) { + ir_add_error(ira, source_instr, buf_sprintf("dependency loop detected")); + return ira->codegen->invalid_inst_gen; +} + +static IrInstGen *ir_analyze_decl_ref(IrAnalyze *ira, IrInst* source_instruction, Tld *tld) { + resolve_top_level_decl(ira->codegen, tld, source_instruction->source_node, true); + if (tld->resolution == TldResolutionInvalid) { + return ira->codegen->invalid_inst_gen; + } + if (tld->resolution == TldResolutionResolving) + return ir_error_dependency_loop(ira, source_instruction); + + switch (tld->id) { + case TldIdContainer: + case TldIdCompTime: + case TldIdUsingNamespace: + zig_unreachable(); + case TldIdVar: { + TldVar *tld_var = (TldVar *)tld; + ZigVar *var = tld_var->var; + assert(var != nullptr); + + if (tld_var->extern_lib_name != nullptr) { + add_link_lib_symbol(ira, tld_var->extern_lib_name, buf_create_from_str(var->name), + source_instruction->source_node); + } + + return ir_get_var_ptr(ira, source_instruction, var); + } + case TldIdFn: { + TldFn *tld_fn = (TldFn *)tld; + ZigFn *fn_entry = tld_fn->fn_entry; + assert(fn_entry->type_entry != nullptr); + + if (type_is_invalid(fn_entry->type_entry)) + return ira->codegen->invalid_inst_gen; + + if (tld_fn->extern_lib_name != nullptr) { + add_link_lib_symbol(ira, tld_fn->extern_lib_name, &fn_entry->symbol_name, source_instruction->source_node); + } + + IrInstGen *fn_inst = ir_const_fn(ira, source_instruction, fn_entry); + return ir_get_ref(ira, source_instruction, fn_inst, true, false); + } + } + zig_unreachable(); +} + +static ErrorTableEntry *find_err_table_entry(ZigType *err_set_type, Buf *field_name) { + assert(err_set_type->id == ZigTypeIdErrorSet); + for (uint32_t i = 0; i < err_set_type->data.error_set.err_count; i += 1) { + ErrorTableEntry *err_table_entry = err_set_type->data.error_set.errors[i]; + if (buf_eql_buf(&err_table_entry->name, field_name)) { + return err_table_entry; + } + } + return nullptr; +} + +static IrInstGen *ir_analyze_instruction_field_ptr(IrAnalyze *ira, IrInstSrcFieldPtr *field_ptr_instruction) { + Error err; + IrInstGen *container_ptr = field_ptr_instruction->container_ptr->child; + if (type_is_invalid(container_ptr->value->type)) + return ira->codegen->invalid_inst_gen; + + ZigType *container_type = container_ptr->value->type->data.pointer.child_type; + + Buf *field_name = field_ptr_instruction->field_name_buffer; + if (!field_name) { + IrInstGen *field_name_expr = field_ptr_instruction->field_name_expr->child; + field_name = ir_resolve_str(ira, field_name_expr); + if (!field_name) + return ira->codegen->invalid_inst_gen; + } + + + AstNode *source_node = field_ptr_instruction->base.base.source_node; + + if (type_is_invalid(container_type)) { + return ira->codegen->invalid_inst_gen; + } else if (is_tuple(container_type) && !field_ptr_instruction->initializing && buf_eql_str(field_name, "len")) { + IrInstGen *len_inst = ir_const_unsigned(ira, &field_ptr_instruction->base.base, + container_type->data.structure.src_field_count); + return ir_get_ref(ira, &field_ptr_instruction->base.base, len_inst, true, false); + } else if (is_slice(container_type) || is_container_ref(container_type)) { + assert(container_ptr->value->type->id == ZigTypeIdPointer); + if (container_type->id == ZigTypeIdPointer) { + ZigType *bare_type = container_ref_type(container_type); + IrInstGen *container_child = ir_get_deref(ira, &field_ptr_instruction->base.base, container_ptr, nullptr); + IrInstGen *result = ir_analyze_container_field_ptr(ira, field_name, &field_ptr_instruction->base.base, + container_child, &field_ptr_instruction->container_ptr->base, bare_type, + field_ptr_instruction->initializing); + return result; + } else { + IrInstGen *result = ir_analyze_container_field_ptr(ira, field_name, &field_ptr_instruction->base.base, + container_ptr, &field_ptr_instruction->container_ptr->base, container_type, + field_ptr_instruction->initializing); + return result; + } + } else if (is_array_ref(container_type) && !field_ptr_instruction->initializing) { + if (buf_eql_str(field_name, "len")) { + ZigValue *len_val = ira->codegen->pass1_arena->create(); + if (container_type->id == ZigTypeIdPointer) { + init_const_usize(ira->codegen, len_val, container_type->data.pointer.child_type->data.array.len); + } else { + init_const_usize(ira->codegen, len_val, container_type->data.array.len); + } + + ZigType *usize = ira->codegen->builtin_types.entry_usize; + bool ptr_is_const = true; + bool ptr_is_volatile = false; + return ir_get_const_ptr(ira, &field_ptr_instruction->base.base, len_val, + usize, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0); + } else { + ir_add_error_node(ira, source_node, + buf_sprintf("no field named '%s' in '%s'", buf_ptr(field_name), + buf_ptr(&container_type->name))); + return ira->codegen->invalid_inst_gen; + } + } else if (container_type->id == ZigTypeIdMetaType) { + ZigValue *container_ptr_val = ir_resolve_const(ira, container_ptr, UndefBad); + if (!container_ptr_val) + return ira->codegen->invalid_inst_gen; + + assert(container_ptr->value->type->id == ZigTypeIdPointer); + ZigValue *child_val = const_ptr_pointee(ira, ira->codegen, container_ptr_val, source_node); + if (child_val == nullptr) + return ira->codegen->invalid_inst_gen; + if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, + field_ptr_instruction->base.base.source_node, child_val, UndefBad))) + { + return ira->codegen->invalid_inst_gen; + } + ZigType *child_type = child_val->data.x_type; + + if (type_is_invalid(child_type)) { + return ira->codegen->invalid_inst_gen; + } else if (is_container(child_type)) { + if (child_type->id == ZigTypeIdEnum) { + if ((err = type_resolve(ira->codegen, child_type, ResolveStatusSizeKnown))) + return ira->codegen->invalid_inst_gen; + + TypeEnumField *field = find_enum_type_field(child_type, field_name); + if (field) { + bool ptr_is_const = true; + bool ptr_is_volatile = false; + return ir_get_const_ptr(ira, &field_ptr_instruction->base.base, + create_const_enum(ira->codegen, child_type, &field->value), child_type, + ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0); + } + } + ScopeDecls *container_scope = get_container_scope(child_type); + Tld *tld = find_container_decl(ira->codegen, container_scope, field_name); + if (tld) { + if (tld->visib_mod == VisibModPrivate && + tld->import != get_scope_import(field_ptr_instruction->base.base.scope)) + { + ErrorMsg *msg = ir_add_error(ira, &field_ptr_instruction->base.base, + buf_sprintf("'%s' is private", buf_ptr(field_name))); + add_error_note(ira->codegen, msg, tld->source_node, buf_sprintf("declared here")); + return ira->codegen->invalid_inst_gen; + } + return ir_analyze_decl_ref(ira, &field_ptr_instruction->base.base, tld); + } + if (child_type->id == ZigTypeIdUnion && + (child_type->data.unionation.decl_node->data.container_decl.init_arg_expr != nullptr || + child_type->data.unionation.decl_node->data.container_decl.auto_enum)) + { + if ((err = type_resolve(ira->codegen, child_type, ResolveStatusSizeKnown))) + return ira->codegen->invalid_inst_gen; + TypeUnionField *field = find_union_type_field(child_type, field_name); + if (field) { + ZigType *enum_type = child_type->data.unionation.tag_type; + bool ptr_is_const = true; + bool ptr_is_volatile = false; + return ir_get_const_ptr(ira, &field_ptr_instruction->base.base, + create_const_enum(ira->codegen, enum_type, &field->enum_field->value), enum_type, + ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0); + } + } + const char *container_name = (child_type == ira->codegen->root_import) ? + "root source file" : buf_ptr(buf_sprintf("container '%s'", buf_ptr(&child_type->name))); + ir_add_error(ira, &field_ptr_instruction->base.base, + buf_sprintf("%s has no member called '%s'", + container_name, buf_ptr(field_name))); + return ira->codegen->invalid_inst_gen; + } else if (child_type->id == ZigTypeIdErrorSet) { + ErrorTableEntry *err_entry; + ZigType *err_set_type; + if (type_is_global_error_set(child_type)) { + auto existing_entry = ira->codegen->error_table.maybe_get(field_name); + if (existing_entry) { + err_entry = existing_entry->value; + } else { + err_entry = heap::c_allocator.create(); + err_entry->decl_node = field_ptr_instruction->base.base.source_node; + buf_init_from_buf(&err_entry->name, field_name); + size_t error_value_count = ira->codegen->errors_by_index.length; + assert((uint32_t)error_value_count < (((uint32_t)1) << (uint32_t)ira->codegen->err_tag_type->data.integral.bit_count)); + err_entry->value = error_value_count; + ira->codegen->errors_by_index.append(err_entry); + ira->codegen->error_table.put(field_name, err_entry); + } + if (err_entry->set_with_only_this_in_it == nullptr) { + err_entry->set_with_only_this_in_it = make_err_set_with_one_item(ira->codegen, + field_ptr_instruction->base.base.scope, field_ptr_instruction->base.base.source_node, + err_entry); + } + err_set_type = err_entry->set_with_only_this_in_it; + } else { + if (!resolve_inferred_error_set(ira->codegen, child_type, field_ptr_instruction->base.base.source_node)) { + return ira->codegen->invalid_inst_gen; + } + err_entry = find_err_table_entry(child_type, field_name); + if (err_entry == nullptr) { + ir_add_error(ira, &field_ptr_instruction->base.base, + buf_sprintf("no error named '%s' in '%s'", buf_ptr(field_name), buf_ptr(&child_type->name))); + return ira->codegen->invalid_inst_gen; + } + err_set_type = child_type; + } + ZigValue *const_val = ira->codegen->pass1_arena->create(); + const_val->special = ConstValSpecialStatic; + const_val->type = err_set_type; + const_val->data.x_err_set = err_entry; + + bool ptr_is_const = true; + bool ptr_is_volatile = false; + return ir_get_const_ptr(ira, &field_ptr_instruction->base.base, const_val, + err_set_type, ConstPtrMutComptimeConst, ptr_is_const, ptr_is_volatile, 0); + } else { + ir_add_error(ira, &field_ptr_instruction->base.base, + buf_sprintf("type '%s' does not support field access", buf_ptr(&container_type->name))); + return ira->codegen->invalid_inst_gen; + } + } else if (field_ptr_instruction->initializing) { + ir_add_error(ira, &field_ptr_instruction->base.base, + buf_sprintf("type '%s' does not support struct initialization syntax", buf_ptr(&container_type->name))); + return ira->codegen->invalid_inst_gen; + } else { + ir_add_error_node(ira, field_ptr_instruction->base.base.source_node, + buf_sprintf("type '%s' does not support field access", buf_ptr(&container_type->name))); + return ira->codegen->invalid_inst_gen; + } +} + +static IrInstGen *ir_analyze_instruction_store_ptr(IrAnalyze *ira, IrInstSrcStorePtr *instruction) { + IrInstGen *ptr = instruction->ptr->child; + if (type_is_invalid(ptr->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *value = instruction->value->child; + if (type_is_invalid(value->value->type)) + return ira->codegen->invalid_inst_gen; + + return ir_analyze_store_ptr(ira, &instruction->base.base, ptr, value, instruction->allow_write_through_const); +} + +static IrInstGen *ir_analyze_instruction_load_ptr(IrAnalyze *ira, IrInstSrcLoadPtr *instruction) { + IrInstGen *ptr = instruction->ptr->child; + if (type_is_invalid(ptr->value->type)) + return ira->codegen->invalid_inst_gen; + return ir_get_deref(ira, &instruction->base.base, ptr, nullptr); +} + +static IrInstGen *ir_analyze_instruction_typeof(IrAnalyze *ira, IrInstSrcTypeOf *typeof_instruction) { + ZigType *type_entry; + + const size_t value_count = typeof_instruction->value_count; + + // Fast path for the common case of TypeOf with a single argument + if (value_count < 2) { + type_entry = typeof_instruction->value.scalar->child->value->type; + } else { + IrInstGen **args = heap::c_allocator.allocate(value_count); + for (size_t i = 0; i < value_count; i += 1) { + IrInstGen *value = typeof_instruction->value.list[i]->child; + if (type_is_invalid(value->value->type)) + return ira->codegen->invalid_inst_gen; + args[i] = value; + } + + type_entry = ir_resolve_peer_types(ira, typeof_instruction->base.base.source_node, + nullptr, args, value_count); + + heap::c_allocator.deallocate(args, value_count); + } + + if (type_is_invalid(type_entry)) + return ira->codegen->invalid_inst_gen; + + return ir_const_type(ira, &typeof_instruction->base.base, type_entry); +} + +static IrInstGen *ir_analyze_instruction_set_cold(IrAnalyze *ira, IrInstSrcSetCold *instruction) { + if (ira->new_irb.exec->is_inline) { + // ignore setCold when running functions at compile time + return ir_const_void(ira, &instruction->base.base); + } + + IrInstGen *is_cold_value = instruction->is_cold->child; + bool want_cold; + if (!ir_resolve_bool(ira, is_cold_value, &want_cold)) + return ira->codegen->invalid_inst_gen; + + ZigFn *fn_entry = scope_fn_entry(instruction->base.base.scope); + if (fn_entry == nullptr) { + ir_add_error(ira, &instruction->base.base, buf_sprintf("@setCold outside function")); + return ira->codegen->invalid_inst_gen; + } + + if (fn_entry->set_cold_node != nullptr) { + ErrorMsg *msg = ir_add_error(ira, &instruction->base.base, buf_sprintf("cold set twice in same function")); + add_error_note(ira->codegen, msg, fn_entry->set_cold_node, buf_sprintf("first set here")); + return ira->codegen->invalid_inst_gen; + } + + fn_entry->set_cold_node = instruction->base.base.source_node; + fn_entry->is_cold = want_cold; + + return ir_const_void(ira, &instruction->base.base); +} + +static IrInstGen *ir_analyze_instruction_set_runtime_safety(IrAnalyze *ira, + IrInstSrcSetRuntimeSafety *set_runtime_safety_instruction) +{ + if (ira->new_irb.exec->is_inline) { + // ignore setRuntimeSafety when running functions at compile time + return ir_const_void(ira, &set_runtime_safety_instruction->base.base); + } + + bool *safety_off_ptr; + AstNode **safety_set_node_ptr; + + Scope *scope = set_runtime_safety_instruction->base.base.scope; + while (scope != nullptr) { + if (scope->id == ScopeIdBlock) { + ScopeBlock *block_scope = (ScopeBlock *)scope; + safety_off_ptr = &block_scope->safety_off; + safety_set_node_ptr = &block_scope->safety_set_node; + break; + } else if (scope->id == ScopeIdFnDef) { + ScopeFnDef *def_scope = (ScopeFnDef *)scope; + ZigFn *target_fn = def_scope->fn_entry; + assert(target_fn->def_scope != nullptr); + safety_off_ptr = &target_fn->def_scope->safety_off; + safety_set_node_ptr = &target_fn->def_scope->safety_set_node; + break; + } else if (scope->id == ScopeIdDecls) { + ScopeDecls *decls_scope = (ScopeDecls *)scope; + safety_off_ptr = &decls_scope->safety_off; + safety_set_node_ptr = &decls_scope->safety_set_node; + break; + } else { + scope = scope->parent; + continue; + } + } + assert(scope != nullptr); + + IrInstGen *safety_on_value = set_runtime_safety_instruction->safety_on->child; + bool want_runtime_safety; + if (!ir_resolve_bool(ira, safety_on_value, &want_runtime_safety)) + return ira->codegen->invalid_inst_gen; + + AstNode *source_node = set_runtime_safety_instruction->base.base.source_node; + if (*safety_set_node_ptr) { + ErrorMsg *msg = ir_add_error_node(ira, source_node, + buf_sprintf("runtime safety set twice for same scope")); + add_error_note(ira->codegen, msg, *safety_set_node_ptr, buf_sprintf("first set here")); + return ira->codegen->invalid_inst_gen; + } + *safety_set_node_ptr = source_node; + *safety_off_ptr = !want_runtime_safety; + + return ir_const_void(ira, &set_runtime_safety_instruction->base.base); +} + +static IrInstGen *ir_analyze_instruction_set_float_mode(IrAnalyze *ira, + IrInstSrcSetFloatMode *instruction) +{ + if (ira->new_irb.exec->is_inline) { + // ignore setFloatMode when running functions at compile time + return ir_const_void(ira, &instruction->base.base); + } + + bool *fast_math_on_ptr; + AstNode **fast_math_set_node_ptr; + + Scope *scope = instruction->base.base.scope; + while (scope != nullptr) { + if (scope->id == ScopeIdBlock) { + ScopeBlock *block_scope = (ScopeBlock *)scope; + fast_math_on_ptr = &block_scope->fast_math_on; + fast_math_set_node_ptr = &block_scope->fast_math_set_node; + break; + } else if (scope->id == ScopeIdFnDef) { + ScopeFnDef *def_scope = (ScopeFnDef *)scope; + ZigFn *target_fn = def_scope->fn_entry; + assert(target_fn->def_scope != nullptr); + fast_math_on_ptr = &target_fn->def_scope->fast_math_on; + fast_math_set_node_ptr = &target_fn->def_scope->fast_math_set_node; + break; + } else if (scope->id == ScopeIdDecls) { + ScopeDecls *decls_scope = (ScopeDecls *)scope; + fast_math_on_ptr = &decls_scope->fast_math_on; + fast_math_set_node_ptr = &decls_scope->fast_math_set_node; + break; + } else { + scope = scope->parent; + continue; + } + } + assert(scope != nullptr); + + IrInstGen *float_mode_value = instruction->mode_value->child; + FloatMode float_mode_scalar; + if (!ir_resolve_float_mode(ira, float_mode_value, &float_mode_scalar)) + return ira->codegen->invalid_inst_gen; + + AstNode *source_node = instruction->base.base.source_node; + if (*fast_math_set_node_ptr) { + ErrorMsg *msg = ir_add_error_node(ira, source_node, + buf_sprintf("float mode set twice for same scope")); + add_error_note(ira->codegen, msg, *fast_math_set_node_ptr, buf_sprintf("first set here")); + return ira->codegen->invalid_inst_gen; + } + *fast_math_set_node_ptr = source_node; + *fast_math_on_ptr = (float_mode_scalar == FloatModeOptimized); + + return ir_const_void(ira, &instruction->base.base); +} + +static IrInstGen *ir_analyze_instruction_any_frame_type(IrAnalyze *ira, IrInstSrcAnyFrameType *instruction) { + ZigType *payload_type = nullptr; + if (instruction->payload_type != nullptr) { + payload_type = ir_resolve_type(ira, instruction->payload_type->child); + if (type_is_invalid(payload_type)) + return ira->codegen->invalid_inst_gen; + } + + ZigType *any_frame_type = get_any_frame_type(ira->codegen, payload_type); + return ir_const_type(ira, &instruction->base.base, any_frame_type); +} + +static IrInstGen *ir_analyze_instruction_slice_type(IrAnalyze *ira, IrInstSrcSliceType *slice_type_instruction) { + IrInstGen *result = ir_const(ira, &slice_type_instruction->base.base, ira->codegen->builtin_types.entry_type); + result->value->special = ConstValSpecialLazy; + + LazyValueSliceType *lazy_slice_type = heap::c_allocator.create(); + lazy_slice_type->ira = ira; ira_ref(ira); + result->value->data.x_lazy = &lazy_slice_type->base; + lazy_slice_type->base.id = LazyValueIdSliceType; + + if (slice_type_instruction->align_value != nullptr) { + lazy_slice_type->align_inst = slice_type_instruction->align_value->child; + if (ir_resolve_const(ira, lazy_slice_type->align_inst, LazyOk) == nullptr) + return ira->codegen->invalid_inst_gen; + } + + if (slice_type_instruction->sentinel != nullptr) { + lazy_slice_type->sentinel = slice_type_instruction->sentinel->child; + if (ir_resolve_const(ira, lazy_slice_type->sentinel, LazyOk) == nullptr) + return ira->codegen->invalid_inst_gen; + } + + lazy_slice_type->elem_type = slice_type_instruction->child_type->child; + if (ir_resolve_type_lazy(ira, lazy_slice_type->elem_type) == nullptr) + return ira->codegen->invalid_inst_gen; + + lazy_slice_type->is_const = slice_type_instruction->is_const; + lazy_slice_type->is_volatile = slice_type_instruction->is_volatile; + lazy_slice_type->is_allowzero = slice_type_instruction->is_allow_zero; + + return result; +} + +static IrInstGen *ir_analyze_instruction_asm(IrAnalyze *ira, IrInstSrcAsm *asm_instruction) { + Error err; + + assert(asm_instruction->base.base.source_node->type == NodeTypeAsmExpr); + + AstNode *node = asm_instruction->base.base.source_node; + AstNodeAsmExpr *asm_expr = &asm_instruction->base.base.source_node->data.asm_expr; + + Buf *template_buf = ir_resolve_str(ira, asm_instruction->asm_template->child); + if (template_buf == nullptr) + return ira->codegen->invalid_inst_gen; + + if (asm_instruction->is_global) { + buf_append_char(&ira->codegen->global_asm, '\n'); + buf_append_buf(&ira->codegen->global_asm, template_buf); + + return ir_const_void(ira, &asm_instruction->base.base); + } + + if (!ir_emit_global_runtime_side_effect(ira, &asm_instruction->base.base)) + return ira->codegen->invalid_inst_gen; + + ZigList tok_list = {}; + if ((err = parse_asm_template(ira, node, template_buf, &tok_list))) { + return ira->codegen->invalid_inst_gen; + } + + for (size_t token_i = 0; token_i < tok_list.length; token_i += 1) { + AsmToken asm_token = tok_list.at(token_i); + if (asm_token.id == AsmTokenIdVar) { + size_t index = find_asm_index(ira->codegen, node, &asm_token, template_buf); + if (index == SIZE_MAX) { + const char *ptr = buf_ptr(template_buf) + asm_token.start + 2; + uint32_t len = asm_token.end - asm_token.start - 2; + + add_node_error(ira->codegen, node, + buf_sprintf("could not find '%.*s' in the inputs or outputs", + len, ptr)); + return ira->codegen->invalid_inst_gen; + } + } + } + + // TODO validate the output types and variable types + + IrInstGen **input_list = heap::c_allocator.allocate(asm_expr->input_list.length); + IrInstGen **output_types = heap::c_allocator.allocate(asm_expr->output_list.length); + + ZigType *return_type = ira->codegen->builtin_types.entry_void; + for (size_t i = 0; i < asm_expr->output_list.length; i += 1) { + AsmOutput *asm_output = asm_expr->output_list.at(i); + if (asm_output->return_type) { + output_types[i] = asm_instruction->output_types[i]->child; + return_type = ir_resolve_type(ira, output_types[i]); + if (type_is_invalid(return_type)) + return ira->codegen->invalid_inst_gen; + } + } + + for (size_t i = 0; i < asm_expr->input_list.length; i += 1) { + IrInstGen *const input_value = asm_instruction->input_list[i]->child; + if (type_is_invalid(input_value->value->type)) + return ira->codegen->invalid_inst_gen; + + if (instr_is_comptime(input_value) && + (input_value->value->type->id == ZigTypeIdComptimeInt || + input_value->value->type->id == ZigTypeIdComptimeFloat)) { + ir_add_error(ira, &input_value->base, + buf_sprintf("expected sized integer or sized float, found %s", buf_ptr(&input_value->value->type->name))); + return ira->codegen->invalid_inst_gen; + } + + input_list[i] = input_value; + } + + return ir_build_asm_gen(ira, &asm_instruction->base.base, + template_buf, tok_list.items, tok_list.length, + input_list, output_types, asm_instruction->output_vars, asm_instruction->return_count, + asm_instruction->has_side_effects, return_type); +} + +static IrInstGen *ir_analyze_instruction_array_type(IrAnalyze *ira, IrInstSrcArrayType *array_type_instruction) { + IrInstGen *result = ir_const(ira, &array_type_instruction->base.base, ira->codegen->builtin_types.entry_type); + result->value->special = ConstValSpecialLazy; + + LazyValueArrayType *lazy_array_type = heap::c_allocator.create(); + lazy_array_type->ira = ira; ira_ref(ira); + result->value->data.x_lazy = &lazy_array_type->base; + lazy_array_type->base.id = LazyValueIdArrayType; + + lazy_array_type->elem_type = array_type_instruction->child_type->child; + if (ir_resolve_type_lazy(ira, lazy_array_type->elem_type) == nullptr) + return ira->codegen->invalid_inst_gen; + + if (!ir_resolve_usize(ira, array_type_instruction->size->child, &lazy_array_type->length)) + return ira->codegen->invalid_inst_gen; + + if (array_type_instruction->sentinel != nullptr) { + lazy_array_type->sentinel = array_type_instruction->sentinel->child; + if (ir_resolve_const(ira, lazy_array_type->sentinel, LazyOk) == nullptr) + return ira->codegen->invalid_inst_gen; + } + + return result; +} + +static IrInstGen *ir_analyze_instruction_size_of(IrAnalyze *ira, IrInstSrcSizeOf *instruction) { + IrInstGen *result = ir_const(ira, &instruction->base.base, ira->codegen->builtin_types.entry_num_lit_int); + result->value->special = ConstValSpecialLazy; + + LazyValueSizeOf *lazy_size_of = heap::c_allocator.create(); + lazy_size_of->ira = ira; ira_ref(ira); + result->value->data.x_lazy = &lazy_size_of->base; + lazy_size_of->base.id = LazyValueIdSizeOf; + lazy_size_of->bit_size = instruction->bit_size; + + lazy_size_of->target_type = instruction->type_value->child; + if (ir_resolve_type_lazy(ira, lazy_size_of->target_type) == nullptr) + return ira->codegen->invalid_inst_gen; + + return result; +} + +static IrInstGen *ir_analyze_test_non_null(IrAnalyze *ira, IrInst *source_inst, IrInstGen *value) { + ZigType *type_entry = value->value->type; + + if (type_entry->id == ZigTypeIdPointer && type_entry->data.pointer.allow_zero) { + if (instr_is_comptime(value)) { + ZigValue *c_ptr_val = ir_resolve_const(ira, value, UndefOk); + if (c_ptr_val == nullptr) + return ira->codegen->invalid_inst_gen; + if (c_ptr_val->special == ConstValSpecialUndef) + return ir_const_undef(ira, source_inst, ira->codegen->builtin_types.entry_bool); + bool is_null = c_ptr_val->data.x_ptr.special == ConstPtrSpecialNull || + (c_ptr_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr && + c_ptr_val->data.x_ptr.data.hard_coded_addr.addr == 0); + return ir_const_bool(ira, source_inst, !is_null); + } + + return ir_build_test_non_null_gen(ira, source_inst, value); + } else if (type_entry->id == ZigTypeIdOptional) { + if (instr_is_comptime(value)) { + ZigValue *maybe_val = ir_resolve_const(ira, value, UndefOk); + if (maybe_val == nullptr) + return ira->codegen->invalid_inst_gen; + if (maybe_val->special == ConstValSpecialUndef) + return ir_const_undef(ira, source_inst, ira->codegen->builtin_types.entry_bool); + + return ir_const_bool(ira, source_inst, !optional_value_is_null(maybe_val)); + } + + return ir_build_test_non_null_gen(ira, source_inst, value); + } else if (type_entry->id == ZigTypeIdNull) { + return ir_const_bool(ira, source_inst, false); + } else { + return ir_const_bool(ira, source_inst, true); + } +} + +static IrInstGen *ir_analyze_instruction_test_non_null(IrAnalyze *ira, IrInstSrcTestNonNull *instruction) { + IrInstGen *value = instruction->value->child; + if (type_is_invalid(value->value->type)) + return ira->codegen->invalid_inst_gen; + + return ir_analyze_test_non_null(ira, &instruction->base.base, value); +} + +static IrInstGen *ir_analyze_unwrap_optional_payload(IrAnalyze *ira, IrInst* source_instr, + IrInstGen *base_ptr, bool safety_check_on, bool initializing) +{ + Error err; + + ZigType *type_entry = get_ptr_elem_type(ira->codegen, base_ptr); + if (type_is_invalid(type_entry)) + return ira->codegen->invalid_inst_gen; + + if (type_entry->id == ZigTypeIdPointer && type_entry->data.pointer.ptr_len == PtrLenC) { + if (instr_is_comptime(base_ptr)) { + ZigValue *val = ir_resolve_const(ira, base_ptr, UndefBad); + if (!val) + return ira->codegen->invalid_inst_gen; + if (val->data.x_ptr.mut != ConstPtrMutRuntimeVar) { + ZigValue *c_ptr_val = const_ptr_pointee(ira, ira->codegen, val, source_instr->source_node); + if (c_ptr_val == nullptr) + return ira->codegen->invalid_inst_gen; + bool is_null = c_ptr_val->data.x_ptr.special == ConstPtrSpecialNull || + (c_ptr_val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr && + c_ptr_val->data.x_ptr.data.hard_coded_addr.addr == 0); + if (is_null) { + ir_add_error(ira, source_instr, buf_sprintf("unable to unwrap null")); + return ira->codegen->invalid_inst_gen; + } + return base_ptr; + } + } + if (!safety_check_on) + return base_ptr; + IrInstGen *c_ptr_val = ir_get_deref(ira, source_instr, base_ptr, nullptr); + ir_build_assert_non_null(ira, source_instr, c_ptr_val); + return base_ptr; + } + + if (type_entry->id != ZigTypeIdOptional) { + ir_add_error(ira, &base_ptr->base, + buf_sprintf("expected optional type, found '%s'", buf_ptr(&type_entry->name))); + return ira->codegen->invalid_inst_gen; + } + + ZigType *child_type = type_entry->data.maybe.child_type; + ZigType *result_type = get_pointer_to_type_extra(ira->codegen, child_type, + base_ptr->value->type->data.pointer.is_const, base_ptr->value->type->data.pointer.is_volatile, + PtrLenSingle, 0, 0, 0, false); + + bool same_comptime_repr = types_have_same_zig_comptime_repr(ira->codegen, child_type, type_entry); + + if (instr_is_comptime(base_ptr)) { + ZigValue *ptr_val = ir_resolve_const(ira, base_ptr, UndefBad); + if (ptr_val == nullptr) + return ira->codegen->invalid_inst_gen; + if (ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar) { + ZigValue *optional_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node); + if (optional_val == nullptr) + return ira->codegen->invalid_inst_gen; + + if (initializing) { + switch (type_has_one_possible_value(ira->codegen, child_type)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_inst_gen; + case OnePossibleValueNo: + if (!same_comptime_repr) { + ZigValue *payload_val = ira->codegen->pass1_arena->create(); + payload_val->type = child_type; + payload_val->special = ConstValSpecialUndef; + payload_val->parent.id = ConstParentIdOptionalPayload; + payload_val->parent.data.p_optional_payload.optional_val = optional_val; + + optional_val->data.x_optional = payload_val; + optional_val->special = ConstValSpecialStatic; + } + break; + case OnePossibleValueYes: { + optional_val->special = ConstValSpecialStatic; + optional_val->data.x_optional = get_the_one_possible_value(ira->codegen, child_type); + break; + } + } + } else { + if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, + source_instr->source_node, optional_val, UndefBad))) + return ira->codegen->invalid_inst_gen; + if (optional_value_is_null(optional_val)) { + ir_add_error(ira, source_instr, buf_sprintf("unable to unwrap null")); + return ira->codegen->invalid_inst_gen; + } + } + + IrInstGen *result; + if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { + result = ir_build_optional_unwrap_ptr_gen(ira, source_instr, base_ptr, false, + initializing, result_type); + result->value->special = ConstValSpecialStatic; + } else { + result = ir_const(ira, source_instr, result_type); + } + ZigValue *result_val = result->value; + result_val->data.x_ptr.special = ConstPtrSpecialRef; + result_val->data.x_ptr.mut = ptr_val->data.x_ptr.mut; + switch (type_has_one_possible_value(ira->codegen, child_type)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_inst_gen; + case OnePossibleValueNo: + if (same_comptime_repr) { + result_val->data.x_ptr.data.ref.pointee = optional_val; + } else { + assert(optional_val->data.x_optional != nullptr); + result_val->data.x_ptr.data.ref.pointee = optional_val->data.x_optional; + } + break; + case OnePossibleValueYes: + assert(optional_val->data.x_optional != nullptr); + result_val->data.x_ptr.data.ref.pointee = optional_val->data.x_optional; + break; + } + return result; + } + } + + return ir_build_optional_unwrap_ptr_gen(ira, source_instr, base_ptr, safety_check_on, + initializing, result_type); +} + +static IrInstGen *ir_analyze_instruction_optional_unwrap_ptr(IrAnalyze *ira, + IrInstSrcOptionalUnwrapPtr *instruction) +{ + IrInstGen *base_ptr = instruction->base_ptr->child; + if (type_is_invalid(base_ptr->value->type)) + return ira->codegen->invalid_inst_gen; + + return ir_analyze_unwrap_optional_payload(ira, &instruction->base.base, base_ptr, + instruction->safety_check_on, false); +} + +static IrInstGen *ir_analyze_instruction_ctz(IrAnalyze *ira, IrInstSrcCtz *instruction) { + ZigType *int_type = ir_resolve_int_type(ira, instruction->type->child); + if (type_is_invalid(int_type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *op = ir_implicit_cast(ira, instruction->op->child, int_type); + if (type_is_invalid(op->value->type)) + return ira->codegen->invalid_inst_gen; + + if (int_type->data.integral.bit_count == 0) + return ir_const_unsigned(ira, &instruction->base.base, 0); + + if (instr_is_comptime(op)) { + ZigValue *val = ir_resolve_const(ira, op, UndefOk); + if (val == nullptr) + return ira->codegen->invalid_inst_gen; + if (val->special == ConstValSpecialUndef) + return ir_const_undef(ira, &instruction->base.base, ira->codegen->builtin_types.entry_num_lit_int); + size_t result_usize = bigint_ctz(&op->value->data.x_bigint, int_type->data.integral.bit_count); + return ir_const_unsigned(ira, &instruction->base.base, result_usize); + } + + ZigType *return_type = get_smallest_unsigned_int_type(ira->codegen, int_type->data.integral.bit_count); + return ir_build_ctz_gen(ira, &instruction->base.base, return_type, op); +} + +static IrInstGen *ir_analyze_instruction_clz(IrAnalyze *ira, IrInstSrcClz *instruction) { + ZigType *int_type = ir_resolve_int_type(ira, instruction->type->child); + if (type_is_invalid(int_type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *op = ir_implicit_cast(ira, instruction->op->child, int_type); + if (type_is_invalid(op->value->type)) + return ira->codegen->invalid_inst_gen; + + if (int_type->data.integral.bit_count == 0) + return ir_const_unsigned(ira, &instruction->base.base, 0); + + if (instr_is_comptime(op)) { + ZigValue *val = ir_resolve_const(ira, op, UndefOk); + if (val == nullptr) + return ira->codegen->invalid_inst_gen; + if (val->special == ConstValSpecialUndef) + return ir_const_undef(ira, &instruction->base.base, ira->codegen->builtin_types.entry_num_lit_int); + size_t result_usize = bigint_clz(&op->value->data.x_bigint, int_type->data.integral.bit_count); + return ir_const_unsigned(ira, &instruction->base.base, result_usize); + } + + ZigType *return_type = get_smallest_unsigned_int_type(ira->codegen, int_type->data.integral.bit_count); + return ir_build_clz_gen(ira, &instruction->base.base, return_type, op); +} + +static IrInstGen *ir_analyze_instruction_pop_count(IrAnalyze *ira, IrInstSrcPopCount *instruction) { + ZigType *int_type = ir_resolve_int_type(ira, instruction->type->child); + if (type_is_invalid(int_type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *op = ir_implicit_cast(ira, instruction->op->child, int_type); + if (type_is_invalid(op->value->type)) + return ira->codegen->invalid_inst_gen; + + if (int_type->data.integral.bit_count == 0) + return ir_const_unsigned(ira, &instruction->base.base, 0); + + if (instr_is_comptime(op)) { + ZigValue *val = ir_resolve_const(ira, op, UndefOk); + if (val == nullptr) + return ira->codegen->invalid_inst_gen; + if (val->special == ConstValSpecialUndef) + return ir_const_undef(ira, &instruction->base.base, ira->codegen->builtin_types.entry_num_lit_int); + + if (bigint_cmp_zero(&val->data.x_bigint) != CmpLT) { + size_t result = bigint_popcount_unsigned(&val->data.x_bigint); + return ir_const_unsigned(ira, &instruction->base.base, result); + } + size_t result = bigint_popcount_signed(&val->data.x_bigint, int_type->data.integral.bit_count); + return ir_const_unsigned(ira, &instruction->base.base, result); + } + + ZigType *return_type = get_smallest_unsigned_int_type(ira->codegen, int_type->data.integral.bit_count); + return ir_build_pop_count_gen(ira, &instruction->base.base, return_type, op); +} + +static IrInstGen *ir_analyze_union_tag(IrAnalyze *ira, IrInst* source_instr, IrInstGen *value, bool is_gen) { + if (type_is_invalid(value->value->type)) + return ira->codegen->invalid_inst_gen; + + if (value->value->type->id != ZigTypeIdUnion) { + ir_add_error(ira, &value->base, + buf_sprintf("expected enum or union type, found '%s'", buf_ptr(&value->value->type->name))); + return ira->codegen->invalid_inst_gen; + } + if (!value->value->type->data.unionation.have_explicit_tag_type && !is_gen) { + ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("union has no associated enum")); + if (value->value->type->data.unionation.decl_node != nullptr) { + add_error_note(ira->codegen, msg, value->value->type->data.unionation.decl_node, + buf_sprintf("declared here")); + } + return ira->codegen->invalid_inst_gen; + } + + ZigType *tag_type = value->value->type->data.unionation.tag_type; + assert(tag_type->id == ZigTypeIdEnum); + + if (instr_is_comptime(value)) { + ZigValue *val = ir_resolve_const(ira, value, UndefBad); + if (!val) + return ira->codegen->invalid_inst_gen; + + IrInstGenConst *const_instruction = ir_create_inst_gen(&ira->new_irb, + source_instr->scope, source_instr->source_node); + const_instruction->base.value->type = tag_type; + const_instruction->base.value->special = ConstValSpecialStatic; + bigint_init_bigint(&const_instruction->base.value->data.x_enum_tag, &val->data.x_union.tag); + return &const_instruction->base; + } + + return ir_build_union_tag(ira, source_instr, value, tag_type); +} + +static IrInstGen *ir_analyze_instruction_switch_br(IrAnalyze *ira, + IrInstSrcSwitchBr *switch_br_instruction) +{ + IrInstGen *target_value = switch_br_instruction->target_value->child; + if (type_is_invalid(target_value->value->type)) + return ir_unreach_error(ira); + + if (switch_br_instruction->switch_prongs_void != nullptr) { + if (type_is_invalid(switch_br_instruction->switch_prongs_void->child->value->type)) { + return ir_unreach_error(ira); + } + } + + + size_t case_count = switch_br_instruction->case_count; + + bool is_comptime; + if (!ir_resolve_comptime(ira, switch_br_instruction->is_comptime->child, &is_comptime)) + return ira->codegen->invalid_inst_gen; + + if (is_comptime || instr_is_comptime(target_value)) { + ZigValue *target_val = ir_resolve_const(ira, target_value, UndefBad); + if (!target_val) + return ir_unreach_error(ira); + + IrBasicBlockSrc *old_dest_block = switch_br_instruction->else_block; + for (size_t i = 0; i < case_count; i += 1) { + IrInstSrcSwitchBrCase *old_case = &switch_br_instruction->cases[i]; + IrInstGen *case_value = old_case->value->child; + if (type_is_invalid(case_value->value->type)) + return ir_unreach_error(ira); + + IrInstGen *casted_case_value = ir_implicit_cast(ira, case_value, target_value->value->type); + if (type_is_invalid(casted_case_value->value->type)) + return ir_unreach_error(ira); + + ZigValue *case_val = ir_resolve_const(ira, casted_case_value, UndefBad); + if (!case_val) + return ir_unreach_error(ira); + + if (const_values_equal(ira->codegen, target_val, case_val)) { + old_dest_block = old_case->block; + break; + } + } + + if (is_comptime || old_dest_block->ref_count == 1) { + return ir_inline_bb(ira, &switch_br_instruction->base.base, old_dest_block); + } else { + IrBasicBlockGen *new_dest_block = ir_get_new_bb(ira, old_dest_block, &switch_br_instruction->base.base); + IrInstGen *result = ir_build_br_gen(ira, &switch_br_instruction->base.base, new_dest_block); + return ir_finish_anal(ira, result); + } + } + + IrInstGenSwitchBrCase *cases = heap::c_allocator.allocate(case_count); + for (size_t i = 0; i < case_count; i += 1) { + IrInstSrcSwitchBrCase *old_case = &switch_br_instruction->cases[i]; + IrInstGenSwitchBrCase *new_case = &cases[i]; + new_case->block = ir_get_new_bb(ira, old_case->block, &switch_br_instruction->base.base); + new_case->value = ira->codegen->invalid_inst_gen; + + // Calling ir_get_new_bb set the ref_instruction on the new basic block. + // However a switch br may branch to the same basic block which would trigger an + // incorrect re-generation of the block. So we set it to null here and assign + // it back after the loop. + new_case->block->ref_instruction = nullptr; + + IrInstSrc *old_value = old_case->value; + IrInstGen *new_value = old_value->child; + if (type_is_invalid(new_value->value->type)) + continue; + + IrInstGen *casted_new_value = ir_implicit_cast(ira, new_value, target_value->value->type); + if (type_is_invalid(casted_new_value->value->type)) + continue; + + if (!ir_resolve_const(ira, casted_new_value, UndefBad)) + continue; + + new_case->value = casted_new_value; + } + + for (size_t i = 0; i < case_count; i += 1) { + IrInstGenSwitchBrCase *new_case = &cases[i]; + if (type_is_invalid(new_case->value->value->type)) + return ir_unreach_error(ira); + new_case->block->ref_instruction = &switch_br_instruction->base.base; + } + + IrBasicBlockGen *new_else_block = ir_get_new_bb(ira, switch_br_instruction->else_block, &switch_br_instruction->base.base); + IrInstGenSwitchBr *switch_br = ir_build_switch_br_gen(ira, &switch_br_instruction->base.base, + target_value, new_else_block, case_count, cases); + return ir_finish_anal(ira, &switch_br->base); +} + +static IrInstGen *ir_analyze_instruction_switch_target(IrAnalyze *ira, + IrInstSrcSwitchTarget *switch_target_instruction) +{ + Error err; + IrInstGen *target_value_ptr = switch_target_instruction->target_value_ptr->child; + if (type_is_invalid(target_value_ptr->value->type)) + return ira->codegen->invalid_inst_gen; + + if (target_value_ptr->value->type->id == ZigTypeIdMetaType) { + assert(instr_is_comptime(target_value_ptr)); + ZigType *ptr_type = target_value_ptr->value->data.x_type; + assert(ptr_type->id == ZigTypeIdPointer); + return ir_const_type(ira, &switch_target_instruction->base.base, ptr_type->data.pointer.child_type); + } + + ZigType *target_type = target_value_ptr->value->type->data.pointer.child_type; + ZigValue *pointee_val = nullptr; + if (instr_is_comptime(target_value_ptr) && target_value_ptr->value->data.x_ptr.mut != ConstPtrMutRuntimeVar) { + pointee_val = const_ptr_pointee(ira, ira->codegen, target_value_ptr->value, target_value_ptr->base.source_node); + if (pointee_val == nullptr) + return ira->codegen->invalid_inst_gen; + + if (pointee_val->special == ConstValSpecialRuntime) + pointee_val = nullptr; + } + if ((err = type_resolve(ira->codegen, target_type, ResolveStatusSizeKnown))) + return ira->codegen->invalid_inst_gen; + + switch (target_type->id) { + case ZigTypeIdInvalid: + zig_unreachable(); + case ZigTypeIdMetaType: + case ZigTypeIdVoid: + case ZigTypeIdBool: + case ZigTypeIdInt: + case ZigTypeIdFloat: + case ZigTypeIdComptimeFloat: + case ZigTypeIdComptimeInt: + case ZigTypeIdEnumLiteral: + case ZigTypeIdPointer: + case ZigTypeIdFn: + case ZigTypeIdErrorSet: { + if (pointee_val) { + IrInstGen *result = ir_const(ira, &switch_target_instruction->base.base, nullptr); + copy_const_val(ira->codegen, result->value, pointee_val); + result->value->type = target_type; + return result; + } + + IrInstGen *result = ir_get_deref(ira, &switch_target_instruction->base.base, target_value_ptr, nullptr); + result->value->type = target_type; + return result; + } + case ZigTypeIdUnion: { + AstNode *decl_node = target_type->data.unionation.decl_node; + if (!decl_node->data.container_decl.auto_enum && + decl_node->data.container_decl.init_arg_expr == nullptr) + { + ErrorMsg *msg = ir_add_error(ira, &target_value_ptr->base, + buf_sprintf("switch on union which has no attached enum")); + add_error_note(ira->codegen, msg, decl_node, + buf_sprintf("consider 'union(enum)' here")); + return ira->codegen->invalid_inst_gen; + } + ZigType *tag_type = target_type->data.unionation.tag_type; + assert(tag_type != nullptr); + assert(tag_type->id == ZigTypeIdEnum); + if (pointee_val) { + IrInstGen *result = ir_const(ira, &switch_target_instruction->base.base, tag_type); + bigint_init_bigint(&result->value->data.x_enum_tag, &pointee_val->data.x_union.tag); + return result; + } + if (tag_type->data.enumeration.src_field_count == 1 && !tag_type->data.enumeration.non_exhaustive) { + IrInstGen *result = ir_const(ira, &switch_target_instruction->base.base, tag_type); + TypeEnumField *only_field = &tag_type->data.enumeration.fields[0]; + bigint_init_bigint(&result->value->data.x_enum_tag, &only_field->value); + return result; + } + + IrInstGen *union_value = ir_get_deref(ira, &switch_target_instruction->base.base, target_value_ptr, nullptr); + union_value->value->type = target_type; + + return ir_build_union_tag(ira, &switch_target_instruction->base.base, union_value, tag_type); + } + case ZigTypeIdEnum: { + if ((err = type_resolve(ira->codegen, target_type, ResolveStatusZeroBitsKnown))) + return ira->codegen->invalid_inst_gen; + if (target_type->data.enumeration.src_field_count == 1 && !target_type->data.enumeration.non_exhaustive) { + TypeEnumField *only_field = &target_type->data.enumeration.fields[0]; + IrInstGen *result = ir_const(ira, &switch_target_instruction->base.base, target_type); + bigint_init_bigint(&result->value->data.x_enum_tag, &only_field->value); + return result; + } + + if (pointee_val) { + IrInstGen *result = ir_const(ira, &switch_target_instruction->base.base, target_type); + bigint_init_bigint(&result->value->data.x_enum_tag, &pointee_val->data.x_enum_tag); + return result; + } + + IrInstGen *enum_value = ir_get_deref(ira, &switch_target_instruction->base.base, target_value_ptr, nullptr); + enum_value->value->type = target_type; + return enum_value; + } + case ZigTypeIdErrorUnion: + case ZigTypeIdUnreachable: + case ZigTypeIdArray: + case ZigTypeIdStruct: + case ZigTypeIdUndefined: + case ZigTypeIdNull: + case ZigTypeIdOptional: + case ZigTypeIdBoundFn: + case ZigTypeIdOpaque: + case ZigTypeIdVector: + case ZigTypeIdFnFrame: + case ZigTypeIdAnyFrame: + ir_add_error(ira, &switch_target_instruction->base.base, + buf_sprintf("invalid switch target type '%s'", buf_ptr(&target_type->name))); + return ira->codegen->invalid_inst_gen; + } + zig_unreachable(); +} + +static IrInstGen *ir_analyze_instruction_switch_var(IrAnalyze *ira, IrInstSrcSwitchVar *instruction) { + IrInstGen *target_value_ptr = instruction->target_value_ptr->child; + if (type_is_invalid(target_value_ptr->value->type)) + return ira->codegen->invalid_inst_gen; + + ZigType *ref_type = target_value_ptr->value->type; + assert(ref_type->id == ZigTypeIdPointer); + ZigType *target_type = target_value_ptr->value->type->data.pointer.child_type; + if (target_type->id == ZigTypeIdUnion) { + ZigType *enum_type = target_type->data.unionation.tag_type; + assert(enum_type != nullptr); + assert(enum_type->id == ZigTypeIdEnum); + assert(instruction->prongs_len > 0); + + IrInstGen *first_prong_value = instruction->prongs_ptr[0]->child; + if (type_is_invalid(first_prong_value->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *first_casted_prong_value = ir_implicit_cast(ira, first_prong_value, enum_type); + if (type_is_invalid(first_casted_prong_value->value->type)) + return ira->codegen->invalid_inst_gen; + + ZigValue *first_prong_val = ir_resolve_const(ira, first_casted_prong_value, UndefBad); + if (first_prong_val == nullptr) + return ira->codegen->invalid_inst_gen; + + TypeUnionField *first_field = find_union_field_by_tag(target_type, &first_prong_val->data.x_enum_tag); + + ErrorMsg *invalid_payload_msg = nullptr; + for (size_t prong_i = 1; prong_i < instruction->prongs_len; prong_i += 1) { + IrInstGen *this_prong_inst = instruction->prongs_ptr[prong_i]->child; + if (type_is_invalid(this_prong_inst->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *this_casted_prong_value = ir_implicit_cast(ira, this_prong_inst, enum_type); + if (type_is_invalid(this_casted_prong_value->value->type)) + return ira->codegen->invalid_inst_gen; + + ZigValue *this_prong = ir_resolve_const(ira, this_casted_prong_value, UndefBad); + if (this_prong == nullptr) + return ira->codegen->invalid_inst_gen; + + TypeUnionField *payload_field = find_union_field_by_tag(target_type, &this_prong->data.x_enum_tag); + ZigType *payload_type = payload_field->type_entry; + if (first_field->type_entry != payload_type) { + if (invalid_payload_msg == nullptr) { + invalid_payload_msg = ir_add_error(ira, &instruction->base.base, + buf_sprintf("capture group with incompatible types")); + add_error_note(ira->codegen, invalid_payload_msg, first_prong_value->base.source_node, + buf_sprintf("type '%s' here", buf_ptr(&first_field->type_entry->name))); + } + add_error_note(ira->codegen, invalid_payload_msg, this_prong_inst->base.source_node, + buf_sprintf("type '%s' here", buf_ptr(&payload_field->type_entry->name))); + } + } + + if (invalid_payload_msg != nullptr) { + return ira->codegen->invalid_inst_gen; + } + + if (instr_is_comptime(target_value_ptr)) { + ZigValue *target_val_ptr = ir_resolve_const(ira, target_value_ptr, UndefBad); + if (!target_value_ptr) + return ira->codegen->invalid_inst_gen; + + ZigValue *pointee_val = const_ptr_pointee(ira, ira->codegen, target_val_ptr, instruction->base.base.source_node); + if (pointee_val == nullptr) + return ira->codegen->invalid_inst_gen; + + IrInstGen *result = ir_const(ira, &instruction->base.base, + get_pointer_to_type(ira->codegen, first_field->type_entry, + target_val_ptr->type->data.pointer.is_const)); + ZigValue *out_val = result->value; + out_val->data.x_ptr.special = ConstPtrSpecialRef; + out_val->data.x_ptr.mut = target_val_ptr->data.x_ptr.mut; + out_val->data.x_ptr.data.ref.pointee = pointee_val->data.x_union.payload; + return result; + } + + ZigType *result_type = get_pointer_to_type(ira->codegen, first_field->type_entry, + target_value_ptr->value->type->data.pointer.is_const); + return ir_build_union_field_ptr(ira, &instruction->base.base, target_value_ptr, first_field, + false, false, result_type); + } else if (target_type->id == ZigTypeIdErrorSet) { + // construct an error set from the prong values + ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet); + err_set_type->size_in_bits = ira->codegen->builtin_types.entry_global_error_set->size_in_bits; + err_set_type->abi_align = ira->codegen->builtin_types.entry_global_error_set->abi_align; + err_set_type->abi_size = ira->codegen->builtin_types.entry_global_error_set->abi_size; + ZigList error_list = {}; + buf_resize(&err_set_type->name, 0); + buf_appendf(&err_set_type->name, "error{"); + for (size_t i = 0; i < instruction->prongs_len; i += 1) { + ErrorTableEntry *err = ir_resolve_error(ira, instruction->prongs_ptr[i]->child); + if (err == nullptr) + return ira->codegen->invalid_inst_gen; + error_list.append(err); + buf_appendf(&err_set_type->name, "%s,", buf_ptr(&err->name)); + } + err_set_type->data.error_set.errors = error_list.items; + err_set_type->data.error_set.err_count = error_list.length; + buf_appendf(&err_set_type->name, "}"); + + + ZigType *new_target_value_ptr_type = get_pointer_to_type_extra(ira->codegen, + err_set_type, + ref_type->data.pointer.is_const, ref_type->data.pointer.is_volatile, + ref_type->data.pointer.ptr_len, + ref_type->data.pointer.explicit_alignment, + ref_type->data.pointer.bit_offset_in_host, ref_type->data.pointer.host_int_bytes, + ref_type->data.pointer.allow_zero); + return ir_analyze_ptr_cast(ira, &instruction->base.base, target_value_ptr, + &instruction->target_value_ptr->base, new_target_value_ptr_type, &instruction->base.base, false, false); + } else { + ir_add_error(ira, &instruction->base.base, + buf_sprintf("switch on type '%s' provides no expression parameter", buf_ptr(&target_type->name))); + return ira->codegen->invalid_inst_gen; + } +} + +static IrInstGen *ir_analyze_instruction_switch_else_var(IrAnalyze *ira, + IrInstSrcSwitchElseVar *instruction) +{ + IrInstGen *target_value_ptr = instruction->target_value_ptr->child; + if (type_is_invalid(target_value_ptr->value->type)) + return ira->codegen->invalid_inst_gen; + + ZigType *ref_type = target_value_ptr->value->type; + assert(ref_type->id == ZigTypeIdPointer); + ZigType *target_type = target_value_ptr->value->type->data.pointer.child_type; + if (target_type->id == ZigTypeIdErrorSet) { + // make a new set that has the other cases removed + if (!resolve_inferred_error_set(ira->codegen, target_type, instruction->base.base.source_node)) { + return ira->codegen->invalid_inst_gen; + } + if (type_is_global_error_set(target_type)) { + // the type of the else capture variable still has to be the global error set. + // once the runtime hint system is more sophisticated, we could add some hint information here. + return target_value_ptr; + } + // Make note of the errors handled by other cases + ErrorTableEntry **errors = heap::c_allocator.allocate(ira->codegen->errors_by_index.length); + // We may not have any case in the switch if this is a lone else + const size_t switch_cases = instruction->switch_br ? instruction->switch_br->case_count : 0; + for (size_t case_i = 0; case_i < switch_cases; case_i += 1) { + IrInstSrcSwitchBrCase *br_case = &instruction->switch_br->cases[case_i]; + IrInstGen *case_expr = br_case->value->child; + if (case_expr->value->type->id == ZigTypeIdErrorSet) { + ErrorTableEntry *err = ir_resolve_error(ira, case_expr); + if (err == nullptr) + return ira->codegen->invalid_inst_gen; + errors[err->value] = err; + } else if (case_expr->value->type->id == ZigTypeIdMetaType) { + ZigType *err_set_type = ir_resolve_type(ira, case_expr); + if (type_is_invalid(err_set_type)) + return ira->codegen->invalid_inst_gen; + populate_error_set_table(errors, err_set_type); + } else { + zig_unreachable(); + } + } + ZigList result_list = {}; + + ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet); + buf_resize(&err_set_type->name, 0); + buf_appendf(&err_set_type->name, "error{"); + + // Look at all the errors in the type switched on and add them to the result_list + // if they are not handled by cases. + for (uint32_t i = 0; i < target_type->data.error_set.err_count; i += 1) { + ErrorTableEntry *error_entry = target_type->data.error_set.errors[i]; + ErrorTableEntry *existing_entry = errors[error_entry->value]; + if (existing_entry == nullptr) { + result_list.append(error_entry); + buf_appendf(&err_set_type->name, "%s,", buf_ptr(&error_entry->name)); + } + } + heap::c_allocator.deallocate(errors, ira->codegen->errors_by_index.length); + + err_set_type->data.error_set.err_count = result_list.length; + err_set_type->data.error_set.errors = result_list.items; + err_set_type->size_in_bits = ira->codegen->builtin_types.entry_global_error_set->size_in_bits; + err_set_type->abi_align = ira->codegen->builtin_types.entry_global_error_set->abi_align; + err_set_type->abi_size = ira->codegen->builtin_types.entry_global_error_set->abi_size; + + buf_appendf(&err_set_type->name, "}"); + + ZigType *new_target_value_ptr_type = get_pointer_to_type_extra(ira->codegen, + err_set_type, + ref_type->data.pointer.is_const, ref_type->data.pointer.is_volatile, + ref_type->data.pointer.ptr_len, + ref_type->data.pointer.explicit_alignment, + ref_type->data.pointer.bit_offset_in_host, ref_type->data.pointer.host_int_bytes, + ref_type->data.pointer.allow_zero); + return ir_analyze_ptr_cast(ira, &instruction->base.base, target_value_ptr, + &instruction->target_value_ptr->base, new_target_value_ptr_type, &instruction->base.base, false, false); + } + + return target_value_ptr; +} + +static IrInstGen *ir_analyze_instruction_import(IrAnalyze *ira, IrInstSrcImport *import_instruction) { + Error err; + + IrInstGen *name_value = import_instruction->name->child; + Buf *import_target_str = ir_resolve_str(ira, name_value); + if (!import_target_str) + return ira->codegen->invalid_inst_gen; + + AstNode *source_node = import_instruction->base.base.source_node; + ZigType *import = source_node->owner; + + ZigType *target_import; + Buf *import_target_path; + Buf full_path = BUF_INIT; + if ((err = analyze_import(ira->codegen, import, import_target_str, &target_import, + &import_target_path, &full_path))) + { + if (err == ErrorImportOutsidePkgPath) { + ir_add_error_node(ira, source_node, + buf_sprintf("import of file outside package path: '%s'", + buf_ptr(import_target_path))); + return ira->codegen->invalid_inst_gen; + } else if (err == ErrorFileNotFound) { + ir_add_error_node(ira, source_node, + buf_sprintf("unable to find '%s'", buf_ptr(import_target_path))); + return ira->codegen->invalid_inst_gen; + } else { + ir_add_error_node(ira, source_node, + buf_sprintf("unable to open '%s': %s", buf_ptr(&full_path), err_str(err))); + return ira->codegen->invalid_inst_gen; + } + } + + return ir_const_type(ira, &import_instruction->base.base, target_import); +} + +static IrInstGen *ir_analyze_instruction_ref(IrAnalyze *ira, IrInstSrcRef *ref_instruction) { + IrInstGen *value = ref_instruction->value->child; + if (type_is_invalid(value->value->type)) + return ira->codegen->invalid_inst_gen; + + bool is_const = false; + bool is_volatile = false; + + ZigValue *child_value = value->value; + if (child_value->special == ConstValSpecialStatic) { + is_const = true; + } + + return ir_get_ref(ira, &ref_instruction->base.base, value, is_const, is_volatile); +} + +static IrInstGen *ir_analyze_union_init(IrAnalyze *ira, IrInst* source_instruction, + AstNode *field_source_node, ZigType *union_type, Buf *field_name, IrInstGen *field_result_loc, + IrInstGen *result_loc) +{ + Error err; + assert(union_type->id == ZigTypeIdUnion); + + if ((err = type_resolve(ira->codegen, union_type, ResolveStatusZeroBitsKnown))) + return ira->codegen->invalid_inst_gen; + + TypeUnionField *type_field = find_union_type_field(union_type, field_name); + if (type_field == nullptr) { + ir_add_error_node(ira, field_source_node, + buf_sprintf("no field named '%s' in union '%s'", + buf_ptr(field_name), buf_ptr(&union_type->name))); + return ira->codegen->invalid_inst_gen; + } + + if (type_is_invalid(type_field->type_entry)) + return ira->codegen->invalid_inst_gen; + + if (result_loc->value->data.x_ptr.mut == ConstPtrMutInfer) { + if (instr_is_comptime(field_result_loc) && + field_result_loc->value->data.x_ptr.mut != ConstPtrMutRuntimeVar) + { + // nothing + } else { + result_loc->value->special = ConstValSpecialRuntime; + } + } + + bool is_comptime = ir_should_inline(ira->old_irb.exec, source_instruction->scope) + || type_requires_comptime(ira->codegen, union_type) == ReqCompTimeYes; + + IrInstGen *result = ir_get_deref(ira, source_instruction, result_loc, nullptr); + if (is_comptime && !instr_is_comptime(result)) { + ir_add_error(ira, &field_result_loc->base, + buf_sprintf("unable to evaluate constant expression")); + return ira->codegen->invalid_inst_gen; + } + return result; +} + +static IrInstGen *ir_analyze_container_init_fields(IrAnalyze *ira, IrInst *source_instr, + ZigType *container_type, size_t instr_field_count, IrInstSrcContainerInitFieldsField *fields, + IrInstGen *result_loc) +{ + Error err; + if (container_type->id == ZigTypeIdUnion) { + if (instr_field_count != 1) { + ir_add_error(ira, source_instr, + buf_sprintf("union initialization expects exactly one field")); + return ira->codegen->invalid_inst_gen; + } + IrInstSrcContainerInitFieldsField *field = &fields[0]; + IrInstGen *field_result_loc = field->result_loc->child; + if (type_is_invalid(field_result_loc->value->type)) + return ira->codegen->invalid_inst_gen; + + return ir_analyze_union_init(ira, source_instr, field->source_node, container_type, field->name, + field_result_loc, result_loc); + } + if (container_type->id != ZigTypeIdStruct || is_slice(container_type)) { + ir_add_error(ira, source_instr, + buf_sprintf("type '%s' does not support struct initialization syntax", + buf_ptr(&container_type->name))); + return ira->codegen->invalid_inst_gen; + } + + if (container_type->data.structure.resolve_status == ResolveStatusBeingInferred) { + // We're now done inferring the type. + container_type->data.structure.resolve_status = ResolveStatusUnstarted; + } + + if ((err = type_resolve(ira->codegen, container_type, ResolveStatusSizeKnown))) + return ira->codegen->invalid_inst_gen; + + size_t actual_field_count = container_type->data.structure.src_field_count; + + IrInstGen *first_non_const_instruction = nullptr; + + AstNode **field_assign_nodes = heap::c_allocator.allocate(actual_field_count); + ZigList const_ptrs = {}; + + bool is_comptime = ir_should_inline(ira->old_irb.exec, source_instr->scope) + || type_requires_comptime(ira->codegen, container_type) == ReqCompTimeYes; + + + // Here we iterate over the fields that have been initialized, and emit + // compile errors for missing fields and duplicate fields. + // It is only now that we find out whether the struct initialization can be a comptime + // value, but we have already emitted runtime instructions for the fields that + // were initialized with runtime values, and have omitted instructions that would have + // initialized fields with comptime values. + // So now we must clean up this situation. If it turns out the struct initialization can + // be a comptime value, overwrite ConstPtrMutInfer with ConstPtrMutComptimeConst. + // Otherwise, we must emit instructions to runtime-initialize the fields that have + // comptime-known values. + + for (size_t i = 0; i < instr_field_count; i += 1) { + IrInstSrcContainerInitFieldsField *field = &fields[i]; + + IrInstGen *field_result_loc = field->result_loc->child; + if (type_is_invalid(field_result_loc->value->type)) + return ira->codegen->invalid_inst_gen; + + TypeStructField *type_field = find_struct_type_field(container_type, field->name); + if (!type_field) { + ir_add_error_node(ira, field->source_node, + buf_sprintf("no field named '%s' in struct '%s'", + buf_ptr(field->name), buf_ptr(&container_type->name))); + return ira->codegen->invalid_inst_gen; + } + + if (type_is_invalid(type_field->type_entry)) + return ira->codegen->invalid_inst_gen; + + size_t field_index = type_field->src_index; + AstNode *existing_assign_node = field_assign_nodes[field_index]; + if (existing_assign_node) { + ErrorMsg *msg = ir_add_error_node(ira, field->source_node, buf_sprintf("duplicate field")); + add_error_note(ira->codegen, msg, existing_assign_node, buf_sprintf("other field here")); + return ira->codegen->invalid_inst_gen; + } + field_assign_nodes[field_index] = field->source_node; + + if (instr_is_comptime(field_result_loc) && + field_result_loc->value->data.x_ptr.mut != ConstPtrMutRuntimeVar) + { + const_ptrs.append(field_result_loc); + } else { + first_non_const_instruction = field_result_loc; + } + } + + bool any_missing = false; + for (size_t i = 0; i < actual_field_count; i += 1) { + if (field_assign_nodes[i] != nullptr) continue; + + // look for a default field value + TypeStructField *field = container_type->data.structure.fields[i]; + memoize_field_init_val(ira->codegen, container_type, field); + if (field->init_val == nullptr) { + ir_add_error(ira, source_instr, + buf_sprintf("missing field: '%s'", buf_ptr(field->name))); + any_missing = true; + continue; + } + if (type_is_invalid(field->init_val->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *runtime_inst = ir_const(ira, source_instr, field->init_val->type); + copy_const_val(ira->codegen, runtime_inst->value, field->init_val); + + IrInstGen *field_ptr = ir_analyze_struct_field_ptr(ira, source_instr, field, result_loc, + container_type, true); + ir_analyze_store_ptr(ira, source_instr, field_ptr, runtime_inst, false); + if (instr_is_comptime(field_ptr) && field_ptr->value->data.x_ptr.mut != ConstPtrMutRuntimeVar) { + const_ptrs.append(field_ptr); + } else { + first_non_const_instruction = result_loc; + } + } + if (any_missing) + return ira->codegen->invalid_inst_gen; + + if (result_loc->value->data.x_ptr.mut == ConstPtrMutInfer) { + if (const_ptrs.length != actual_field_count) { + result_loc->value->special = ConstValSpecialRuntime; + for (size_t i = 0; i < const_ptrs.length; i += 1) { + IrInstGen *field_result_loc = const_ptrs.at(i); + IrInstGen *deref = ir_get_deref(ira, &field_result_loc->base, field_result_loc, nullptr); + field_result_loc->value->special = ConstValSpecialRuntime; + ir_analyze_store_ptr(ira, &field_result_loc->base, field_result_loc, deref, false); + } + } + } + + IrInstGen *result = ir_get_deref(ira, source_instr, result_loc, nullptr); + + if (is_comptime && !instr_is_comptime(result)) { + ir_add_error_node(ira, first_non_const_instruction->base.source_node, + buf_sprintf("unable to evaluate constant expression")); + return ira->codegen->invalid_inst_gen; + } + + return result; +} + +static IrInstGen *ir_analyze_instruction_container_init_list(IrAnalyze *ira, + IrInstSrcContainerInitList *instruction) +{ + ir_assert(instruction->result_loc != nullptr, &instruction->base.base); + IrInstGen *result_loc = instruction->result_loc->child; + if (type_is_invalid(result_loc->value->type)) + return result_loc; + + ir_assert(result_loc->value->type->id == ZigTypeIdPointer, &instruction->base.base); + if (result_loc->value->type->data.pointer.is_const) { + ir_add_error(ira, &instruction->base.base, buf_sprintf("cannot assign to constant")); + return ira->codegen->invalid_inst_gen; + } + + ZigType *container_type = result_loc->value->type->data.pointer.child_type; + size_t elem_count = instruction->item_count; + + if (is_slice(container_type)) { + ir_add_error_node(ira, instruction->init_array_type_source_node, + buf_sprintf("array literal requires address-of operator to coerce to slice type '%s'", + buf_ptr(&container_type->name))); + return ira->codegen->invalid_inst_gen; + } + + if (container_type->id == ZigTypeIdVoid) { + if (elem_count != 0) { + ir_add_error_node(ira, instruction->base.base.source_node, + buf_sprintf("void expression expects no arguments")); + return ira->codegen->invalid_inst_gen; + } + return ir_const_void(ira, &instruction->base.base); + } + + if (container_type->id == ZigTypeIdStruct && elem_count == 0) { + ir_assert(instruction->result_loc != nullptr, &instruction->base.base); + IrInstGen *result_loc = instruction->result_loc->child; + if (type_is_invalid(result_loc->value->type)) + return result_loc; + return ir_analyze_container_init_fields(ira, &instruction->base.base, container_type, 0, nullptr, result_loc); + } + + if (container_type->id == ZigTypeIdArray) { + ZigType *child_type = container_type->data.array.child_type; + if (container_type->data.array.len != elem_count) { + ZigType *literal_type = get_array_type(ira->codegen, child_type, elem_count, nullptr); + + ir_add_error(ira, &instruction->base.base, + buf_sprintf("expected %s literal, found %s literal", + buf_ptr(&container_type->name), buf_ptr(&literal_type->name))); + return ira->codegen->invalid_inst_gen; + } + } else if (container_type->id == ZigTypeIdStruct && + container_type->data.structure.resolve_status == ResolveStatusBeingInferred) + { + // We're now done inferring the type. + container_type->data.structure.resolve_status = ResolveStatusUnstarted; + } else if (container_type->id == ZigTypeIdVector) { + // OK + } else { + ir_add_error(ira, &instruction->base.base, + buf_sprintf("type '%s' does not support array initialization", + buf_ptr(&container_type->name))); + return ira->codegen->invalid_inst_gen; + } + + switch (type_has_one_possible_value(ira->codegen, container_type)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_inst_gen; + case OnePossibleValueYes: + return ir_const_move(ira, &instruction->base.base, + get_the_one_possible_value(ira->codegen, container_type)); + case OnePossibleValueNo: + break; + } + + bool is_comptime; + switch (type_requires_comptime(ira->codegen, container_type)) { + case ReqCompTimeInvalid: + return ira->codegen->invalid_inst_gen; + case ReqCompTimeNo: + is_comptime = ir_should_inline(ira->old_irb.exec, instruction->base.base.scope); + break; + case ReqCompTimeYes: + is_comptime = true; + break; + } + + IrInstGen *first_non_const_instruction = nullptr; + + // The Result Location Mechanism has already emitted runtime instructions to + // initialize runtime elements and has omitted instructions for the comptime + // elements. However it is only now that we find out whether the array initialization + // can be a comptime value. So we must clean up the situation. If it turns out + // array initialization can be a comptime value, overwrite ConstPtrMutInfer with + // ConstPtrMutComptimeConst. Otherwise, emit instructions to runtime-initialize the + // elements that have comptime-known values. + ZigList const_ptrs = {}; + + for (size_t i = 0; i < elem_count; i += 1) { + IrInstGen *elem_result_loc = instruction->elem_result_loc_list[i]->child; + if (type_is_invalid(elem_result_loc->value->type)) + return ira->codegen->invalid_inst_gen; + + assert(elem_result_loc->value->type->id == ZigTypeIdPointer); + + if (instr_is_comptime(elem_result_loc) && + elem_result_loc->value->data.x_ptr.mut != ConstPtrMutRuntimeVar) + { + const_ptrs.append(elem_result_loc); + } else { + first_non_const_instruction = elem_result_loc; + } + } + + if (result_loc->value->data.x_ptr.mut == ConstPtrMutInfer) { + if (const_ptrs.length != elem_count) { + result_loc->value->special = ConstValSpecialRuntime; + for (size_t i = 0; i < const_ptrs.length; i += 1) { + IrInstGen *elem_result_loc = const_ptrs.at(i); + assert(elem_result_loc->value->special == ConstValSpecialStatic); + if (elem_result_loc->value->type->data.pointer.inferred_struct_field != nullptr) { + // This field will be generated comptime; no need to do this. + continue; + } + IrInstGen *deref = ir_get_deref(ira, &elem_result_loc->base, elem_result_loc, nullptr); + elem_result_loc->value->special = ConstValSpecialRuntime; + ir_analyze_store_ptr(ira, &elem_result_loc->base, elem_result_loc, deref, false); + } + } + } + + const_ptrs.deinit(); + + IrInstGen *result = ir_get_deref(ira, &instruction->base.base, result_loc, nullptr); + // If the result is a tuple, we are allowed to return a struct that uses ConstValSpecialRuntime fields at comptime. + if (instr_is_comptime(result) || is_tuple(container_type)) + return result; + + if (is_comptime) { + ir_add_error(ira, &first_non_const_instruction->base, + buf_sprintf("unable to evaluate constant expression")); + return ira->codegen->invalid_inst_gen; + } + + ZigType *result_elem_type = result_loc->value->type->data.pointer.child_type; + if (is_slice(result_elem_type)) { + ErrorMsg *msg = ir_add_error(ira, &instruction->base.base, + buf_sprintf("runtime-initialized array cannot be casted to slice type '%s'", + buf_ptr(&result_elem_type->name))); + add_error_note(ira->codegen, msg, first_non_const_instruction->base.source_node, + buf_sprintf("this value is not comptime-known")); + return ira->codegen->invalid_inst_gen; + } + return result; +} + +static IrInstGen *ir_analyze_instruction_container_init_fields(IrAnalyze *ira, + IrInstSrcContainerInitFields *instruction) +{ + ir_assert(instruction->result_loc != nullptr, &instruction->base.base); + IrInstGen *result_loc = instruction->result_loc->child; + if (type_is_invalid(result_loc->value->type)) + return result_loc; + + ir_assert(result_loc->value->type->id == ZigTypeIdPointer, &instruction->base.base); + if (result_loc->value->type->data.pointer.is_const) { + ir_add_error(ira, &instruction->base.base, buf_sprintf("cannot assign to constant")); + return ira->codegen->invalid_inst_gen; + } + + ZigType *container_type = result_loc->value->type->data.pointer.child_type; + + return ir_analyze_container_init_fields(ira, &instruction->base.base, container_type, + instruction->field_count, instruction->fields, result_loc); +} + +static IrInstGen *ir_analyze_instruction_compile_err(IrAnalyze *ira, IrInstSrcCompileErr *instruction) { + IrInstGen *msg_value = instruction->msg->child; + Buf *msg_buf = ir_resolve_str(ira, msg_value); + if (!msg_buf) + return ira->codegen->invalid_inst_gen; + + ir_add_error(ira, &instruction->base.base, msg_buf); + + return ira->codegen->invalid_inst_gen; +} + +static IrInstGen *ir_analyze_instruction_compile_log(IrAnalyze *ira, IrInstSrcCompileLog *instruction) { + Buf buf = BUF_INIT; + fprintf(stderr, "| "); + for (size_t i = 0; i < instruction->msg_count; i += 1) { + IrInstGen *msg = instruction->msg_list[i]->child; + if (type_is_invalid(msg->value->type)) + return ira->codegen->invalid_inst_gen; + buf_resize(&buf, 0); + if (msg->value->special == ConstValSpecialLazy) { + // Resolve any lazy value that's passed, we need its value + if (ir_resolve_lazy(ira->codegen, msg->base.source_node, msg->value)) + return ira->codegen->invalid_inst_gen; + } + render_const_value(ira->codegen, &buf, msg->value); + const char *comma_str = (i != 0) ? ", " : ""; + fprintf(stderr, "%s%s", comma_str, buf_ptr(&buf)); + } + fprintf(stderr, "\n"); + + auto *expr = &instruction->base.base.source_node->data.fn_call_expr; + if (!expr->seen) { + // Here we bypass higher level functions such as ir_add_error because we do not want + // invalidate_exec to be called. + add_node_error(ira->codegen, instruction->base.base.source_node, buf_sprintf("found compile log statement")); + } + expr->seen = true; + + return ir_const_void(ira, &instruction->base.base); +} + +static IrInstGen *ir_analyze_instruction_err_name(IrAnalyze *ira, IrInstSrcErrName *instruction) { + IrInstGen *value = instruction->value->child; + if (type_is_invalid(value->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *casted_value = ir_implicit_cast(ira, value, ira->codegen->builtin_types.entry_global_error_set); + if (type_is_invalid(casted_value->value->type)) + return ira->codegen->invalid_inst_gen; + + ZigType *u8_ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, + true, false, PtrLenUnknown, 0, 0, 0, false); + ZigType *str_type = get_slice_type(ira->codegen, u8_ptr_type); + if (instr_is_comptime(casted_value)) { + ZigValue *val = ir_resolve_const(ira, casted_value, UndefBad); + if (val == nullptr) + return ira->codegen->invalid_inst_gen; + ErrorTableEntry *err = casted_value->value->data.x_err_set; + if (!err->cached_error_name_val) { + ZigValue *array_val = create_const_str_lit(ira->codegen, &err->name)->data.x_ptr.data.ref.pointee; + err->cached_error_name_val = create_const_slice(ira->codegen, array_val, 0, buf_len(&err->name), true); + } + IrInstGen *result = ir_const(ira, &instruction->base.base, nullptr); + copy_const_val(ira->codegen, result->value, err->cached_error_name_val); + result->value->type = str_type; + return result; + } + + ira->codegen->generate_error_name_table = true; + + return ir_build_err_name_gen(ira, &instruction->base.base, value, str_type); +} + +static IrInstGen *ir_analyze_instruction_enum_tag_name(IrAnalyze *ira, IrInstSrcTagName *instruction) { + Error err; + IrInstGen *target = instruction->target->child; + if (type_is_invalid(target->value->type)) + return ira->codegen->invalid_inst_gen; + + if (target->value->type->id == ZigTypeIdEnumLiteral) { + IrInstGen *result = ir_const(ira, &instruction->base.base, nullptr); + Buf *field_name = target->value->data.x_enum_literal; + ZigValue *array_val = create_const_str_lit(ira->codegen, field_name)->data.x_ptr.data.ref.pointee; + init_const_slice(ira->codegen, result->value, array_val, 0, buf_len(field_name), true); + return result; + } + + if (target->value->type->id == ZigTypeIdUnion) { + target = ir_analyze_union_tag(ira, &instruction->base.base, target, instruction->base.is_gen); + if (type_is_invalid(target->value->type)) + return ira->codegen->invalid_inst_gen; + } + + if (target->value->type->id != ZigTypeIdEnum) { + ir_add_error(ira, &target->base, + buf_sprintf("expected enum tag, found '%s'", buf_ptr(&target->value->type->name))); + return ira->codegen->invalid_inst_gen; + } + + if (target->value->type->data.enumeration.src_field_count == 1 && + !target->value->type->data.enumeration.non_exhaustive) { + TypeEnumField *only_field = &target->value->type->data.enumeration.fields[0]; + ZigValue *array_val = create_const_str_lit(ira->codegen, only_field->name)->data.x_ptr.data.ref.pointee; + IrInstGen *result = ir_const(ira, &instruction->base.base, nullptr); + init_const_slice(ira->codegen, result->value, array_val, 0, buf_len(only_field->name), true); + return result; + } + + if (instr_is_comptime(target)) { + if ((err = type_resolve(ira->codegen, target->value->type, ResolveStatusZeroBitsKnown))) + return ira->codegen->invalid_inst_gen; + TypeEnumField *field = find_enum_field_by_tag(target->value->type, &target->value->data.x_bigint); + if (field == nullptr) { + Buf *int_buf = buf_alloc(); + bigint_append_buf(int_buf, &target->value->data.x_bigint, 10); + + ir_add_error(ira, &target->base, + buf_sprintf("no tag by value %s", buf_ptr(int_buf))); + return ira->codegen->invalid_inst_gen; + } + ZigValue *array_val = create_const_str_lit(ira->codegen, field->name)->data.x_ptr.data.ref.pointee; + IrInstGen *result = ir_const(ira, &instruction->base.base, nullptr); + init_const_slice(ira->codegen, result->value, array_val, 0, buf_len(field->name), true); + return result; + } + + ZigType *u8_ptr_type = get_pointer_to_type_extra( + ira->codegen, ira->codegen->builtin_types.entry_u8, + true, false, PtrLenUnknown, + 0, 0, 0, false); + ZigType *result_type = get_slice_type(ira->codegen, u8_ptr_type); + return ir_build_tag_name_gen(ira, &instruction->base.base, target, result_type); +} + +static IrInstGen *ir_analyze_instruction_field_parent_ptr(IrAnalyze *ira, + IrInstSrcFieldParentPtr *instruction) +{ + Error err; + IrInstGen *type_value = instruction->type_value->child; + ZigType *container_type = ir_resolve_type(ira, type_value); + if (type_is_invalid(container_type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *field_name_value = instruction->field_name->child; + Buf *field_name = ir_resolve_str(ira, field_name_value); + if (!field_name) + return ira->codegen->invalid_inst_gen; + + IrInstGen *field_ptr = instruction->field_ptr->child; + if (type_is_invalid(field_ptr->value->type)) + return ira->codegen->invalid_inst_gen; + + if (container_type->id != ZigTypeIdStruct) { + ir_add_error(ira, &type_value->base, + buf_sprintf("expected struct type, found '%s'", buf_ptr(&container_type->name))); + return ira->codegen->invalid_inst_gen; + } + + if ((err = type_resolve(ira->codegen, container_type, ResolveStatusSizeKnown))) + return ira->codegen->invalid_inst_gen; + + TypeStructField *field = find_struct_type_field(container_type, field_name); + if (field == nullptr) { + ir_add_error(ira, &field_name_value->base, + buf_sprintf("struct '%s' has no field '%s'", + buf_ptr(&container_type->name), buf_ptr(field_name))); + return ira->codegen->invalid_inst_gen; + } + + if (field_ptr->value->type->id != ZigTypeIdPointer) { + ir_add_error(ira, &field_ptr->base, + buf_sprintf("expected pointer, found '%s'", buf_ptr(&field_ptr->value->type->name))); + return ira->codegen->invalid_inst_gen; + } + + bool is_packed = (container_type->data.structure.layout == ContainerLayoutPacked); + uint32_t field_ptr_align = is_packed ? 1 : get_abi_alignment(ira->codegen, field->type_entry); + uint32_t parent_ptr_align = is_packed ? 1 : get_abi_alignment(ira->codegen, container_type); + + ZigType *field_ptr_type = get_pointer_to_type_extra(ira->codegen, field->type_entry, + field_ptr->value->type->data.pointer.is_const, + field_ptr->value->type->data.pointer.is_volatile, + PtrLenSingle, + field_ptr_align, 0, 0, false); + IrInstGen *casted_field_ptr = ir_implicit_cast(ira, field_ptr, field_ptr_type); + if (type_is_invalid(casted_field_ptr->value->type)) + return ira->codegen->invalid_inst_gen; + + ZigType *result_type = get_pointer_to_type_extra(ira->codegen, container_type, + casted_field_ptr->value->type->data.pointer.is_const, + casted_field_ptr->value->type->data.pointer.is_volatile, + PtrLenSingle, + parent_ptr_align, 0, 0, false); + + if (instr_is_comptime(casted_field_ptr)) { + ZigValue *field_ptr_val = ir_resolve_const(ira, casted_field_ptr, UndefBad); + if (!field_ptr_val) + return ira->codegen->invalid_inst_gen; + + if (field_ptr_val->data.x_ptr.special != ConstPtrSpecialBaseStruct) { + ir_add_error(ira, &field_ptr->base, buf_sprintf("pointer value not based on parent struct")); + return ira->codegen->invalid_inst_gen; + } + + size_t ptr_field_index = field_ptr_val->data.x_ptr.data.base_struct.field_index; + if (ptr_field_index != field->src_index) { + ir_add_error(ira, &instruction->base.base, + buf_sprintf("field '%s' has index %" ZIG_PRI_usize " but pointer value is index %" ZIG_PRI_usize " of struct '%s'", + buf_ptr(field->name), field->src_index, + ptr_field_index, buf_ptr(&container_type->name))); + return ira->codegen->invalid_inst_gen; + } + + IrInstGen *result = ir_const(ira, &instruction->base.base, result_type); + ZigValue *out_val = result->value; + out_val->data.x_ptr.special = ConstPtrSpecialRef; + out_val->data.x_ptr.data.ref.pointee = field_ptr_val->data.x_ptr.data.base_struct.struct_val; + out_val->data.x_ptr.mut = field_ptr_val->data.x_ptr.mut; + return result; + } + + return ir_build_field_parent_ptr_gen(ira, &instruction->base.base, casted_field_ptr, field, result_type); +} + +static TypeStructField *validate_byte_offset(IrAnalyze *ira, + IrInstGen *type_value, + IrInstGen *field_name_value, + size_t *byte_offset) +{ + ZigType *container_type = ir_resolve_type(ira, type_value); + if (type_is_invalid(container_type)) + return nullptr; + + Error err; + if ((err = type_resolve(ira->codegen, container_type, ResolveStatusSizeKnown))) + return nullptr; + + Buf *field_name = ir_resolve_str(ira, field_name_value); + if (!field_name) + return nullptr; + + if (container_type->id != ZigTypeIdStruct) { + ir_add_error(ira, &type_value->base, + buf_sprintf("expected struct type, found '%s'", buf_ptr(&container_type->name))); + return nullptr; + } + + TypeStructField *field = find_struct_type_field(container_type, field_name); + if (field == nullptr) { + ir_add_error(ira, &field_name_value->base, + buf_sprintf("struct '%s' has no field '%s'", + buf_ptr(&container_type->name), buf_ptr(field_name))); + return nullptr; + } + + if (!type_has_bits(ira->codegen, field->type_entry)) { + ir_add_error(ira, &field_name_value->base, + buf_sprintf("zero-bit field '%s' in struct '%s' has no offset", + buf_ptr(field_name), buf_ptr(&container_type->name))); + return nullptr; + } + + *byte_offset = field->offset; + return field; +} + +static IrInstGen *ir_analyze_instruction_byte_offset_of(IrAnalyze *ira, IrInstSrcByteOffsetOf *instruction) { + IrInstGen *type_value = instruction->type_value->child; + if (type_is_invalid(type_value->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *field_name_value = instruction->field_name->child; + size_t byte_offset = 0; + if (!validate_byte_offset(ira, type_value, field_name_value, &byte_offset)) + return ira->codegen->invalid_inst_gen; + + + return ir_const_unsigned(ira, &instruction->base.base, byte_offset); +} + +static IrInstGen *ir_analyze_instruction_bit_offset_of(IrAnalyze *ira, IrInstSrcBitOffsetOf *instruction) { + IrInstGen *type_value = instruction->type_value->child; + if (type_is_invalid(type_value->value->type)) + return ira->codegen->invalid_inst_gen; + IrInstGen *field_name_value = instruction->field_name->child; + size_t byte_offset = 0; + TypeStructField *field = nullptr; + if (!(field = validate_byte_offset(ira, type_value, field_name_value, &byte_offset))) + return ira->codegen->invalid_inst_gen; + + size_t bit_offset = byte_offset * 8 + field->bit_offset_in_host; + return ir_const_unsigned(ira, &instruction->base.base, bit_offset); +} + +static void ensure_field_index(ZigType *type, const char *field_name, size_t index) { + Buf *field_name_buf; + + assert(type != nullptr && !type_is_invalid(type)); + field_name_buf = buf_create_from_str(field_name); + TypeStructField *field = find_struct_type_field(type, field_name_buf); + buf_deinit(field_name_buf); + + if (field == nullptr || field->src_index != index) + zig_panic("reference to unknown field %s", field_name); +} + +static ZigType *ir_type_info_get_type(IrAnalyze *ira, const char *type_name, ZigType *root) { + Error err; + ZigType *type_info_type = get_builtin_type(ira->codegen, "TypeInfo"); + assert(type_info_type->id == ZigTypeIdUnion); + if ((err = type_resolve(ira->codegen, type_info_type, ResolveStatusSizeKnown))) { + zig_unreachable(); + } + + if (type_name == nullptr && root == nullptr) + return type_info_type; + else if (type_name == nullptr) + return root; + + ZigType *root_type = (root == nullptr) ? type_info_type : root; + + ScopeDecls *type_info_scope = get_container_scope(root_type); + assert(type_info_scope != nullptr); + + Buf field_name = BUF_INIT; + buf_init_from_str(&field_name, type_name); + auto entry = type_info_scope->decl_table.get(&field_name); + buf_deinit(&field_name); + + TldVar *tld = (TldVar *)entry; + assert(tld->base.id == TldIdVar); + + ZigVar *var = tld->var; + + assert(var->const_value->type->id == ZigTypeIdMetaType); + + return ir_resolve_const_type(ira->codegen, ira->new_irb.exec, nullptr, var->const_value); +} + +static Error ir_make_type_info_decls(IrAnalyze *ira, IrInst* source_instr, ZigValue *out_val, + ScopeDecls *decls_scope, bool resolve_types) +{ + Error err; + ZigType *type_info_declaration_type = ir_type_info_get_type(ira, "Declaration", nullptr); + if ((err = type_resolve(ira->codegen, type_info_declaration_type, ResolveStatusSizeKnown))) + return err; + + ensure_field_index(type_info_declaration_type, "name", 0); + ensure_field_index(type_info_declaration_type, "is_pub", 1); + ensure_field_index(type_info_declaration_type, "data", 2); + + if (!resolve_types) { + ZigType *ptr_type = get_pointer_to_type_extra(ira->codegen, type_info_declaration_type, + false, false, PtrLenUnknown, 0, 0, 0, false); + + out_val->special = ConstValSpecialLazy; + out_val->type = get_slice_type(ira->codegen, ptr_type); + + LazyValueTypeInfoDecls *lazy_type_info_decls = heap::c_allocator.create(); + lazy_type_info_decls->ira = ira; ira_ref(ira); + out_val->data.x_lazy = &lazy_type_info_decls->base; + lazy_type_info_decls->base.id = LazyValueIdTypeInfoDecls; + + lazy_type_info_decls->source_instr = source_instr; + lazy_type_info_decls->decls_scope = decls_scope; + + return ErrorNone; + } + + ZigType *type_info_declaration_data_type = ir_type_info_get_type(ira, "Data", type_info_declaration_type); + if ((err = type_resolve(ira->codegen, type_info_declaration_data_type, ResolveStatusSizeKnown))) + return err; + + ZigType *type_info_fn_decl_type = ir_type_info_get_type(ira, "FnDecl", type_info_declaration_data_type); + if ((err = type_resolve(ira->codegen, type_info_fn_decl_type, ResolveStatusSizeKnown))) + return err; + + ZigType *type_info_fn_decl_inline_type = ir_type_info_get_type(ira, "Inline", type_info_fn_decl_type); + if ((err = type_resolve(ira->codegen, type_info_fn_decl_inline_type, ResolveStatusSizeKnown))) + return err; + + // The unresolved declarations are collected in a separate queue to avoid + // modifying decl_table while iterating over it + ZigList resolve_decl_queue{}; + + auto decl_it = decls_scope->decl_table.entry_iterator(); + decltype(decls_scope->decl_table)::Entry *curr_entry = nullptr; + while ((curr_entry = decl_it.next()) != nullptr) { + if (curr_entry->value->resolution == TldResolutionInvalid) { + return ErrorSemanticAnalyzeFail; + } + + if (curr_entry->value->resolution == TldResolutionResolving) { + ir_error_dependency_loop(ira, source_instr); + return ErrorSemanticAnalyzeFail; + } + + // If the declaration is unresolved, force it to be resolved again. + if (curr_entry->value->resolution == TldResolutionUnresolved) + resolve_decl_queue.append(curr_entry->value); + } + + for (size_t i = 0; i < resolve_decl_queue.length; i++) { + Tld *decl = resolve_decl_queue.at(i); + resolve_top_level_decl(ira->codegen, decl, decl->source_node, false); + if (decl->resolution == TldResolutionInvalid) { + return ErrorSemanticAnalyzeFail; + } + } + + resolve_decl_queue.deinit(); + + // Loop through our declarations once to figure out how many declarations we will generate info for. + int declaration_count = 0; + decl_it = decls_scope->decl_table.entry_iterator(); + while ((curr_entry = decl_it.next()) != nullptr) { + // Skip comptime blocks and test functions. + if (curr_entry->value->id == TldIdCompTime) + continue; + + if (curr_entry->value->id == TldIdFn) { + ZigFn *fn_entry = ((TldFn *)curr_entry->value)->fn_entry; + if (fn_entry->is_test) + continue; + } + + declaration_count += 1; + } + + ZigValue *declaration_array = ira->codegen->pass1_arena->create(); + declaration_array->special = ConstValSpecialStatic; + declaration_array->type = get_array_type(ira->codegen, type_info_declaration_type, declaration_count, nullptr); + declaration_array->data.x_array.special = ConstArraySpecialNone; + declaration_array->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(declaration_count); + init_const_slice(ira->codegen, out_val, declaration_array, 0, declaration_count, false); + + // Loop through the declarations and generate info. + decl_it = decls_scope->decl_table.entry_iterator(); + curr_entry = nullptr; + int declaration_index = 0; + while ((curr_entry = decl_it.next()) != nullptr) { + // Skip comptime blocks and test functions. + if (curr_entry->value->id == TldIdCompTime) { + continue; + } else if (curr_entry->value->id == TldIdFn) { + ZigFn *fn_entry = ((TldFn *)curr_entry->value)->fn_entry; + if (fn_entry->is_test) + continue; + } + + ZigValue *declaration_val = &declaration_array->data.x_array.data.s_none.elements[declaration_index]; + + declaration_val->special = ConstValSpecialStatic; + declaration_val->type = type_info_declaration_type; + + ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 3); + ZigValue *name = create_const_str_lit(ira->codegen, curr_entry->key)->data.x_ptr.data.ref.pointee; + init_const_slice(ira->codegen, inner_fields[0], name, 0, buf_len(curr_entry->key), true); + inner_fields[1]->special = ConstValSpecialStatic; + inner_fields[1]->type = ira->codegen->builtin_types.entry_bool; + inner_fields[1]->data.x_bool = curr_entry->value->visib_mod == VisibModPub; + inner_fields[2]->special = ConstValSpecialStatic; + inner_fields[2]->type = type_info_declaration_data_type; + inner_fields[2]->parent.id = ConstParentIdStruct; + inner_fields[2]->parent.data.p_struct.struct_val = declaration_val; + inner_fields[2]->parent.data.p_struct.field_index = 1; + + switch (curr_entry->value->id) { + case TldIdVar: + { + ZigVar *var = ((TldVar *)curr_entry->value)->var; + assert(var != nullptr); + + if ((err = type_resolve(ira->codegen, var->const_value->type, ResolveStatusSizeKnown))) + return ErrorSemanticAnalyzeFail; + + if (var->const_value->type->id == ZigTypeIdMetaType) { + // We have a variable of type 'type', so it's actually a type declaration. + // 0: Data.Type: type + bigint_init_unsigned(&inner_fields[2]->data.x_union.tag, 0); + inner_fields[2]->data.x_union.payload = var->const_value; + } else { + // We have a variable of another type, so we store the type of the variable. + // 1: Data.Var: type + bigint_init_unsigned(&inner_fields[2]->data.x_union.tag, 1); + + ZigValue *payload = ira->codegen->pass1_arena->create(); + payload->special = ConstValSpecialStatic; + payload->type = ira->codegen->builtin_types.entry_type; + payload->data.x_type = var->const_value->type; + + inner_fields[2]->data.x_union.payload = payload; + } + + break; + } + case TldIdFn: + { + // 2: Data.Fn: Data.FnDecl + bigint_init_unsigned(&inner_fields[2]->data.x_union.tag, 2); + + ZigFn *fn_entry = ((TldFn *)curr_entry->value)->fn_entry; + assert(!fn_entry->is_test); + assert(fn_entry->type_entry != nullptr); + + AstNodeFnProto *fn_node = &fn_entry->proto_node->data.fn_proto; + + ZigValue *fn_decl_val = ira->codegen->pass1_arena->create(); + fn_decl_val->special = ConstValSpecialStatic; + fn_decl_val->type = type_info_fn_decl_type; + fn_decl_val->parent.id = ConstParentIdUnion; + fn_decl_val->parent.data.p_union.union_val = inner_fields[2]; + + ZigValue **fn_decl_fields = alloc_const_vals_ptrs(ira->codegen, 9); + fn_decl_val->data.x_struct.fields = fn_decl_fields; + + // fn_type: type + ensure_field_index(fn_decl_val->type, "fn_type", 0); + fn_decl_fields[0]->special = ConstValSpecialStatic; + fn_decl_fields[0]->type = ira->codegen->builtin_types.entry_type; + fn_decl_fields[0]->data.x_type = fn_entry->type_entry; + // inline_type: Data.FnDecl.Inline + ensure_field_index(fn_decl_val->type, "inline_type", 1); + fn_decl_fields[1]->special = ConstValSpecialStatic; + fn_decl_fields[1]->type = type_info_fn_decl_inline_type; + bigint_init_unsigned(&fn_decl_fields[1]->data.x_enum_tag, fn_entry->fn_inline); + // is_var_args: bool + ensure_field_index(fn_decl_val->type, "is_var_args", 2); + bool is_varargs = fn_node->is_var_args; + fn_decl_fields[2]->special = ConstValSpecialStatic; + fn_decl_fields[2]->type = ira->codegen->builtin_types.entry_bool; + fn_decl_fields[2]->data.x_bool = is_varargs; + // is_extern: bool + ensure_field_index(fn_decl_val->type, "is_extern", 3); + fn_decl_fields[3]->special = ConstValSpecialStatic; + fn_decl_fields[3]->type = ira->codegen->builtin_types.entry_bool; + fn_decl_fields[3]->data.x_bool = fn_node->is_extern; + // is_export: bool + ensure_field_index(fn_decl_val->type, "is_export", 4); + fn_decl_fields[4]->special = ConstValSpecialStatic; + fn_decl_fields[4]->type = ira->codegen->builtin_types.entry_bool; + fn_decl_fields[4]->data.x_bool = fn_node->is_export; + // lib_name: ?[]const u8 + ensure_field_index(fn_decl_val->type, "lib_name", 5); + fn_decl_fields[5]->special = ConstValSpecialStatic; + ZigType *u8_ptr = get_pointer_to_type_extra( + ira->codegen, ira->codegen->builtin_types.entry_u8, + true, false, PtrLenUnknown, + 0, 0, 0, false); + fn_decl_fields[5]->type = get_optional_type(ira->codegen, get_slice_type(ira->codegen, u8_ptr)); + if (fn_node->is_extern && fn_node->lib_name != nullptr && buf_len(fn_node->lib_name) > 0) { + fn_decl_fields[5]->data.x_optional = ira->codegen->pass1_arena->create(); + ZigValue *lib_name = create_const_str_lit(ira->codegen, fn_node->lib_name)->data.x_ptr.data.ref.pointee; + init_const_slice(ira->codegen, fn_decl_fields[5]->data.x_optional, lib_name, 0, + buf_len(fn_node->lib_name), true); + } else { + fn_decl_fields[5]->data.x_optional = nullptr; + } + // return_type: type + ensure_field_index(fn_decl_val->type, "return_type", 6); + fn_decl_fields[6]->special = ConstValSpecialStatic; + fn_decl_fields[6]->type = ira->codegen->builtin_types.entry_type; + fn_decl_fields[6]->data.x_type = fn_entry->type_entry->data.fn.fn_type_id.return_type; + // arg_names: [][] const u8 + ensure_field_index(fn_decl_val->type, "arg_names", 7); + size_t fn_arg_count = fn_entry->variable_list.length; + ZigValue *fn_arg_name_array = ira->codegen->pass1_arena->create(); + fn_arg_name_array->special = ConstValSpecialStatic; + fn_arg_name_array->type = get_array_type(ira->codegen, + get_slice_type(ira->codegen, u8_ptr), fn_arg_count, nullptr); + fn_arg_name_array->data.x_array.special = ConstArraySpecialNone; + fn_arg_name_array->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(fn_arg_count); + + init_const_slice(ira->codegen, fn_decl_fields[7], fn_arg_name_array, 0, fn_arg_count, false); + + for (size_t fn_arg_index = 0; fn_arg_index < fn_arg_count; fn_arg_index++) { + ZigVar *arg_var = fn_entry->variable_list.at(fn_arg_index); + ZigValue *fn_arg_name_val = &fn_arg_name_array->data.x_array.data.s_none.elements[fn_arg_index]; + ZigValue *arg_name = create_const_str_lit(ira->codegen, + buf_create_from_str(arg_var->name))->data.x_ptr.data.ref.pointee; + init_const_slice(ira->codegen, fn_arg_name_val, arg_name, 0, strlen(arg_var->name), true); + fn_arg_name_val->parent.id = ConstParentIdArray; + fn_arg_name_val->parent.data.p_array.array_val = fn_arg_name_array; + fn_arg_name_val->parent.data.p_array.elem_index = fn_arg_index; + } + + inner_fields[2]->data.x_union.payload = fn_decl_val; + break; + } + case TldIdContainer: + { + ZigType *type_entry = ((TldContainer *)curr_entry->value)->type_entry; + if ((err = type_resolve(ira->codegen, type_entry, ResolveStatusSizeKnown))) + return ErrorSemanticAnalyzeFail; + + // This is a type. + bigint_init_unsigned(&inner_fields[2]->data.x_union.tag, 0); + + ZigValue *payload = ira->codegen->pass1_arena->create(); + payload->special = ConstValSpecialStatic; + payload->type = ira->codegen->builtin_types.entry_type; + payload->data.x_type = type_entry; + + inner_fields[2]->data.x_union.payload = payload; + + break; + } + default: + zig_unreachable(); + } + + declaration_val->data.x_struct.fields = inner_fields; + declaration_index += 1; + } + + assert(declaration_index == declaration_count); + return ErrorNone; +} + +static BuiltinPtrSize ptr_len_to_size_enum_index(PtrLen ptr_len) { + switch (ptr_len) { + case PtrLenSingle: + return BuiltinPtrSizeOne; + case PtrLenUnknown: + return BuiltinPtrSizeMany; + case PtrLenC: + return BuiltinPtrSizeC; + } + zig_unreachable(); +} + +static PtrLen size_enum_index_to_ptr_len(BuiltinPtrSize size_enum_index) { + switch (size_enum_index) { + case BuiltinPtrSizeOne: + return PtrLenSingle; + case BuiltinPtrSizeMany: + case BuiltinPtrSizeSlice: + return PtrLenUnknown; + case BuiltinPtrSizeC: + return PtrLenC; + } + zig_unreachable(); +} + +static ZigValue *create_ptr_like_type_info(IrAnalyze *ira, IrInst *source_instr, ZigType *ptr_type_entry) { + ZigType *attrs_type; + BuiltinPtrSize size_enum_index; + if (is_slice(ptr_type_entry)) { + TypeStructField *ptr_field = ptr_type_entry->data.structure.fields[slice_ptr_index]; + attrs_type = resolve_struct_field_type(ira->codegen, ptr_field); + size_enum_index = BuiltinPtrSizeSlice; + } else if (ptr_type_entry->id == ZigTypeIdPointer) { + attrs_type = ptr_type_entry; + size_enum_index = ptr_len_to_size_enum_index(ptr_type_entry->data.pointer.ptr_len); + } else { + zig_unreachable(); + } + + ZigType *type_info_pointer_type = ir_type_info_get_type(ira, "Pointer", nullptr); + assertNoError(type_resolve(ira->codegen, type_info_pointer_type, ResolveStatusSizeKnown)); + + ZigValue *result = ira->codegen->pass1_arena->create(); + result->special = ConstValSpecialStatic; + result->type = type_info_pointer_type; + + ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 7); + result->data.x_struct.fields = fields; + + // size: Size + ensure_field_index(result->type, "size", 0); + ZigType *type_info_pointer_size_type = ir_type_info_get_type(ira, "Size", type_info_pointer_type); + assertNoError(type_resolve(ira->codegen, type_info_pointer_size_type, ResolveStatusSizeKnown)); + fields[0]->special = ConstValSpecialStatic; + fields[0]->type = type_info_pointer_size_type; + bigint_init_unsigned(&fields[0]->data.x_enum_tag, size_enum_index); + + // is_const: bool + ensure_field_index(result->type, "is_const", 1); + fields[1]->special = ConstValSpecialStatic; + fields[1]->type = ira->codegen->builtin_types.entry_bool; + fields[1]->data.x_bool = attrs_type->data.pointer.is_const; + // is_volatile: bool + ensure_field_index(result->type, "is_volatile", 2); + fields[2]->special = ConstValSpecialStatic; + fields[2]->type = ira->codegen->builtin_types.entry_bool; + fields[2]->data.x_bool = attrs_type->data.pointer.is_volatile; + // alignment: u32 + ensure_field_index(result->type, "alignment", 3); + fields[3]->type = ira->codegen->builtin_types.entry_num_lit_int; + if (attrs_type->data.pointer.explicit_alignment != 0) { + fields[3]->special = ConstValSpecialStatic; + bigint_init_unsigned(&fields[3]->data.x_bigint, attrs_type->data.pointer.explicit_alignment); + } else { + LazyValueAlignOf *lazy_align_of = heap::c_allocator.create(); + lazy_align_of->ira = ira; ira_ref(ira); + fields[3]->special = ConstValSpecialLazy; + fields[3]->data.x_lazy = &lazy_align_of->base; + lazy_align_of->base.id = LazyValueIdAlignOf; + lazy_align_of->target_type = ir_const_type(ira, source_instr, attrs_type->data.pointer.child_type); + } + // child: type + ensure_field_index(result->type, "child", 4); + fields[4]->special = ConstValSpecialStatic; + fields[4]->type = ira->codegen->builtin_types.entry_type; + fields[4]->data.x_type = attrs_type->data.pointer.child_type; + // is_allowzero: bool + ensure_field_index(result->type, "is_allowzero", 5); + fields[5]->special = ConstValSpecialStatic; + fields[5]->type = ira->codegen->builtin_types.entry_bool; + fields[5]->data.x_bool = attrs_type->data.pointer.allow_zero; + // sentinel: anytype + ensure_field_index(result->type, "sentinel", 6); + fields[6]->special = ConstValSpecialStatic; + if (attrs_type->data.pointer.sentinel != nullptr) { + fields[6]->type = get_optional_type(ira->codegen, attrs_type->data.pointer.child_type); + set_optional_payload(fields[6], attrs_type->data.pointer.sentinel); + } else { + fields[6]->type = ira->codegen->builtin_types.entry_null; + } + + return result; +}; + +static void make_enum_field_val(IrAnalyze *ira, ZigValue *enum_field_val, TypeEnumField *enum_field, + ZigType *type_info_enum_field_type) +{ + enum_field_val->special = ConstValSpecialStatic; + enum_field_val->type = type_info_enum_field_type; + + ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 2); + inner_fields[1]->special = ConstValSpecialStatic; + inner_fields[1]->type = ira->codegen->builtin_types.entry_num_lit_int; + + ZigValue *name = create_const_str_lit(ira->codegen, enum_field->name)->data.x_ptr.data.ref.pointee; + init_const_slice(ira->codegen, inner_fields[0], name, 0, buf_len(enum_field->name), true); + + bigint_init_bigint(&inner_fields[1]->data.x_bigint, &enum_field->value); + + enum_field_val->data.x_struct.fields = inner_fields; +} + +static Error ir_make_type_info_value(IrAnalyze *ira, IrInst* source_instr, ZigType *type_entry, + ZigValue **out) +{ + Error err; + assert(type_entry != nullptr); + assert(!type_is_invalid(type_entry)); + + auto entry = ira->codegen->type_info_cache.maybe_get(type_entry); + if (entry != nullptr) { + *out = entry->value; + return ErrorNone; + } + + ZigValue *result = nullptr; + switch (type_entry->id) { + case ZigTypeIdInvalid: + zig_unreachable(); + case ZigTypeIdMetaType: + case ZigTypeIdVoid: + case ZigTypeIdBool: + case ZigTypeIdUnreachable: + case ZigTypeIdComptimeFloat: + case ZigTypeIdComptimeInt: + case ZigTypeIdEnumLiteral: + case ZigTypeIdUndefined: + case ZigTypeIdNull: + case ZigTypeIdOpaque: + result = ira->codegen->intern.for_void(); + break; + case ZigTypeIdInt: + { + result = ira->codegen->pass1_arena->create(); + result->special = ConstValSpecialStatic; + result->type = ir_type_info_get_type(ira, "Int", nullptr); + + ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 2); + result->data.x_struct.fields = fields; + + // is_signed: bool + ensure_field_index(result->type, "is_signed", 0); + fields[0]->special = ConstValSpecialStatic; + fields[0]->type = ira->codegen->builtin_types.entry_bool; + fields[0]->data.x_bool = type_entry->data.integral.is_signed; + // bits: u8 + ensure_field_index(result->type, "bits", 1); + fields[1]->special = ConstValSpecialStatic; + fields[1]->type = ira->codegen->builtin_types.entry_num_lit_int; + bigint_init_unsigned(&fields[1]->data.x_bigint, type_entry->data.integral.bit_count); + + break; + } + case ZigTypeIdFloat: + { + result = ira->codegen->pass1_arena->create(); + result->special = ConstValSpecialStatic; + result->type = ir_type_info_get_type(ira, "Float", nullptr); + + ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 1); + result->data.x_struct.fields = fields; + + // bits: u8 + ensure_field_index(result->type, "bits", 0); + fields[0]->special = ConstValSpecialStatic; + fields[0]->type = ira->codegen->builtin_types.entry_num_lit_int; + bigint_init_unsigned(&fields[0]->data.x_bigint, type_entry->data.floating.bit_count); + + break; + } + case ZigTypeIdPointer: + { + result = create_ptr_like_type_info(ira, source_instr, type_entry); + if (result == nullptr) + return ErrorSemanticAnalyzeFail; + break; + } + case ZigTypeIdArray: + { + result = ira->codegen->pass1_arena->create(); + result->special = ConstValSpecialStatic; + result->type = ir_type_info_get_type(ira, "Array", nullptr); + + ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 3); + result->data.x_struct.fields = fields; + + // len: usize + ensure_field_index(result->type, "len", 0); + fields[0]->special = ConstValSpecialStatic; + fields[0]->type = ira->codegen->builtin_types.entry_num_lit_int; + bigint_init_unsigned(&fields[0]->data.x_bigint, type_entry->data.array.len); + // child: type + ensure_field_index(result->type, "child", 1); + fields[1]->special = ConstValSpecialStatic; + fields[1]->type = ira->codegen->builtin_types.entry_type; + fields[1]->data.x_type = type_entry->data.array.child_type; + // sentinel: anytype + fields[2]->special = ConstValSpecialStatic; + fields[2]->type = get_optional_type(ira->codegen, type_entry->data.array.child_type); + fields[2]->data.x_optional = type_entry->data.array.sentinel; + break; + } + case ZigTypeIdVector: { + result = ira->codegen->pass1_arena->create(); + result->special = ConstValSpecialStatic; + result->type = ir_type_info_get_type(ira, "Vector", nullptr); + + ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 2); + result->data.x_struct.fields = fields; + + // len: usize + ensure_field_index(result->type, "len", 0); + fields[0]->special = ConstValSpecialStatic; + fields[0]->type = ira->codegen->builtin_types.entry_num_lit_int; + bigint_init_unsigned(&fields[0]->data.x_bigint, type_entry->data.vector.len); + // child: type + ensure_field_index(result->type, "child", 1); + fields[1]->special = ConstValSpecialStatic; + fields[1]->type = ira->codegen->builtin_types.entry_type; + fields[1]->data.x_type = type_entry->data.vector.elem_type; + + break; + } + case ZigTypeIdOptional: + { + result = ira->codegen->pass1_arena->create(); + result->special = ConstValSpecialStatic; + result->type = ir_type_info_get_type(ira, "Optional", nullptr); + + ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 1); + result->data.x_struct.fields = fields; + + // child: type + ensure_field_index(result->type, "child", 0); + fields[0]->special = ConstValSpecialStatic; + fields[0]->type = ira->codegen->builtin_types.entry_type; + fields[0]->data.x_type = type_entry->data.maybe.child_type; + + break; + } + case ZigTypeIdAnyFrame: { + result = ira->codegen->pass1_arena->create(); + result->special = ConstValSpecialStatic; + result->type = ir_type_info_get_type(ira, "AnyFrame", nullptr); + + ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 1); + result->data.x_struct.fields = fields; + + // child: ?type + ensure_field_index(result->type, "child", 0); + fields[0]->special = ConstValSpecialStatic; + fields[0]->type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type); + fields[0]->data.x_optional = (type_entry->data.any_frame.result_type == nullptr) ? nullptr : + create_const_type(ira->codegen, type_entry->data.any_frame.result_type); + break; + } + case ZigTypeIdEnum: + { + if ((err = type_resolve(ira->codegen, type_entry, ResolveStatusSizeKnown))) + return err; + + result = ira->codegen->pass1_arena->create(); + result->special = ConstValSpecialStatic; + result->type = ir_type_info_get_type(ira, "Enum", nullptr); + + ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 5); + result->data.x_struct.fields = fields; + + // layout: ContainerLayout + ensure_field_index(result->type, "layout", 0); + fields[0]->special = ConstValSpecialStatic; + fields[0]->type = ir_type_info_get_type(ira, "ContainerLayout", nullptr); + bigint_init_unsigned(&fields[0]->data.x_enum_tag, type_entry->data.enumeration.layout); + // tag_type: type + ensure_field_index(result->type, "tag_type", 1); + fields[1]->special = ConstValSpecialStatic; + fields[1]->type = ira->codegen->builtin_types.entry_type; + fields[1]->data.x_type = type_entry->data.enumeration.tag_int_type; + // fields: []TypeInfo.EnumField + ensure_field_index(result->type, "fields", 2); + + ZigType *type_info_enum_field_type = ir_type_info_get_type(ira, "EnumField", nullptr); + if ((err = type_resolve(ira->codegen, type_info_enum_field_type, ResolveStatusSizeKnown))) { + zig_unreachable(); + } + uint32_t enum_field_count = type_entry->data.enumeration.src_field_count; + + ZigValue *enum_field_array = ira->codegen->pass1_arena->create(); + enum_field_array->special = ConstValSpecialStatic; + enum_field_array->type = get_array_type(ira->codegen, type_info_enum_field_type, enum_field_count, nullptr); + enum_field_array->data.x_array.special = ConstArraySpecialNone; + enum_field_array->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(enum_field_count); + + init_const_slice(ira->codegen, fields[2], enum_field_array, 0, enum_field_count, false); + + for (uint32_t enum_field_index = 0; enum_field_index < enum_field_count; enum_field_index++) + { + TypeEnumField *enum_field = &type_entry->data.enumeration.fields[enum_field_index]; + ZigValue *enum_field_val = &enum_field_array->data.x_array.data.s_none.elements[enum_field_index]; + make_enum_field_val(ira, enum_field_val, enum_field, type_info_enum_field_type); + enum_field_val->parent.id = ConstParentIdArray; + enum_field_val->parent.data.p_array.array_val = enum_field_array; + enum_field_val->parent.data.p_array.elem_index = enum_field_index; + } + // decls: []TypeInfo.Declaration + ensure_field_index(result->type, "decls", 3); + if ((err = ir_make_type_info_decls(ira, source_instr, fields[3], + type_entry->data.enumeration.decls_scope, false))) + { + return err; + } + // is_exhaustive: bool + ensure_field_index(result->type, "is_exhaustive", 4); + fields[4]->special = ConstValSpecialStatic; + fields[4]->type = ira->codegen->builtin_types.entry_bool; + fields[4]->data.x_bool = !type_entry->data.enumeration.non_exhaustive; + + break; + } + case ZigTypeIdErrorSet: + { + result = ira->codegen->pass1_arena->create(); + result->special = ConstValSpecialStatic; + result->type = ir_type_info_get_type(ira, "ErrorSet", nullptr); + + ZigType *type_info_error_type = ir_type_info_get_type(ira, "Error", nullptr); + if (!resolve_inferred_error_set(ira->codegen, type_entry, source_instr->source_node)) { + return ErrorSemanticAnalyzeFail; + } + if (type_is_global_error_set(type_entry)) { + result->data.x_optional = nullptr; + break; + } + if ((err = type_resolve(ira->codegen, type_info_error_type, ResolveStatusSizeKnown))) { + zig_unreachable(); + } + ZigValue *slice_val = ira->codegen->pass1_arena->create(); + result->data.x_optional = slice_val; + + uint32_t error_count = type_entry->data.error_set.err_count; + ZigValue *error_array = ira->codegen->pass1_arena->create(); + error_array->special = ConstValSpecialStatic; + error_array->type = get_array_type(ira->codegen, type_info_error_type, error_count, nullptr); + error_array->data.x_array.special = ConstArraySpecialNone; + error_array->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(error_count); + + init_const_slice(ira->codegen, slice_val, error_array, 0, error_count, false); + for (uint32_t error_index = 0; error_index < error_count; error_index++) { + ErrorTableEntry *error = type_entry->data.error_set.errors[error_index]; + ZigValue *error_val = &error_array->data.x_array.data.s_none.elements[error_index]; + + error_val->special = ConstValSpecialStatic; + error_val->type = type_info_error_type; + + ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 1); + + ZigValue *name = nullptr; + if (error->cached_error_name_val != nullptr) + name = error->cached_error_name_val; + if (name == nullptr) + name = create_const_str_lit(ira->codegen, &error->name)->data.x_ptr.data.ref.pointee; + init_const_slice(ira->codegen, inner_fields[0], name, 0, buf_len(&error->name), true); + + error_val->data.x_struct.fields = inner_fields; + error_val->parent.id = ConstParentIdArray; + error_val->parent.data.p_array.array_val = error_array; + error_val->parent.data.p_array.elem_index = error_index; + } + + break; + } + case ZigTypeIdErrorUnion: + { + result = ira->codegen->pass1_arena->create(); + result->special = ConstValSpecialStatic; + result->type = ir_type_info_get_type(ira, "ErrorUnion", nullptr); + + ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 2); + result->data.x_struct.fields = fields; + + // error_set: type + ensure_field_index(result->type, "error_set", 0); + fields[0]->special = ConstValSpecialStatic; + fields[0]->type = ira->codegen->builtin_types.entry_type; + fields[0]->data.x_type = type_entry->data.error_union.err_set_type; + + // payload: type + ensure_field_index(result->type, "payload", 1); + fields[1]->special = ConstValSpecialStatic; + fields[1]->type = ira->codegen->builtin_types.entry_type; + fields[1]->data.x_type = type_entry->data.error_union.payload_type; + + break; + } + case ZigTypeIdUnion: + { + if ((err = type_resolve(ira->codegen, type_entry, ResolveStatusSizeKnown))) + return err; + + result = ira->codegen->pass1_arena->create(); + result->special = ConstValSpecialStatic; + result->type = ir_type_info_get_type(ira, "Union", nullptr); + + ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 4); + result->data.x_struct.fields = fields; + + // layout: ContainerLayout + ensure_field_index(result->type, "layout", 0); + fields[0]->special = ConstValSpecialStatic; + fields[0]->type = ir_type_info_get_type(ira, "ContainerLayout", nullptr); + bigint_init_unsigned(&fields[0]->data.x_enum_tag, type_entry->data.unionation.layout); + // tag_type: ?type + ensure_field_index(result->type, "tag_type", 1); + fields[1]->special = ConstValSpecialStatic; + fields[1]->type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type); + + AstNode *union_decl_node = type_entry->data.unionation.decl_node; + if (union_decl_node->data.container_decl.auto_enum || + union_decl_node->data.container_decl.init_arg_expr != nullptr) + { + ZigValue *tag_type = ira->codegen->pass1_arena->create(); + tag_type->special = ConstValSpecialStatic; + tag_type->type = ira->codegen->builtin_types.entry_type; + tag_type->data.x_type = type_entry->data.unionation.tag_type; + fields[1]->data.x_optional = tag_type; + } else { + fields[1]->data.x_optional = nullptr; + } + // fields: []TypeInfo.UnionField + ensure_field_index(result->type, "fields", 2); + + ZigType *type_info_union_field_type = ir_type_info_get_type(ira, "UnionField", nullptr); + if ((err = type_resolve(ira->codegen, type_info_union_field_type, ResolveStatusSizeKnown))) + zig_unreachable(); + uint32_t union_field_count = type_entry->data.unionation.src_field_count; + + ZigValue *union_field_array = ira->codegen->pass1_arena->create(); + union_field_array->special = ConstValSpecialStatic; + union_field_array->type = get_array_type(ira->codegen, type_info_union_field_type, union_field_count, nullptr); + union_field_array->data.x_array.special = ConstArraySpecialNone; + union_field_array->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(union_field_count); + + init_const_slice(ira->codegen, fields[2], union_field_array, 0, union_field_count, false); + + for (uint32_t union_field_index = 0; union_field_index < union_field_count; union_field_index++) { + TypeUnionField *union_field = &type_entry->data.unionation.fields[union_field_index]; + ZigValue *union_field_val = &union_field_array->data.x_array.data.s_none.elements[union_field_index]; + + union_field_val->special = ConstValSpecialStatic; + union_field_val->type = type_info_union_field_type; + + ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 2); + inner_fields[1]->special = ConstValSpecialStatic; + inner_fields[1]->type = ira->codegen->builtin_types.entry_type; + inner_fields[1]->data.x_type = union_field->type_entry; + + ZigValue *name = create_const_str_lit(ira->codegen, union_field->name)->data.x_ptr.data.ref.pointee; + init_const_slice(ira->codegen, inner_fields[0], name, 0, buf_len(union_field->name), true); + + union_field_val->data.x_struct.fields = inner_fields; + union_field_val->parent.id = ConstParentIdArray; + union_field_val->parent.data.p_array.array_val = union_field_array; + union_field_val->parent.data.p_array.elem_index = union_field_index; + } + // decls: []TypeInfo.Declaration + ensure_field_index(result->type, "decls", 3); + if ((err = ir_make_type_info_decls(ira, source_instr, fields[3], + type_entry->data.unionation.decls_scope, false))) + { + return err; + } + + break; + } + case ZigTypeIdStruct: + { + if (type_entry->data.structure.special == StructSpecialSlice) { + result = create_ptr_like_type_info(ira, source_instr, type_entry); + if (result == nullptr) + return ErrorSemanticAnalyzeFail; + break; + } + + if ((err = type_resolve(ira->codegen, type_entry, ResolveStatusSizeKnown))) + return err; + + result = ira->codegen->pass1_arena->create(); + result->special = ConstValSpecialStatic; + result->type = ir_type_info_get_type(ira, "Struct", nullptr); + + ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 4); + result->data.x_struct.fields = fields; + + // layout: ContainerLayout + ensure_field_index(result->type, "layout", 0); + fields[0]->special = ConstValSpecialStatic; + fields[0]->type = ir_type_info_get_type(ira, "ContainerLayout", nullptr); + bigint_init_unsigned(&fields[0]->data.x_enum_tag, type_entry->data.structure.layout); + // fields: []TypeInfo.StructField + ensure_field_index(result->type, "fields", 1); + + ZigType *type_info_struct_field_type = ir_type_info_get_type(ira, "StructField", nullptr); + if ((err = type_resolve(ira->codegen, type_info_struct_field_type, ResolveStatusSizeKnown))) { + zig_unreachable(); + } + uint32_t struct_field_count = type_entry->data.structure.src_field_count; + + ZigValue *struct_field_array = ira->codegen->pass1_arena->create(); + struct_field_array->special = ConstValSpecialStatic; + struct_field_array->type = get_array_type(ira->codegen, type_info_struct_field_type, struct_field_count, nullptr); + struct_field_array->data.x_array.special = ConstArraySpecialNone; + struct_field_array->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(struct_field_count); + + init_const_slice(ira->codegen, fields[1], struct_field_array, 0, struct_field_count, false); + + for (uint32_t struct_field_index = 0; struct_field_index < struct_field_count; struct_field_index++) { + TypeStructField *struct_field = type_entry->data.structure.fields[struct_field_index]; + ZigValue *struct_field_val = &struct_field_array->data.x_array.data.s_none.elements[struct_field_index]; + + struct_field_val->special = ConstValSpecialStatic; + struct_field_val->type = type_info_struct_field_type; + + ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 4); + + inner_fields[1]->special = ConstValSpecialStatic; + inner_fields[1]->type = ira->codegen->builtin_types.entry_type; + inner_fields[1]->data.x_type = struct_field->type_entry; + + // default_value: anytype + inner_fields[2]->special = ConstValSpecialStatic; + inner_fields[2]->type = get_optional_type2(ira->codegen, struct_field->type_entry); + if (inner_fields[2]->type == nullptr) return ErrorSemanticAnalyzeFail; + memoize_field_init_val(ira->codegen, type_entry, struct_field); + if(struct_field->init_val != nullptr && type_is_invalid(struct_field->init_val->type)){ + return ErrorSemanticAnalyzeFail; + } + set_optional_payload(inner_fields[2], struct_field->init_val); + + inner_fields[3]->special = ConstValSpecialStatic; + inner_fields[3]->type = ira->codegen->builtin_types.entry_bool; + inner_fields[3]->data.x_bool = struct_field->is_comptime; + + ZigValue *name = create_const_str_lit(ira->codegen, struct_field->name)->data.x_ptr.data.ref.pointee; + init_const_slice(ira->codegen, inner_fields[0], name, 0, buf_len(struct_field->name), true); + + struct_field_val->data.x_struct.fields = inner_fields; + struct_field_val->parent.id = ConstParentIdArray; + struct_field_val->parent.data.p_array.array_val = struct_field_array; + struct_field_val->parent.data.p_array.elem_index = struct_field_index; + } + // decls: []TypeInfo.Declaration + ensure_field_index(result->type, "decls", 2); + if ((err = ir_make_type_info_decls(ira, source_instr, fields[2], + type_entry->data.structure.decls_scope, false))) + { + return err; + } + + // is_tuple: bool + ensure_field_index(result->type, "is_tuple", 3); + fields[3]->special = ConstValSpecialStatic; + fields[3]->type = ira->codegen->builtin_types.entry_bool; + fields[3]->data.x_bool = is_tuple(type_entry); + + break; + } + case ZigTypeIdFn: + { + result = ira->codegen->pass1_arena->create(); + result->special = ConstValSpecialStatic; + result->type = ir_type_info_get_type(ira, "Fn", nullptr); + + ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 5); + result->data.x_struct.fields = fields; + + // calling_convention: TypeInfo.CallingConvention + ensure_field_index(result->type, "calling_convention", 0); + fields[0]->special = ConstValSpecialStatic; + fields[0]->type = get_builtin_type(ira->codegen, "CallingConvention"); + bigint_init_unsigned(&fields[0]->data.x_enum_tag, type_entry->data.fn.fn_type_id.cc); + // is_generic: bool + ensure_field_index(result->type, "is_generic", 1); + bool is_generic = type_entry->data.fn.is_generic; + fields[1]->special = ConstValSpecialStatic; + fields[1]->type = ira->codegen->builtin_types.entry_bool; + fields[1]->data.x_bool = is_generic; + // is_varargs: bool + ensure_field_index(result->type, "is_var_args", 2); + bool is_varargs = type_entry->data.fn.fn_type_id.is_var_args; + fields[2]->special = ConstValSpecialStatic; + fields[2]->type = ira->codegen->builtin_types.entry_bool; + fields[2]->data.x_bool = type_entry->data.fn.fn_type_id.is_var_args; + // return_type: ?type + ensure_field_index(result->type, "return_type", 3); + fields[3]->special = ConstValSpecialStatic; + fields[3]->type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type); + if (type_entry->data.fn.fn_type_id.return_type == nullptr) + fields[3]->data.x_optional = nullptr; + else { + ZigValue *return_type = ira->codegen->pass1_arena->create(); + return_type->special = ConstValSpecialStatic; + return_type->type = ira->codegen->builtin_types.entry_type; + return_type->data.x_type = type_entry->data.fn.fn_type_id.return_type; + fields[3]->data.x_optional = return_type; + } + // args: []TypeInfo.FnArg + ZigType *type_info_fn_arg_type = ir_type_info_get_type(ira, "FnArg", nullptr); + if ((err = type_resolve(ira->codegen, type_info_fn_arg_type, ResolveStatusSizeKnown))) { + zig_unreachable(); + } + size_t fn_arg_count = type_entry->data.fn.fn_type_id.param_count - + (is_varargs && type_entry->data.fn.fn_type_id.cc != CallingConventionC); + + ZigValue *fn_arg_array = ira->codegen->pass1_arena->create(); + fn_arg_array->special = ConstValSpecialStatic; + fn_arg_array->type = get_array_type(ira->codegen, type_info_fn_arg_type, fn_arg_count, nullptr); + fn_arg_array->data.x_array.special = ConstArraySpecialNone; + fn_arg_array->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(fn_arg_count); + + init_const_slice(ira->codegen, fields[4], fn_arg_array, 0, fn_arg_count, false); + + for (size_t fn_arg_index = 0; fn_arg_index < fn_arg_count; fn_arg_index++) { + FnTypeParamInfo *fn_param_info = &type_entry->data.fn.fn_type_id.param_info[fn_arg_index]; + ZigValue *fn_arg_val = &fn_arg_array->data.x_array.data.s_none.elements[fn_arg_index]; + + fn_arg_val->special = ConstValSpecialStatic; + fn_arg_val->type = type_info_fn_arg_type; + + bool arg_is_generic = fn_param_info->type == nullptr; + if (arg_is_generic) assert(is_generic); + + ZigValue **inner_fields = alloc_const_vals_ptrs(ira->codegen, 3); + inner_fields[0]->special = ConstValSpecialStatic; + inner_fields[0]->type = ira->codegen->builtin_types.entry_bool; + inner_fields[0]->data.x_bool = arg_is_generic; + inner_fields[1]->special = ConstValSpecialStatic; + inner_fields[1]->type = ira->codegen->builtin_types.entry_bool; + inner_fields[1]->data.x_bool = fn_param_info->is_noalias; + inner_fields[2]->special = ConstValSpecialStatic; + inner_fields[2]->type = get_optional_type(ira->codegen, ira->codegen->builtin_types.entry_type); + + if (arg_is_generic) + inner_fields[2]->data.x_optional = nullptr; + else { + ZigValue *arg_type = ira->codegen->pass1_arena->create(); + arg_type->special = ConstValSpecialStatic; + arg_type->type = ira->codegen->builtin_types.entry_type; + arg_type->data.x_type = fn_param_info->type; + inner_fields[2]->data.x_optional = arg_type; + } + + fn_arg_val->data.x_struct.fields = inner_fields; + fn_arg_val->parent.id = ConstParentIdArray; + fn_arg_val->parent.data.p_array.array_val = fn_arg_array; + fn_arg_val->parent.data.p_array.elem_index = fn_arg_index; + } + + break; + } + case ZigTypeIdBoundFn: + { + ZigType *fn_type = type_entry->data.bound_fn.fn_type; + assert(fn_type->id == ZigTypeIdFn); + if ((err = ir_make_type_info_value(ira, source_instr, fn_type, &result))) + return err; + + break; + } + case ZigTypeIdFnFrame: + { + result = ira->codegen->pass1_arena->create(); + result->special = ConstValSpecialStatic; + result->type = ir_type_info_get_type(ira, "Frame", nullptr); + ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 1); + result->data.x_struct.fields = fields; + ZigFn *fn = type_entry->data.frame.fn; + // function: anytype + ensure_field_index(result->type, "function", 0); + fields[0] = create_const_fn(ira->codegen, fn); + break; + } + } + + assert(result != nullptr); + ira->codegen->type_info_cache.put(type_entry, result); + *out = result; + return ErrorNone; +} + +static IrInstGen *ir_analyze_instruction_type_info(IrAnalyze *ira, IrInstSrcTypeInfo *instruction) { + Error err; + IrInstGen *type_value = instruction->type_value->child; + ZigType *type_entry = ir_resolve_type(ira, type_value); + if (type_is_invalid(type_entry)) + return ira->codegen->invalid_inst_gen; + + ZigType *result_type = ir_type_info_get_type(ira, nullptr, nullptr); + + ZigValue *payload; + if ((err = ir_make_type_info_value(ira, &instruction->base.base, type_entry, &payload))) + return ira->codegen->invalid_inst_gen; + + IrInstGen *result = ir_const(ira, &instruction->base.base, result_type); + ZigValue *out_val = result->value; + bigint_init_unsigned(&out_val->data.x_union.tag, type_id_index(type_entry)); + out_val->data.x_union.payload = payload; + + if (payload != nullptr) { + payload->parent.id = ConstParentIdUnion; + payload->parent.data.p_union.union_val = out_val; + } + + return result; +} + +static ZigValue *get_const_field(IrAnalyze *ira, AstNode *source_node, ZigValue *struct_value, + const char *name, size_t field_index) +{ + Error err; + ensure_field_index(struct_value->type, name, field_index); + ZigValue *val = struct_value->data.x_struct.fields[field_index]; + if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, source_node, val, UndefBad))) + return nullptr; + return val; +} + +static Error get_const_field_sentinel(IrAnalyze *ira, IrInst* source_instr, ZigValue *struct_value, + const char *name, size_t field_index, ZigType *elem_type, ZigValue **result) +{ + ZigValue *field_val = get_const_field(ira, source_instr->source_node, struct_value, name, field_index); + if (field_val == nullptr) + return ErrorSemanticAnalyzeFail; + + IrInstGen *field_inst = ir_const_move(ira, source_instr, field_val); + IrInstGen *casted_field_inst = ir_implicit_cast(ira, field_inst, + get_optional_type(ira->codegen, elem_type)); + if (type_is_invalid(casted_field_inst->value->type)) + return ErrorSemanticAnalyzeFail; + + if (optional_value_is_null(casted_field_inst->value)) { + *result = nullptr; + } else { + assert(type_has_optional_repr(casted_field_inst->value->type)); + *result = casted_field_inst->value->data.x_optional; + } + + return ErrorNone; +} + +static Error get_const_field_bool(IrAnalyze *ira, AstNode *source_node, ZigValue *struct_value, + const char *name, size_t field_index, bool *out) +{ + ZigValue *value = get_const_field(ira, source_node, struct_value, name, field_index); + if (value == nullptr) + return ErrorSemanticAnalyzeFail; + assert(value->type == ira->codegen->builtin_types.entry_bool); + *out = value->data.x_bool; + return ErrorNone; +} + +static BigInt *get_const_field_lit_int(IrAnalyze *ira, AstNode *source_node, ZigValue *struct_value, const char *name, size_t field_index) +{ + ZigValue *value = get_const_field(ira, source_node, struct_value, name, field_index); + if (value == nullptr) + return nullptr; + assert(value->type == ira->codegen->builtin_types.entry_num_lit_int); + return &value->data.x_bigint; +} + +static ZigType *get_const_field_meta_type(IrAnalyze *ira, AstNode *source_node, ZigValue *struct_value, const char *name, size_t field_index) +{ + ZigValue *value = get_const_field(ira, source_node, struct_value, name, field_index); + if (value == nullptr) + return ira->codegen->invalid_inst_gen->value->type; + assert(value->type == ira->codegen->builtin_types.entry_type); + return value->data.x_type; +} + +static ZigType *get_const_field_meta_type_optional(IrAnalyze *ira, AstNode *source_node, + ZigValue *struct_value, const char *name, size_t field_index) +{ + ZigValue *value = get_const_field(ira, source_node, struct_value, name, field_index); + if (value == nullptr) + return ira->codegen->invalid_inst_gen->value->type; + assert(value->type->id == ZigTypeIdOptional); + assert(value->type->data.maybe.child_type == ira->codegen->builtin_types.entry_type); + if (value->data.x_optional == nullptr) + return nullptr; + return value->data.x_optional->data.x_type; +} + +static Error get_const_field_buf(IrAnalyze *ira, AstNode *source_node, ZigValue *struct_value, + const char *name, size_t field_index, Buf *out) +{ + ZigValue *slice = get_const_field(ira, source_node, struct_value, name, field_index); + ZigValue *ptr = slice->data.x_struct.fields[slice_ptr_index]; + ZigValue *len = slice->data.x_struct.fields[slice_len_index]; + assert(ptr->data.x_ptr.special == ConstPtrSpecialBaseArray); + assert(ptr->data.x_ptr.data.base_array.elem_index == 0); + ZigValue *arr = ptr->data.x_ptr.data.base_array.array_val; + assert(arr->special == ConstValSpecialStatic); + switch (arr->data.x_array.special) { + case ConstArraySpecialUndef: + return ErrorSemanticAnalyzeFail; + case ConstArraySpecialNone: { + buf_resize(out, 0); + size_t count = bigint_as_usize(&len->data.x_bigint); + for (size_t j = 0; j < count; j++) { + ZigValue *ch_val = &arr->data.x_array.data.s_none.elements[j]; + unsigned ch = bigint_as_u32(&ch_val->data.x_bigint); + buf_append_char(out, ch); + } + break; + } + case ConstArraySpecialBuf: + buf_init_from_buf(out, arr->data.x_array.data.s_buf); + break; + } + return ErrorNone; +} + +static ZigType *type_info_to_type(IrAnalyze *ira, IrInst *source_instr, ZigTypeId tagTypeId, ZigValue *payload) { + Error err; + switch (tagTypeId) { + case ZigTypeIdInvalid: + zig_unreachable(); + case ZigTypeIdMetaType: + return ira->codegen->builtin_types.entry_type; + case ZigTypeIdVoid: + return ira->codegen->builtin_types.entry_void; + case ZigTypeIdBool: + return ira->codegen->builtin_types.entry_bool; + case ZigTypeIdUnreachable: + return ira->codegen->builtin_types.entry_unreachable; + case ZigTypeIdInt: { + assert(payload->special == ConstValSpecialStatic); + assert(payload->type == ir_type_info_get_type(ira, "Int", nullptr)); + BigInt *bi = get_const_field_lit_int(ira, source_instr->source_node, payload, "bits", 1); + if (bi == nullptr) + return ira->codegen->invalid_inst_gen->value->type; + bool is_signed; + if ((err = get_const_field_bool(ira, source_instr->source_node, payload, "is_signed", 0, &is_signed))) + return ira->codegen->invalid_inst_gen->value->type; + return get_int_type(ira->codegen, is_signed, bigint_as_u32(bi)); + } + case ZigTypeIdFloat: + { + assert(payload->special == ConstValSpecialStatic); + assert(payload->type == ir_type_info_get_type(ira, "Float", nullptr)); + BigInt *bi = get_const_field_lit_int(ira, source_instr->source_node, payload, "bits", 0); + if (bi == nullptr) + return ira->codegen->invalid_inst_gen->value->type; + uint32_t bits = bigint_as_u32(bi); + switch (bits) { + case 16: return ira->codegen->builtin_types.entry_f16; + case 32: return ira->codegen->builtin_types.entry_f32; + case 64: return ira->codegen->builtin_types.entry_f64; + case 128: return ira->codegen->builtin_types.entry_f128; + } + ir_add_error(ira, source_instr, buf_sprintf("%d-bit float unsupported", bits)); + return ira->codegen->invalid_inst_gen->value->type; + } + case ZigTypeIdPointer: + { + ZigType *type_info_pointer_type = ir_type_info_get_type(ira, "Pointer", nullptr); + assert(payload->special == ConstValSpecialStatic); + assert(payload->type == type_info_pointer_type); + ZigValue *size_value = get_const_field(ira, source_instr->source_node, payload, "size", 0); + if (size_value == nullptr) + return ira->codegen->invalid_inst_gen->value->type; + + assert(size_value->type == ir_type_info_get_type(ira, "Size", type_info_pointer_type)); + BuiltinPtrSize size_enum_index = (BuiltinPtrSize)bigint_as_u32(&size_value->data.x_enum_tag); + PtrLen ptr_len = size_enum_index_to_ptr_len(size_enum_index); + ZigType *elem_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "child", 4); + if (type_is_invalid(elem_type)) + return ira->codegen->invalid_inst_gen->value->type; + ZigValue *sentinel; + if ((err = get_const_field_sentinel(ira, source_instr, payload, "sentinel", 6, + elem_type, &sentinel))) + { + return ira->codegen->invalid_inst_gen->value->type; + } + if (sentinel != nullptr && (size_enum_index == BuiltinPtrSizeOne || size_enum_index == BuiltinPtrSizeC)) { + ir_add_error(ira, source_instr, + buf_sprintf("sentinels are only allowed on slices and unknown-length pointers")); + return ira->codegen->invalid_inst_gen->value->type; + } + BigInt *bi = get_const_field_lit_int(ira, source_instr->source_node, payload, "alignment", 3); + if (bi == nullptr) + return ira->codegen->invalid_inst_gen->value->type; + + bool is_const; + if ((err = get_const_field_bool(ira, source_instr->source_node, payload, "is_const", 1, &is_const))) + return ira->codegen->invalid_inst_gen->value->type; + + bool is_volatile; + if ((err = get_const_field_bool(ira, source_instr->source_node, payload, "is_volatile", 2, + &is_volatile))) + { + return ira->codegen->invalid_inst_gen->value->type; + } + + bool is_allowzero; + if ((err = get_const_field_bool(ira, source_instr->source_node, payload, "is_allowzero", 5, + &is_allowzero))) + { + return ira->codegen->invalid_inst_gen->value->type; + } + + + ZigType *ptr_type = get_pointer_to_type_extra2(ira->codegen, + elem_type, + is_const, + is_volatile, + ptr_len, + bigint_as_u32(bi), + 0, // bit_offset_in_host + 0, // host_int_bytes + is_allowzero, + VECTOR_INDEX_NONE, nullptr, sentinel); + if (size_enum_index != BuiltinPtrSizeSlice) + return ptr_type; + return get_slice_type(ira->codegen, ptr_type); + } + case ZigTypeIdArray: { + assert(payload->special == ConstValSpecialStatic); + assert(payload->type == ir_type_info_get_type(ira, "Array", nullptr)); + ZigType *elem_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "child", 1); + if (type_is_invalid(elem_type)) + return ira->codegen->invalid_inst_gen->value->type; + ZigValue *sentinel; + if ((err = get_const_field_sentinel(ira, source_instr, payload, "sentinel", 2, + elem_type, &sentinel))) + { + return ira->codegen->invalid_inst_gen->value->type; + } + BigInt *bi = get_const_field_lit_int(ira, source_instr->source_node, payload, "len", 0); + if (bi == nullptr) + return ira->codegen->invalid_inst_gen->value->type; + return get_array_type(ira->codegen, elem_type, bigint_as_u64(bi), sentinel); + } + case ZigTypeIdComptimeFloat: + return ira->codegen->builtin_types.entry_num_lit_float; + case ZigTypeIdComptimeInt: + return ira->codegen->builtin_types.entry_num_lit_int; + case ZigTypeIdUndefined: + return ira->codegen->builtin_types.entry_undef; + case ZigTypeIdNull: + return ira->codegen->builtin_types.entry_null; + case ZigTypeIdOptional: { + assert(payload->special == ConstValSpecialStatic); + assert(payload->type == ir_type_info_get_type(ira, "Optional", nullptr)); + ZigType *child_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "child", 0); + if (type_is_invalid(child_type)) + return ira->codegen->invalid_inst_gen->value->type; + return get_optional_type(ira->codegen, child_type); + } + case ZigTypeIdErrorUnion: { + assert(payload->special == ConstValSpecialStatic); + assert(payload->type == ir_type_info_get_type(ira, "ErrorUnion", nullptr)); + ZigType *err_set_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "error_set", 0); + if (type_is_invalid(err_set_type)) + return ira->codegen->invalid_inst_gen->value->type; + + ZigType *payload_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "payload", 1); + if (type_is_invalid(payload_type)) + return ira->codegen->invalid_inst_gen->value->type; + + return get_error_union_type(ira->codegen, err_set_type, payload_type); + } + case ZigTypeIdOpaque: { + Buf *bare_name = buf_alloc(); + Buf *full_name = get_anon_type_name(ira->codegen, + ira->old_irb.exec, "opaque", source_instr->scope, source_instr->source_node, bare_name); + return get_opaque_type(ira->codegen, + source_instr->scope, source_instr->source_node, buf_ptr(full_name), bare_name); + } + case ZigTypeIdVector: { + assert(payload->special == ConstValSpecialStatic); + assert(payload->type == ir_type_info_get_type(ira, "Vector", nullptr)); + BigInt *len = get_const_field_lit_int(ira, source_instr->source_node, payload, "len", 0); + if (len == nullptr) + return ira->codegen->invalid_inst_gen->value->type; + + ZigType *child_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "child", 1); + if ((err = ir_validate_vector_elem_type(ira, source_instr->source_node, child_type))) { + return ira->codegen->invalid_inst_gen->value->type; + } + return get_vector_type(ira->codegen, bigint_as_u32(len), child_type); + } + case ZigTypeIdAnyFrame: { + assert(payload->special == ConstValSpecialStatic); + assert(payload->type == ir_type_info_get_type(ira, "AnyFrame", nullptr)); + ZigType *child_type = get_const_field_meta_type_optional(ira, source_instr->source_node, payload, "child", 0); + if (child_type != nullptr && type_is_invalid(child_type)) + return ira->codegen->invalid_inst_gen->value->type; + + return get_any_frame_type(ira->codegen, child_type); + } + case ZigTypeIdEnumLiteral: + return ira->codegen->builtin_types.entry_enum_literal; + case ZigTypeIdFnFrame: { + assert(payload->special == ConstValSpecialStatic); + assert(payload->type == ir_type_info_get_type(ira, "Frame", nullptr)); + ZigValue *function = get_const_field(ira, source_instr->source_node, payload, "function", 0); + if (function == nullptr) + return ira->codegen->invalid_inst_gen->value->type; + + assert(function->type->id == ZigTypeIdFn); + ZigFn *fn = function->data.x_ptr.data.fn.fn_entry; + return get_fn_frame_type(ira->codegen, fn); + } + case ZigTypeIdErrorSet: { + assert(payload->special == ConstValSpecialStatic); + assert(payload->type->id == ZigTypeIdOptional); + ZigValue *slice = payload->data.x_optional; + if (slice == nullptr) + return ira->codegen->builtin_types.entry_global_error_set; + assert(slice->special == ConstValSpecialStatic); + assert(is_slice(slice->type)); + ZigType *err_set_type = new_type_table_entry(ZigTypeIdErrorSet); + Buf bare_name = BUF_INIT; + buf_init_from_buf(&err_set_type->name, get_anon_type_name(ira->codegen, ira->old_irb.exec, "error", source_instr->scope, source_instr->source_node, &bare_name)); + err_set_type->size_in_bits = ira->codegen->builtin_types.entry_global_error_set->size_in_bits; + err_set_type->abi_align = ira->codegen->builtin_types.entry_global_error_set->abi_align; + err_set_type->abi_size = ira->codegen->builtin_types.entry_global_error_set->abi_size; + ZigValue *ptr = slice->data.x_struct.fields[slice_ptr_index]; + assert(ptr->data.x_ptr.special == ConstPtrSpecialBaseArray);; + assert(ptr->data.x_ptr.data.base_array.elem_index == 0); + ZigValue *arr = ptr->data.x_ptr.data.base_array.array_val; + assert(arr->special == ConstValSpecialStatic); + assert(arr->data.x_array.special == ConstArraySpecialNone); + ZigValue *len = slice->data.x_struct.fields[slice_len_index]; + size_t count = bigint_as_usize(&len->data.x_bigint); + err_set_type->data.error_set.err_count = count; + err_set_type->data.error_set.errors = heap::c_allocator.allocate(count); + bool *already_set = heap::c_allocator.allocate(ira->codegen->errors_by_index.length + count); + for (size_t i = 0; i < count; i++) { + ZigValue *error = &arr->data.x_array.data.s_none.elements[i]; + assert(error->type == ir_type_info_get_type(ira, "Error", nullptr)); + ErrorTableEntry *err_entry = heap::c_allocator.create(); + err_entry->decl_node = source_instr->source_node; + if ((err = get_const_field_buf(ira, source_instr->source_node, error, "name", 0, &err_entry->name))) + return ira->codegen->invalid_inst_gen->value->type; + auto existing_entry = ira->codegen->error_table.put_unique(&err_entry->name, err_entry); + if (existing_entry) { + err_entry->value = existing_entry->value->value; + } else { + size_t error_value_count = ira->codegen->errors_by_index.length; + assert((uint32_t)error_value_count < (((uint32_t)1) << (uint32_t)ira->codegen->err_tag_type->data.integral.bit_count)); + err_entry->value = error_value_count; + ira->codegen->errors_by_index.append(err_entry); + } + if (already_set[err_entry->value]) { + ir_add_error(ira, source_instr, buf_sprintf("duplicate error: %s", buf_ptr(&err_entry->name))); + return ira->codegen->invalid_inst_gen->value->type; + } else { + already_set[err_entry->value] = true; + } + err_set_type->data.error_set.errors[i] = err_entry; + } + return err_set_type; + } + case ZigTypeIdStruct: { + assert(payload->special == ConstValSpecialStatic); + assert(payload->type == ir_type_info_get_type(ira, "Struct", nullptr)); + + ZigValue *layout_value = get_const_field(ira, source_instr->source_node, payload, "layout", 0); + if (layout_value == nullptr) + return ira->codegen->invalid_inst_gen->value->type; + assert(layout_value->special == ConstValSpecialStatic); + assert(layout_value->type == ir_type_info_get_type(ira, "ContainerLayout", nullptr)); + ContainerLayout layout = (ContainerLayout)bigint_as_u32(&layout_value->data.x_enum_tag); + + ZigValue *fields_value = get_const_field(ira, source_instr->source_node, payload, "fields", 1); + if (fields_value == nullptr) + return ira->codegen->invalid_inst_gen->value->type; + assert(fields_value->special == ConstValSpecialStatic); + assert(is_slice(fields_value->type)); + ZigValue *fields_ptr = fields_value->data.x_struct.fields[slice_ptr_index]; + ZigValue *fields_len_value = fields_value->data.x_struct.fields[slice_len_index]; + size_t fields_len = bigint_as_usize(&fields_len_value->data.x_bigint); + + ZigValue *decls_value = get_const_field(ira, source_instr->source_node, payload, "decls", 2); + if (decls_value == nullptr) + return ira->codegen->invalid_inst_gen->value->type; + assert(decls_value->special == ConstValSpecialStatic); + assert(is_slice(decls_value->type)); + ZigValue *decls_len_value = decls_value->data.x_struct.fields[slice_len_index]; + size_t decls_len = bigint_as_usize(&decls_len_value->data.x_bigint); + if (decls_len != 0) { + ir_add_error(ira, source_instr, buf_create_from_str("TypeInfo.Struct.decls must be empty for @Type")); + return ira->codegen->invalid_inst_gen->value->type; + } + + bool is_tuple; + if ((err = get_const_field_bool(ira, source_instr->source_node, payload, "is_tuple", 3, &is_tuple))) + return ira->codegen->invalid_inst_gen->value->type; + + ZigType *entry = new_type_table_entry(ZigTypeIdStruct); + buf_init_from_buf(&entry->name, + get_anon_type_name(ira->codegen, ira->old_irb.exec, "struct", source_instr->scope, source_instr->source_node, &entry->name)); + entry->data.structure.decl_node = source_instr->source_node; + entry->data.structure.fields = alloc_type_struct_fields(fields_len); + entry->data.structure.fields_by_name.init(fields_len); + entry->data.structure.src_field_count = fields_len; + entry->data.structure.layout = layout; + entry->data.structure.special = is_tuple ? StructSpecialInferredTuple : StructSpecialNone; + entry->data.structure.created_by_at_type = true; + entry->data.structure.decls_scope = create_decls_scope( + ira->codegen, source_instr->source_node, source_instr->scope, entry, get_scope_import(source_instr->scope), &entry->name); + + assert(fields_ptr->data.x_ptr.special == ConstPtrSpecialBaseArray); + assert(fields_ptr->data.x_ptr.data.base_array.elem_index == 0); + ZigValue *fields_arr = fields_ptr->data.x_ptr.data.base_array.array_val; + assert(fields_arr->special == ConstValSpecialStatic); + assert(fields_arr->data.x_array.special == ConstArraySpecialNone); + for (size_t i = 0; i < fields_len; i++) { + ZigValue *field_value = &fields_arr->data.x_array.data.s_none.elements[i]; + assert(field_value->type == ir_type_info_get_type(ira, "StructField", nullptr)); + TypeStructField *field = entry->data.structure.fields[i]; + field->name = buf_alloc(); + if ((err = get_const_field_buf(ira, source_instr->source_node, field_value, "name", 0, field->name))) + return ira->codegen->invalid_inst_gen->value->type; + field->decl_node = source_instr->source_node; + ZigValue *type_value = get_const_field(ira, source_instr->source_node, field_value, "field_type", 1); + if (type_value == nullptr) + return ira->codegen->invalid_inst_gen->value->type; + field->type_val = type_value; + field->type_entry = type_value->data.x_type; + if (entry->data.structure.fields_by_name.put_unique(field->name, field) != nullptr) { + ir_add_error(ira, source_instr, buf_sprintf("duplicate struct field '%s'", buf_ptr(field->name))); + return ira->codegen->invalid_inst_gen->value->type; + } + ZigValue *default_value = get_const_field(ira, source_instr->source_node, field_value, "default_value", 2); + if (default_value == nullptr) + return ira->codegen->invalid_inst_gen->value->type; + if (default_value->type->id == ZigTypeIdNull) { + field->init_val = nullptr; + } else if (default_value->type->id == ZigTypeIdOptional && default_value->type->data.maybe.child_type == field->type_entry) { + field->init_val = default_value->data.x_optional; + } else if (default_value->type == field->type_entry) { + field->init_val = default_value; + } else { + ir_add_error(ira, source_instr, + buf_sprintf("default_value of field '%s' is of type '%s', expected '%s' or '?%s'", + buf_ptr(field->name), buf_ptr(&default_value->type->name), + buf_ptr(&field->type_entry->name), buf_ptr(&field->type_entry->name))); + return ira->codegen->invalid_inst_gen->value->type; + } + if ((err = get_const_field_bool(ira, source_instr->source_node, field_value, "is_comptime", 3, &field->is_comptime))) + return ira->codegen->invalid_inst_gen->value->type; + } + + return entry; + } + case ZigTypeIdEnum: { + assert(payload->special == ConstValSpecialStatic); + assert(payload->type == ir_type_info_get_type(ira, "Enum", nullptr)); + + ZigValue *layout_value = get_const_field(ira, source_instr->source_node, payload, "layout", 0); + if (layout_value == nullptr) + return ira->codegen->invalid_inst_gen->value->type; + + assert(layout_value->special == ConstValSpecialStatic); + assert(layout_value->type == ir_type_info_get_type(ira, "ContainerLayout", nullptr)); + ContainerLayout layout = (ContainerLayout)bigint_as_u32(&layout_value->data.x_enum_tag); + + ZigType *tag_type = get_const_field_meta_type(ira, source_instr->source_node, payload, "tag_type", 1); + + ZigValue *fields_value = get_const_field(ira, source_instr->source_node, payload, "fields", 2); + if (fields_value == nullptr) + return ira->codegen->invalid_inst_gen->value->type; + + assert(fields_value->special == ConstValSpecialStatic); + assert(is_slice(fields_value->type)); + ZigValue *fields_ptr = fields_value->data.x_struct.fields[slice_ptr_index]; + ZigValue *fields_len_value = fields_value->data.x_struct.fields[slice_len_index]; + size_t fields_len = bigint_as_usize(&fields_len_value->data.x_bigint); + + ZigValue *decls_value = get_const_field(ira, source_instr->source_node, payload, "decls", 3); + if (decls_value == nullptr) + return ira->codegen->invalid_inst_gen->value->type; + + assert(decls_value->special == ConstValSpecialStatic); + assert(is_slice(decls_value->type)); + ZigValue *decls_len_value = decls_value->data.x_struct.fields[slice_len_index]; + size_t decls_len = bigint_as_usize(&decls_len_value->data.x_bigint); + if (decls_len != 0) { + ir_add_error(ira, source_instr, buf_create_from_str("TypeInfo.Enum.decls must be empty for @Type")); + return ira->codegen->invalid_inst_gen->value->type; + } + + Error err; + bool is_exhaustive; + if ((err = get_const_field_bool(ira, source_instr->source_node, payload, "is_exhaustive", 4, &is_exhaustive))) + return ira->codegen->invalid_inst_gen->value->type; + + ZigType *entry = new_type_table_entry(ZigTypeIdEnum); + buf_init_from_buf(&entry->name, + get_anon_type_name(ira->codegen, ira->old_irb.exec, "enum", source_instr->scope, source_instr->source_node, &entry->name)); + entry->data.enumeration.decl_node = source_instr->source_node; + entry->data.enumeration.tag_int_type = tag_type; + entry->data.enumeration.decls_scope = create_decls_scope( + ira->codegen, source_instr->source_node, source_instr->scope, entry, get_scope_import(source_instr->scope), &entry->name); + entry->data.enumeration.fields = heap::c_allocator.allocate(fields_len); + entry->data.enumeration.fields_by_name.init(fields_len); + entry->data.enumeration.src_field_count = fields_len; + entry->data.enumeration.layout = layout; + entry->data.enumeration.non_exhaustive = !is_exhaustive; + + assert(fields_ptr->data.x_ptr.special == ConstPtrSpecialBaseArray); + assert(fields_ptr->data.x_ptr.data.base_array.elem_index == 0); + ZigValue *fields_arr = fields_ptr->data.x_ptr.data.base_array.array_val; + assert(fields_arr->special == ConstValSpecialStatic); + assert(fields_arr->data.x_array.special == ConstArraySpecialNone); + for (size_t i = 0; i < fields_len; i++) { + ZigValue *field_value = &fields_arr->data.x_array.data.s_none.elements[i]; + assert(field_value->type == ir_type_info_get_type(ira, "EnumField", nullptr)); + TypeEnumField *field = &entry->data.enumeration.fields[i]; + field->name = buf_alloc(); + if ((err = get_const_field_buf(ira, source_instr->source_node, field_value, "name", 0, field->name))) + return ira->codegen->invalid_inst_gen->value->type; + field->decl_index = i; + field->decl_node = source_instr->source_node; + if (entry->data.enumeration.fields_by_name.put_unique(field->name, field) != nullptr) { + ir_add_error(ira, source_instr, buf_sprintf("duplicate enum field '%s'", buf_ptr(field->name))); + return ira->codegen->invalid_inst_gen->value->type; + } + BigInt *field_int_value = get_const_field_lit_int(ira, source_instr->source_node, field_value, "value", 1); + if (field_int_value == nullptr) + return ira->codegen->invalid_inst_gen->value->type; + field->value = *field_int_value; + } + return entry; + } + case ZigTypeIdUnion: { + assert(payload->special == ConstValSpecialStatic); + assert(payload->type == ir_type_info_get_type(ira, "Union", nullptr)); + + ZigValue *layout_value = get_const_field(ira, source_instr->source_node, payload, "layout", 0); + if (layout_value == nullptr) + return ira->codegen->invalid_inst_gen->value->type; + assert(layout_value->special == ConstValSpecialStatic); + assert(layout_value->type == ir_type_info_get_type(ira, "ContainerLayout", nullptr)); + ContainerLayout layout = (ContainerLayout)bigint_as_u32(&layout_value->data.x_enum_tag); + + ZigType *tag_type = get_const_field_meta_type_optional(ira, source_instr->source_node, payload, "tag_type", 1); + if (tag_type != nullptr && type_is_invalid(tag_type)) { + return ira->codegen->invalid_inst_gen->value->type; + } + if (tag_type != nullptr && tag_type->id != ZigTypeIdEnum) { + ir_add_error(ira, source_instr, buf_sprintf( + "expected enum type, found '%s'", type_id_name(tag_type->id))); + return ira->codegen->invalid_inst_gen->value->type; + } + + ZigValue *fields_value = get_const_field(ira, source_instr->source_node, payload, "fields", 2); + if (fields_value == nullptr) + return ira->codegen->invalid_inst_gen->value->type; + + assert(fields_value->special == ConstValSpecialStatic); + assert(is_slice(fields_value->type)); + ZigValue *fields_ptr = fields_value->data.x_struct.fields[slice_ptr_index]; + ZigValue *fields_len_value = fields_value->data.x_struct.fields[slice_len_index]; + size_t fields_len = bigint_as_usize(&fields_len_value->data.x_bigint); + + ZigValue *decls_value = get_const_field(ira, source_instr->source_node, payload, "decls", 3); + if (decls_value == nullptr) + return ira->codegen->invalid_inst_gen->value->type; + + assert(decls_value->special == ConstValSpecialStatic); + assert(is_slice(decls_value->type)); + ZigValue *decls_len_value = decls_value->data.x_struct.fields[slice_len_index]; + size_t decls_len = bigint_as_usize(&decls_len_value->data.x_bigint); + if (decls_len != 0) { + ir_add_error(ira, source_instr, buf_create_from_str("TypeInfo.Union.decls must be empty for @Type")); + return ira->codegen->invalid_inst_gen->value->type; + } + + ZigType *entry = new_type_table_entry(ZigTypeIdUnion); + buf_init_from_buf(&entry->name, + get_anon_type_name(ira->codegen, ira->old_irb.exec, "union", source_instr->scope, source_instr->source_node, &entry->name)); + entry->data.unionation.decl_node = source_instr->source_node; + entry->data.unionation.fields = heap::c_allocator.allocate(fields_len); + entry->data.unionation.fields_by_name.init(fields_len); + entry->data.unionation.decls_scope = create_decls_scope( + ira->codegen, source_instr->source_node, source_instr->scope, entry, get_scope_import(source_instr->scope), &entry->name); + entry->data.unionation.tag_type = tag_type; + entry->data.unionation.src_field_count = fields_len; + entry->data.unionation.layout = layout; + + assert(fields_ptr->data.x_ptr.special == ConstPtrSpecialBaseArray); + assert(fields_ptr->data.x_ptr.data.base_array.elem_index == 0); + ZigValue *fields_arr = fields_ptr->data.x_ptr.data.base_array.array_val; + assert(fields_arr->special == ConstValSpecialStatic); + assert(fields_arr->data.x_array.special == ConstArraySpecialNone); + for (size_t i = 0; i < fields_len; i++) { + ZigValue *field_value = &fields_arr->data.x_array.data.s_none.elements[i]; + assert(field_value->type == ir_type_info_get_type(ira, "UnionField", nullptr)); + TypeUnionField *field = &entry->data.unionation.fields[i]; + field->name = buf_alloc(); + if ((err = get_const_field_buf(ira, source_instr->source_node, field_value, "name", 0, field->name))) + return ira->codegen->invalid_inst_gen->value->type; + if (entry->data.unionation.fields_by_name.put_unique(field->name, field) != nullptr) { + ir_add_error(ira, source_instr, buf_sprintf("duplicate union field '%s'", buf_ptr(field->name))); + return ira->codegen->invalid_inst_gen->value->type; + } + field->decl_node = source_instr->source_node; + ZigValue *type_value = get_const_field(ira, source_instr->source_node, field_value, "field_type", 1); + if (type_value == nullptr) + return ira->codegen->invalid_inst_gen->value->type; + field->type_val = type_value; + field->type_entry = type_value->data.x_type; + } + return entry; + } + case ZigTypeIdFn: + case ZigTypeIdBoundFn: + ir_add_error(ira, source_instr, buf_sprintf( + "@Type not available for 'TypeInfo.%s'", type_id_name(tagTypeId))); + return ira->codegen->invalid_inst_gen->value->type; + } + zig_unreachable(); +} + +static IrInstGen *ir_analyze_instruction_type(IrAnalyze *ira, IrInstSrcType *instruction) { + IrInstGen *uncasted_type_info = instruction->type_info->child; + if (type_is_invalid(uncasted_type_info->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *type_info = ir_implicit_cast(ira, uncasted_type_info, ir_type_info_get_type(ira, nullptr, nullptr)); + if (type_is_invalid(type_info->value->type)) + return ira->codegen->invalid_inst_gen; + + ZigValue *type_info_val = ir_resolve_const(ira, type_info, UndefBad); + if (type_info_val == nullptr) + return ira->codegen->invalid_inst_gen; + ZigTypeId type_id_tag = type_id_at_index(bigint_as_usize(&type_info_val->data.x_union.tag)); + ZigType *type = type_info_to_type(ira, &uncasted_type_info->base, type_id_tag, + type_info_val->data.x_union.payload); + if (type_is_invalid(type)) + return ira->codegen->invalid_inst_gen; + return ir_const_type(ira, &instruction->base.base, type); +} + +static IrInstGen *ir_analyze_instruction_set_eval_branch_quota(IrAnalyze *ira, + IrInstSrcSetEvalBranchQuota *instruction) +{ + uint64_t new_quota; + if (!ir_resolve_usize(ira, instruction->new_quota->child, &new_quota)) + return ira->codegen->invalid_inst_gen; + + if (new_quota > *ira->new_irb.exec->backward_branch_quota) { + *ira->new_irb.exec->backward_branch_quota = new_quota; + } + + return ir_const_void(ira, &instruction->base.base); +} + +static IrInstGen *ir_analyze_instruction_type_name(IrAnalyze *ira, IrInstSrcTypeName *instruction) { + IrInstGen *type_value = instruction->type_value->child; + ZigType *type_entry = ir_resolve_type(ira, type_value); + if (type_is_invalid(type_entry)) + return ira->codegen->invalid_inst_gen; + + if (!type_entry->cached_const_name_val) { + type_entry->cached_const_name_val = create_const_str_lit(ira->codegen, type_bare_name(type_entry)); + } + IrInstGen *result = ir_const(ira, &instruction->base.base, nullptr); + copy_const_val(ira->codegen, result->value, type_entry->cached_const_name_val); + return result; +} + +static IrInstGen *ir_analyze_instruction_c_import(IrAnalyze *ira, IrInstSrcCImport *instruction) { + Error err; + AstNode *node = instruction->base.base.source_node; + assert(node->type == NodeTypeFnCallExpr); + AstNode *block_node = node->data.fn_call_expr.params.at(0); + + ScopeCImport *cimport_scope = create_cimport_scope(ira->codegen, node, instruction->base.base.scope); + + // Execute the C import block like an inline function + ZigType *void_type = ira->codegen->builtin_types.entry_void; + ZigValue *cimport_result; + ZigValue *result_ptr; + create_result_ptr(ira->codegen, void_type, &cimport_result, &result_ptr); + if ((err = ir_eval_const_value(ira->codegen, &cimport_scope->base, block_node, result_ptr, + ira->new_irb.exec->backward_branch_count, ira->new_irb.exec->backward_branch_quota, nullptr, + &cimport_scope->buf, block_node, nullptr, nullptr, nullptr, UndefBad))) + { + return ira->codegen->invalid_inst_gen; + } + if (type_is_invalid(cimport_result->type)) + return ira->codegen->invalid_inst_gen; + + ZigPackage *cur_scope_pkg = scope_package(instruction->base.base.scope); + Buf *namespace_name = buf_sprintf("%s.cimport:%" ZIG_PRI_usize ":%" ZIG_PRI_usize, + buf_ptr(&cur_scope_pkg->pkg_path), node->line + 1, node->column + 1); + + ZigPackage *cimport_pkg = new_anonymous_package(); + cimport_pkg->package_table.put(buf_create_from_str("builtin"), ira->codegen->compile_var_package); + cimport_pkg->package_table.put(buf_create_from_str("std"), ira->codegen->std_package); + buf_init_from_buf(&cimport_pkg->pkg_path, namespace_name); + + const char *out_zig_path_ptr; + size_t out_zig_path_len; + Stage2ErrorMsg *errors_ptr; + size_t errors_len; + if ((err = stage2_cimport(&ira->codegen->stage1, + buf_ptr(&cimport_scope->buf), buf_len(&cimport_scope->buf), + &out_zig_path_ptr, &out_zig_path_len, + &errors_ptr, &errors_len))) + { + if (err != ErrorCCompileErrors) { + ir_add_error_node(ira, node, buf_sprintf("C import failed: %s", err_str(err))); + return ira->codegen->invalid_inst_gen; + } + + ErrorMsg *parent_err_msg = ir_add_error_node(ira, node, buf_sprintf("C import failed")); + if (!ira->codegen->stage1.link_libc) { + add_error_note(ira->codegen, parent_err_msg, node, + buf_sprintf("libc headers not available; compilation does not link against libc")); + } + for (size_t i = 0; i < errors_len; i += 1) { + Stage2ErrorMsg *clang_err = &errors_ptr[i]; + // Clang can emit "too many errors, stopping now", in which case `source` and `filename_ptr` are null + if (clang_err->source && clang_err->filename_ptr) { + ErrorMsg *err_msg = err_msg_create_with_offset( + clang_err->filename_ptr ? + buf_create_from_mem(clang_err->filename_ptr, clang_err->filename_len) : buf_alloc(), + clang_err->line, clang_err->column, clang_err->offset, clang_err->source, + buf_create_from_mem(clang_err->msg_ptr, clang_err->msg_len)); + err_msg_add_note(parent_err_msg, err_msg); + } + } + + return ira->codegen->invalid_inst_gen; + } + Buf *out_zig_path = buf_create_from_mem(out_zig_path_ptr, out_zig_path_len); + + Buf *import_code = buf_alloc(); + if ((err = file_fetch(ira->codegen, out_zig_path, import_code))) { + ir_add_error_node(ira, node, + buf_sprintf("unable to open '%s': %s", buf_ptr(out_zig_path), err_str(err))); + return ira->codegen->invalid_inst_gen; + } + ZigType *child_import = add_source_file(ira->codegen, cimport_pkg, out_zig_path, + import_code, SourceKindCImport); + return ir_const_type(ira, &instruction->base.base, child_import); +} + +static IrInstGen *ir_analyze_instruction_c_include(IrAnalyze *ira, IrInstSrcCInclude *instruction) { + IrInstGen *name_value = instruction->name->child; + if (type_is_invalid(name_value->value->type)) + return ira->codegen->invalid_inst_gen; + + Buf *include_name = ir_resolve_str(ira, name_value); + if (!include_name) + return ira->codegen->invalid_inst_gen; + + Buf *c_import_buf = ira->new_irb.exec->c_import_buf; + // We check for this error in pass1 + assert(c_import_buf); + + buf_appendf(c_import_buf, "#include <%s>\n", buf_ptr(include_name)); + + return ir_const_void(ira, &instruction->base.base); +} + +static IrInstGen *ir_analyze_instruction_c_define(IrAnalyze *ira, IrInstSrcCDefine *instruction) { + IrInstGen *name = instruction->name->child; + if (type_is_invalid(name->value->type)) + return ira->codegen->invalid_inst_gen; + + Buf *define_name = ir_resolve_str(ira, name); + if (!define_name) + return ira->codegen->invalid_inst_gen; + + IrInstGen *value = instruction->value->child; + if (type_is_invalid(value->value->type)) + return ira->codegen->invalid_inst_gen; + + Buf *define_value = nullptr; + // The second parameter is either a string or void (equivalent to "") + if (value->value->type->id != ZigTypeIdVoid) { + define_value = ir_resolve_str(ira, value); + if (!define_value) + return ira->codegen->invalid_inst_gen; + } + + Buf *c_import_buf = ira->new_irb.exec->c_import_buf; + // We check for this error in pass1 + assert(c_import_buf); + + buf_appendf(c_import_buf, "#define %s %s\n", buf_ptr(define_name), + define_value ? buf_ptr(define_value) : ""); + + return ir_const_void(ira, &instruction->base.base); +} + +static IrInstGen *ir_analyze_instruction_c_undef(IrAnalyze *ira, IrInstSrcCUndef *instruction) { + IrInstGen *name = instruction->name->child; + if (type_is_invalid(name->value->type)) + return ira->codegen->invalid_inst_gen; + + Buf *undef_name = ir_resolve_str(ira, name); + if (!undef_name) + return ira->codegen->invalid_inst_gen; + + Buf *c_import_buf = ira->new_irb.exec->c_import_buf; + // We check for this error in pass1 + assert(c_import_buf); + + buf_appendf(c_import_buf, "#undef %s\n", buf_ptr(undef_name)); + + return ir_const_void(ira, &instruction->base.base); +} + +static IrInstGen *ir_analyze_instruction_embed_file(IrAnalyze *ira, IrInstSrcEmbedFile *instruction) { + IrInstGen *name = instruction->name->child; + if (type_is_invalid(name->value->type)) + return ira->codegen->invalid_inst_gen; + + Buf *rel_file_path = ir_resolve_str(ira, name); + if (!rel_file_path) + return ira->codegen->invalid_inst_gen; + + ZigType *import = get_scope_import(instruction->base.base.scope); + // figure out absolute path to resource + Buf source_dir_path = BUF_INIT; + os_path_dirname(import->data.structure.root_struct->path, &source_dir_path); + + Buf *resolve_paths[] = { + &source_dir_path, + rel_file_path, + }; + Buf *file_path = buf_alloc(); + *file_path = os_path_resolve(resolve_paths, 2); + + // load from file system into const expr + Buf *file_contents = buf_alloc(); + Error err; + if ((err = file_fetch(ira->codegen, file_path, file_contents))) { + if (err == ErrorFileNotFound) { + ir_add_error(ira, &instruction->name->base, + buf_sprintf("unable to find '%s'", buf_ptr(file_path))); + return ira->codegen->invalid_inst_gen; + } else { + ir_add_error(ira, &instruction->name->base, + buf_sprintf("unable to open '%s': %s", buf_ptr(file_path), err_str(err))); + return ira->codegen->invalid_inst_gen; + } + } + + IrInstGen *result = ir_const(ira, &instruction->base.base, nullptr); + init_const_str_lit(ira->codegen, result->value, file_contents); + return result; +} + +static IrInstGen *ir_analyze_instruction_cmpxchg(IrAnalyze *ira, IrInstSrcCmpxchg *instruction) { + ZigType *operand_type = ir_resolve_atomic_operand_type(ira, instruction->type_value->child); + if (type_is_invalid(operand_type)) + return ira->codegen->invalid_inst_gen; + + if (operand_type->id == ZigTypeIdFloat) { + ir_add_error(ira, &instruction->type_value->child->base, + buf_sprintf("expected bool, integer, enum or pointer type, found '%s'", buf_ptr(&operand_type->name))); + return ira->codegen->invalid_inst_gen; + } + + IrInstGen *ptr = instruction->ptr->child; + if (type_is_invalid(ptr->value->type)) + return ira->codegen->invalid_inst_gen; + + // TODO let this be volatile + ZigType *ptr_type = get_pointer_to_type(ira->codegen, operand_type, false); + IrInstGen *casted_ptr = ir_implicit_cast2(ira, &instruction->ptr->base, ptr, ptr_type); + if (type_is_invalid(casted_ptr->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *cmp_value = instruction->cmp_value->child; + if (type_is_invalid(cmp_value->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *new_value = instruction->new_value->child; + if (type_is_invalid(new_value->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *success_order_value = instruction->success_order_value->child; + if (type_is_invalid(success_order_value->value->type)) + return ira->codegen->invalid_inst_gen; + + AtomicOrder success_order; + if (!ir_resolve_atomic_order(ira, success_order_value, &success_order)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *failure_order_value = instruction->failure_order_value->child; + if (type_is_invalid(failure_order_value->value->type)) + return ira->codegen->invalid_inst_gen; + + AtomicOrder failure_order; + if (!ir_resolve_atomic_order(ira, failure_order_value, &failure_order)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *casted_cmp_value = ir_implicit_cast2(ira, &instruction->cmp_value->base, cmp_value, operand_type); + if (type_is_invalid(casted_cmp_value->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *casted_new_value = ir_implicit_cast2(ira, &instruction->new_value->base, new_value, operand_type); + if (type_is_invalid(casted_new_value->value->type)) + return ira->codegen->invalid_inst_gen; + + if (success_order < AtomicOrderMonotonic) { + ir_add_error(ira, &success_order_value->base, + buf_sprintf("success atomic ordering must be Monotonic or stricter")); + return ira->codegen->invalid_inst_gen; + } + if (failure_order < AtomicOrderMonotonic) { + ir_add_error(ira, &failure_order_value->base, + buf_sprintf("failure atomic ordering must be Monotonic or stricter")); + return ira->codegen->invalid_inst_gen; + } + if (failure_order > success_order) { + ir_add_error(ira, &failure_order_value->base, + buf_sprintf("failure atomic ordering must be no stricter than success")); + return ira->codegen->invalid_inst_gen; + } + if (failure_order == AtomicOrderRelease || failure_order == AtomicOrderAcqRel) { + ir_add_error(ira, &failure_order_value->base, + buf_sprintf("failure atomic ordering must not be Release or AcqRel")); + return ira->codegen->invalid_inst_gen; + } + + ZigType *result_type = get_optional_type(ira->codegen, operand_type); + + // special case zero bit types + switch (type_has_one_possible_value(ira->codegen, operand_type)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_inst_gen; + case OnePossibleValueYes: { + IrInstGen *result = ir_const(ira, &instruction->base.base, result_type); + set_optional_value_to_null(result->value); + return result; + } + case OnePossibleValueNo: + break; + } + + if (instr_is_comptime(casted_ptr) && casted_ptr->value->data.x_ptr.mut != ConstPtrMutRuntimeVar && + instr_is_comptime(casted_cmp_value) && instr_is_comptime(casted_new_value)) { + ZigValue *ptr_val = ir_resolve_const(ira, casted_ptr, UndefBad); + if (ptr_val == nullptr) + return ira->codegen->invalid_inst_gen; + + ZigValue *stored_val = const_ptr_pointee(ira, ira->codegen, ptr_val, instruction->base.base.source_node); + if (stored_val == nullptr) + return ira->codegen->invalid_inst_gen; + + ZigValue *expected_val = ir_resolve_const(ira, casted_cmp_value, UndefBad); + if (expected_val == nullptr) + return ira->codegen->invalid_inst_gen; + + ZigValue *new_val = ir_resolve_const(ira, casted_new_value, UndefBad); + if (new_val == nullptr) + return ira->codegen->invalid_inst_gen; + + bool eql = const_values_equal(ira->codegen, stored_val, expected_val); + IrInstGen *result = ir_const(ira, &instruction->base.base, result_type); + if (eql) { + copy_const_val(ira->codegen, stored_val, new_val); + set_optional_value_to_null(result->value); + } else { + set_optional_payload(result->value, stored_val); + } + return result; + } + + IrInstGen *result_loc; + if (handle_is_ptr(ira->codegen, result_type)) { + result_loc = ir_resolve_result(ira, &instruction->base.base, instruction->result_loc, + result_type, nullptr, true, true); + if (type_is_invalid(result_loc->value->type) || result_loc->value->type->id == ZigTypeIdUnreachable) { + return result_loc; + } + } else { + result_loc = nullptr; + } + + return ir_build_cmpxchg_gen(ira, &instruction->base.base, result_type, + casted_ptr, casted_cmp_value, casted_new_value, + success_order, failure_order, instruction->is_weak, result_loc); +} + +static IrInstGen *ir_analyze_instruction_fence(IrAnalyze *ira, IrInstSrcFence *instruction) { + IrInstGen *order_inst = instruction->order->child; + if (type_is_invalid(order_inst->value->type)) + return ira->codegen->invalid_inst_gen; + + AtomicOrder order; + if (!ir_resolve_atomic_order(ira, order_inst, &order)) + return ira->codegen->invalid_inst_gen; + + if (order < AtomicOrderAcquire) { + ir_add_error(ira, &order_inst->base, + buf_sprintf("atomic ordering must be Acquire or stricter")); + return ira->codegen->invalid_inst_gen; + } + + return ir_build_fence_gen(ira, &instruction->base.base, order); +} + +static IrInstGen *ir_analyze_instruction_truncate(IrAnalyze *ira, IrInstSrcTruncate *instruction) { + IrInstGen *dest_type_value = instruction->dest_type->child; + ZigType *dest_type = ir_resolve_type(ira, dest_type_value); + if (type_is_invalid(dest_type)) + return ira->codegen->invalid_inst_gen; + + if (dest_type->id != ZigTypeIdInt && + dest_type->id != ZigTypeIdComptimeInt) + { + ir_add_error(ira, &dest_type_value->base, buf_sprintf("expected integer type, found '%s'", buf_ptr(&dest_type->name))); + return ira->codegen->invalid_inst_gen; + } + + IrInstGen *target = instruction->target->child; + ZigType *src_type = target->value->type; + if (type_is_invalid(src_type)) + return ira->codegen->invalid_inst_gen; + + if (src_type->id != ZigTypeIdInt && + src_type->id != ZigTypeIdComptimeInt) + { + ir_add_error(ira, &target->base, buf_sprintf("expected integer type, found '%s'", buf_ptr(&src_type->name))); + return ira->codegen->invalid_inst_gen; + } + + if (dest_type->id == ZigTypeIdComptimeInt) { + return ir_implicit_cast2(ira, &instruction->target->base, target, dest_type); + } + + if (instr_is_comptime(target)) { + ZigValue *val = ir_resolve_const(ira, target, UndefBad); + if (val == nullptr) + return ira->codegen->invalid_inst_gen; + + IrInstGen *result = ir_const(ira, &instruction->base.base, dest_type); + bigint_truncate(&result->value->data.x_bigint, &val->data.x_bigint, + dest_type->data.integral.bit_count, dest_type->data.integral.is_signed); + return result; + } + + if (src_type->data.integral.bit_count == 0 || dest_type->data.integral.bit_count == 0) { + IrInstGen *result = ir_const(ira, &instruction->base.base, dest_type); + bigint_init_unsigned(&result->value->data.x_bigint, 0); + return result; + } + + if (src_type->data.integral.is_signed != dest_type->data.integral.is_signed) { + const char *sign_str = dest_type->data.integral.is_signed ? "signed" : "unsigned"; + ir_add_error(ira, &target->base, buf_sprintf("expected %s integer type, found '%s'", sign_str, buf_ptr(&src_type->name))); + return ira->codegen->invalid_inst_gen; + } else if (src_type->data.integral.bit_count < dest_type->data.integral.bit_count) { + ir_add_error(ira, &target->base, buf_sprintf("type '%s' has fewer bits than destination type '%s'", + buf_ptr(&src_type->name), buf_ptr(&dest_type->name))); + return ira->codegen->invalid_inst_gen; + } + + return ir_build_truncate_gen(ira, &instruction->base.base, dest_type, target); +} + +static IrInstGen *ir_analyze_instruction_int_cast(IrAnalyze *ira, IrInstSrcIntCast *instruction) { + ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child); + if (type_is_invalid(dest_type)) + return ira->codegen->invalid_inst_gen; + + if (dest_type->id != ZigTypeIdInt && dest_type->id != ZigTypeIdComptimeInt) { + ir_add_error(ira, &instruction->dest_type->base, buf_sprintf("expected integer type, found '%s'", buf_ptr(&dest_type->name))); + return ira->codegen->invalid_inst_gen; + } + + IrInstGen *target = instruction->target->child; + if (type_is_invalid(target->value->type)) + return ira->codegen->invalid_inst_gen; + + if (target->value->type->id != ZigTypeIdInt && target->value->type->id != ZigTypeIdComptimeInt) { + ir_add_error(ira, &instruction->target->base, buf_sprintf("expected integer type, found '%s'", + buf_ptr(&target->value->type->name))); + return ira->codegen->invalid_inst_gen; + } + + if (instr_is_comptime(target) || dest_type->id == ZigTypeIdComptimeInt) { + ZigValue *val = ir_resolve_const(ira, target, UndefBad); + if (val == nullptr) + return ira->codegen->invalid_inst_gen; + + return ir_implicit_cast2(ira, &instruction->target->base, target, dest_type); + } + + return ir_analyze_widen_or_shorten(ira, &instruction->base.base, target, dest_type); +} + +static IrInstGen *ir_analyze_instruction_float_cast(IrAnalyze *ira, IrInstSrcFloatCast *instruction) { + ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child); + if (type_is_invalid(dest_type)) + return ira->codegen->invalid_inst_gen; + + if (dest_type->id != ZigTypeIdFloat && dest_type->id != ZigTypeIdComptimeFloat) { + ir_add_error(ira, &instruction->dest_type->base, + buf_sprintf("expected float type, found '%s'", buf_ptr(&dest_type->name))); + return ira->codegen->invalid_inst_gen; + } + + IrInstGen *target = instruction->target->child; + if (type_is_invalid(target->value->type)) + return ira->codegen->invalid_inst_gen; + + if (target->value->type->id == ZigTypeIdComptimeInt || + target->value->type->id == ZigTypeIdComptimeFloat) + { + if (ir_num_lit_fits_in_other_type(ira, target, dest_type, true)) { + CastOp op; + if (target->value->type->id == ZigTypeIdComptimeInt) { + op = CastOpIntToFloat; + } else { + op = CastOpNumLitToConcrete; + } + return ir_resolve_cast(ira, &instruction->base.base, target, dest_type, op); + } else { + return ira->codegen->invalid_inst_gen; + } + } + + if (target->value->type->id != ZigTypeIdFloat) { + ir_add_error(ira, &instruction->target->base, buf_sprintf("expected float type, found '%s'", + buf_ptr(&target->value->type->name))); + return ira->codegen->invalid_inst_gen; + } + + if (instr_is_comptime(target) || dest_type->id == ZigTypeIdComptimeFloat) { + ZigValue *val = ir_resolve_const(ira, target, UndefBad); + if (val == nullptr) + return ira->codegen->invalid_inst_gen; + + return ir_analyze_widen_or_shorten(ira, &instruction->target->base, target, dest_type); + } + + return ir_analyze_widen_or_shorten(ira, &instruction->base.base, target, dest_type); +} + +static IrInstGen *ir_analyze_instruction_err_set_cast(IrAnalyze *ira, IrInstSrcErrSetCast *instruction) { + ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child); + if (type_is_invalid(dest_type)) + return ira->codegen->invalid_inst_gen; + + if (dest_type->id != ZigTypeIdErrorSet) { + ir_add_error(ira, &instruction->dest_type->base, + buf_sprintf("expected error set type, found '%s'", buf_ptr(&dest_type->name))); + return ira->codegen->invalid_inst_gen; + } + + IrInstGen *target = instruction->target->child; + if (type_is_invalid(target->value->type)) + return ira->codegen->invalid_inst_gen; + + if (target->value->type->id != ZigTypeIdErrorSet) { + ir_add_error(ira, &instruction->target->base, + buf_sprintf("expected error set type, found '%s'", buf_ptr(&target->value->type->name))); + return ira->codegen->invalid_inst_gen; + } + + return ir_analyze_err_set_cast(ira, &instruction->base.base, target, dest_type); +} + +static Error resolve_ptr_align(IrAnalyze *ira, ZigType *ty, uint32_t *result_align) { + Error err; + + ZigType *ptr_type; + if (is_slice(ty)) { + TypeStructField *ptr_field = ty->data.structure.fields[slice_ptr_index]; + ptr_type = resolve_struct_field_type(ira->codegen, ptr_field); + } else { + ptr_type = get_src_ptr_type(ty); + } + assert(ptr_type != nullptr); + if (ptr_type->id == ZigTypeIdPointer) { + if ((err = type_resolve(ira->codegen, ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) + return err; + } else if (is_slice(ptr_type)) { + TypeStructField *ptr_field = ptr_type->data.structure.fields[slice_ptr_index]; + ZigType *slice_ptr_type = resolve_struct_field_type(ira->codegen, ptr_field); + if ((err = type_resolve(ira->codegen, slice_ptr_type->data.pointer.child_type, ResolveStatusAlignmentKnown))) + return err; + } + + *result_align = get_ptr_align(ira->codegen, ty); + return ErrorNone; +} + +static IrInstGen *ir_analyze_instruction_int_to_float(IrAnalyze *ira, IrInstSrcIntToFloat *instruction) { + ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child); + if (type_is_invalid(dest_type)) + return ira->codegen->invalid_inst_gen; + + if (dest_type->id != ZigTypeIdFloat && dest_type->id != ZigTypeIdComptimeFloat) { + ir_add_error(ira, &instruction->dest_type->base, + buf_sprintf("expected float type, found '%s'", buf_ptr(&dest_type->name))); + return ira->codegen->invalid_inst_gen; + } + + IrInstGen *target = instruction->target->child; + if (type_is_invalid(target->value->type)) + return ira->codegen->invalid_inst_gen; + + if (target->value->type->id != ZigTypeIdInt && target->value->type->id != ZigTypeIdComptimeInt) { + ir_add_error(ira, &instruction->target->base, buf_sprintf("expected int type, found '%s'", + buf_ptr(&target->value->type->name))); + return ira->codegen->invalid_inst_gen; + } + + return ir_resolve_cast(ira, &instruction->base.base, target, dest_type, CastOpIntToFloat); +} + +static IrInstGen *ir_analyze_instruction_float_to_int(IrAnalyze *ira, IrInstSrcFloatToInt *instruction) { + ZigType *dest_type = ir_resolve_type(ira, instruction->dest_type->child); + if (type_is_invalid(dest_type)) + return ira->codegen->invalid_inst_gen; + + if (dest_type->id != ZigTypeIdInt && dest_type->id != ZigTypeIdComptimeInt) { + ir_add_error(ira, &instruction->dest_type->base, buf_sprintf("expected integer type, found '%s'", buf_ptr(&dest_type->name))); + return ira->codegen->invalid_inst_gen; + } + + IrInstGen *target = instruction->target->child; + if (type_is_invalid(target->value->type)) + return ira->codegen->invalid_inst_gen; + + if (target->value->type->id == ZigTypeIdComptimeInt) { + return ir_implicit_cast(ira, target, dest_type); + } + + if (target->value->type->id != ZigTypeIdFloat && target->value->type->id != ZigTypeIdComptimeFloat) { + ir_add_error_node(ira, target->base.source_node, buf_sprintf("expected float type, found '%s'", + buf_ptr(&target->value->type->name))); + return ira->codegen->invalid_inst_gen; + } + + return ir_resolve_cast(ira, &instruction->base.base, target, dest_type, CastOpFloatToInt); +} + +static IrInstGen *ir_analyze_instruction_err_to_int(IrAnalyze *ira, IrInstSrcErrToInt *instruction) { + IrInstGen *target = instruction->target->child; + if (type_is_invalid(target->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *casted_target; + if (target->value->type->id == ZigTypeIdErrorSet) { + casted_target = target; + } else { + casted_target = ir_implicit_cast(ira, target, ira->codegen->builtin_types.entry_global_error_set); + if (type_is_invalid(casted_target->value->type)) + return ira->codegen->invalid_inst_gen; + } + + return ir_analyze_err_to_int(ira, &instruction->base.base, casted_target, ira->codegen->err_tag_type); +} + +static IrInstGen *ir_analyze_instruction_int_to_err(IrAnalyze *ira, IrInstSrcIntToErr *instruction) { + IrInstGen *target = instruction->target->child; + if (type_is_invalid(target->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *casted_target = ir_implicit_cast(ira, target, ira->codegen->err_tag_type); + if (type_is_invalid(casted_target->value->type)) + return ira->codegen->invalid_inst_gen; + + return ir_analyze_int_to_err(ira, &instruction->base.base, casted_target, ira->codegen->builtin_types.entry_global_error_set); +} + +static IrInstGen *ir_analyze_instruction_bool_to_int(IrAnalyze *ira, IrInstSrcBoolToInt *instruction) { + IrInstGen *target = instruction->target->child; + if (type_is_invalid(target->value->type)) + return ira->codegen->invalid_inst_gen; + + if (target->value->type->id != ZigTypeIdBool) { + ir_add_error(ira, &instruction->target->base, buf_sprintf("expected bool, found '%s'", + buf_ptr(&target->value->type->name))); + return ira->codegen->invalid_inst_gen; + } + + if (instr_is_comptime(target)) { + bool is_true; + if (!ir_resolve_bool(ira, target, &is_true)) + return ira->codegen->invalid_inst_gen; + + return ir_const_unsigned(ira, &instruction->base.base, is_true ? 1 : 0); + } + + ZigType *u1_type = get_int_type(ira->codegen, false, 1); + return ir_resolve_cast(ira, &instruction->base.base, target, u1_type, CastOpBoolToInt); +} + +static IrInstGen *ir_analyze_instruction_vector_type(IrAnalyze *ira, IrInstSrcVectorType *instruction) { + uint64_t len; + if (!ir_resolve_unsigned(ira, instruction->len->child, ira->codegen->builtin_types.entry_u32, &len)) + return ira->codegen->invalid_inst_gen; + + ZigType *elem_type = ir_resolve_vector_elem_type(ira, instruction->elem_type->child); + if (type_is_invalid(elem_type)) + return ira->codegen->invalid_inst_gen; + + ZigType *vector_type = get_vector_type(ira->codegen, len, elem_type); + + return ir_const_type(ira, &instruction->base.base, vector_type); +} + +static IrInstGen *ir_analyze_shuffle_vector(IrAnalyze *ira, IrInst* source_instr, + ZigType *scalar_type, IrInstGen *a, IrInstGen *b, IrInstGen *mask) +{ + Error err; + ir_assert(source_instr && scalar_type && a && b && mask, source_instr); + + if ((err = ir_validate_vector_elem_type(ira, source_instr->source_node, scalar_type))) + return ira->codegen->invalid_inst_gen; + + uint32_t len_mask; + if (mask->value->type->id == ZigTypeIdVector) { + len_mask = mask->value->type->data.vector.len; + } else if (mask->value->type->id == ZigTypeIdArray) { + len_mask = mask->value->type->data.array.len; + } else { + ir_add_error(ira, &mask->base, + buf_sprintf("expected vector or array, found '%s'", + buf_ptr(&mask->value->type->name))); + return ira->codegen->invalid_inst_gen; + } + mask = ir_implicit_cast(ira, mask, get_vector_type(ira->codegen, len_mask, + ira->codegen->builtin_types.entry_i32)); + if (type_is_invalid(mask->value->type)) + return ira->codegen->invalid_inst_gen; + + uint32_t len_a; + if (a->value->type->id == ZigTypeIdVector) { + len_a = a->value->type->data.vector.len; + } else if (a->value->type->id == ZigTypeIdArray) { + len_a = a->value->type->data.array.len; + } else if (a->value->type->id == ZigTypeIdUndefined) { + len_a = UINT32_MAX; + } else { + ir_add_error(ira, &a->base, + buf_sprintf("expected vector or array with element type '%s', found '%s'", + buf_ptr(&scalar_type->name), + buf_ptr(&a->value->type->name))); + return ira->codegen->invalid_inst_gen; + } + + uint32_t len_b; + if (b->value->type->id == ZigTypeIdVector) { + len_b = b->value->type->data.vector.len; + } else if (b->value->type->id == ZigTypeIdArray) { + len_b = b->value->type->data.array.len; + } else if (b->value->type->id == ZigTypeIdUndefined) { + len_b = UINT32_MAX; + } else { + ir_add_error(ira, &b->base, + buf_sprintf("expected vector or array with element type '%s', found '%s'", + buf_ptr(&scalar_type->name), + buf_ptr(&b->value->type->name))); + return ira->codegen->invalid_inst_gen; + } + + if (len_a == UINT32_MAX && len_b == UINT32_MAX) { + return ir_const_undef(ira, &a->base, get_vector_type(ira->codegen, len_mask, scalar_type)); + } + + if (len_a == UINT32_MAX) { + len_a = len_b; + a = ir_const_undef(ira, &a->base, get_vector_type(ira->codegen, len_a, scalar_type)); + } else { + a = ir_implicit_cast(ira, a, get_vector_type(ira->codegen, len_a, scalar_type)); + if (type_is_invalid(a->value->type)) + return ira->codegen->invalid_inst_gen; + } + + if (len_b == UINT32_MAX) { + len_b = len_a; + b = ir_const_undef(ira, &b->base, get_vector_type(ira->codegen, len_b, scalar_type)); + } else { + b = ir_implicit_cast(ira, b, get_vector_type(ira->codegen, len_b, scalar_type)); + if (type_is_invalid(b->value->type)) + return ira->codegen->invalid_inst_gen; + } + + ZigValue *mask_val = ir_resolve_const(ira, mask, UndefOk); + if (mask_val == nullptr) + return ira->codegen->invalid_inst_gen; + + expand_undef_array(ira->codegen, mask_val); + + for (uint32_t i = 0; i < len_mask; i += 1) { + ZigValue *mask_elem_val = &mask_val->data.x_array.data.s_none.elements[i]; + if (mask_elem_val->special == ConstValSpecialUndef) + continue; + int32_t v_i32 = bigint_as_signed(&mask_elem_val->data.x_bigint); + uint32_t v; + IrInstGen *chosen_operand; + if (v_i32 >= 0) { + v = (uint32_t)v_i32; + chosen_operand = a; + } else { + v = (uint32_t)~v_i32; + chosen_operand = b; + } + if (v >= chosen_operand->value->type->data.vector.len) { + ErrorMsg *msg = ir_add_error(ira, &mask->base, + buf_sprintf("mask index '%u' has out-of-bounds selection", i)); + add_error_note(ira->codegen, msg, chosen_operand->base.source_node, + buf_sprintf("selected index '%u' out of bounds of %s", v, + buf_ptr(&chosen_operand->value->type->name))); + if (chosen_operand == a && v < len_a + len_b) { + add_error_note(ira->codegen, msg, b->base.source_node, + buf_create_from_str("selections from the second vector are specified with negative numbers")); + } + return ira->codegen->invalid_inst_gen; + } + } + + ZigType *result_type = get_vector_type(ira->codegen, len_mask, scalar_type); + if (instr_is_comptime(a) && instr_is_comptime(b)) { + ZigValue *a_val = ir_resolve_const(ira, a, UndefOk); + if (a_val == nullptr) + return ira->codegen->invalid_inst_gen; + + ZigValue *b_val = ir_resolve_const(ira, b, UndefOk); + if (b_val == nullptr) + return ira->codegen->invalid_inst_gen; + + expand_undef_array(ira->codegen, a_val); + expand_undef_array(ira->codegen, b_val); + + IrInstGen *result = ir_const(ira, source_instr, result_type); + result->value->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(len_mask); + for (uint32_t i = 0; i < mask_val->type->data.vector.len; i += 1) { + ZigValue *mask_elem_val = &mask_val->data.x_array.data.s_none.elements[i]; + ZigValue *result_elem_val = &result->value->data.x_array.data.s_none.elements[i]; + if (mask_elem_val->special == ConstValSpecialUndef) { + result_elem_val->special = ConstValSpecialUndef; + continue; + } + int32_t v = bigint_as_signed(&mask_elem_val->data.x_bigint); + // We've already checked for and emitted compile errors for index out of bounds here. + ZigValue *src_elem_val = (v >= 0) ? + &a->value->data.x_array.data.s_none.elements[v] : + &b->value->data.x_array.data.s_none.elements[~v]; + copy_const_val(ira->codegen, result_elem_val, src_elem_val); + + ir_assert(result_elem_val->special == ConstValSpecialStatic, source_instr); + } + result->value->special = ConstValSpecialStatic; + return result; + } + + // All static analysis passed, and not comptime. + // For runtime codegen, vectors a and b must be the same length. Here we + // recursively @shuffle the smaller vector to append undefined elements + // to it up to the length of the longer vector. This recursion terminates + // in 1 call because these calls to ir_analyze_shuffle_vector guarantee + // len_a == len_b. + if (len_a != len_b) { + uint32_t len_min = min(len_a, len_b); + uint32_t len_max = max(len_a, len_b); + + IrInstGen *expand_mask = ir_const(ira, &mask->base, + get_vector_type(ira->codegen, len_max, ira->codegen->builtin_types.entry_i32)); + expand_mask->value->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(len_max); + uint32_t i = 0; + for (; i < len_min; i += 1) + bigint_init_unsigned(&expand_mask->value->data.x_array.data.s_none.elements[i].data.x_bigint, i); + for (; i < len_max; i += 1) + bigint_init_signed(&expand_mask->value->data.x_array.data.s_none.elements[i].data.x_bigint, -1); + + IrInstGen *undef = ir_const_undef(ira, source_instr, + get_vector_type(ira->codegen, len_min, scalar_type)); + + if (len_b < len_a) { + b = ir_analyze_shuffle_vector(ira, source_instr, scalar_type, b, undef, expand_mask); + } else { + a = ir_analyze_shuffle_vector(ira, source_instr, scalar_type, a, undef, expand_mask); + } + } + + return ir_build_shuffle_vector_gen(ira, source_instr->scope, source_instr->source_node, + result_type, a, b, mask); +} + +static IrInstGen *ir_analyze_instruction_shuffle_vector(IrAnalyze *ira, IrInstSrcShuffleVector *instruction) { + ZigType *scalar_type = ir_resolve_vector_elem_type(ira, instruction->scalar_type->child); + if (type_is_invalid(scalar_type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *a = instruction->a->child; + if (type_is_invalid(a->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *b = instruction->b->child; + if (type_is_invalid(b->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *mask = instruction->mask->child; + if (type_is_invalid(mask->value->type)) + return ira->codegen->invalid_inst_gen; + + return ir_analyze_shuffle_vector(ira, &instruction->base.base, scalar_type, a, b, mask); +} + +static IrInstGen *ir_analyze_instruction_splat(IrAnalyze *ira, IrInstSrcSplat *instruction) { + Error err; + + IrInstGen *len = instruction->len->child; + if (type_is_invalid(len->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *scalar = instruction->scalar->child; + if (type_is_invalid(scalar->value->type)) + return ira->codegen->invalid_inst_gen; + + uint64_t len_u64; + if (!ir_resolve_unsigned(ira, len, ira->codegen->builtin_types.entry_u32, &len_u64)) + return ira->codegen->invalid_inst_gen; + uint32_t len_int = len_u64; + + if ((err = ir_validate_vector_elem_type(ira, scalar->base.source_node, scalar->value->type))) + return ira->codegen->invalid_inst_gen; + + ZigType *return_type = get_vector_type(ira->codegen, len_int, scalar->value->type); + + if (instr_is_comptime(scalar)) { + ZigValue *scalar_val = ir_resolve_const(ira, scalar, UndefOk); + if (scalar_val == nullptr) + return ira->codegen->invalid_inst_gen; + if (scalar_val->special == ConstValSpecialUndef) + return ir_const_undef(ira, &instruction->base.base, return_type); + + IrInstGen *result = ir_const(ira, &instruction->base.base, return_type); + result->value->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(len_int); + for (uint32_t i = 0; i < len_int; i += 1) { + copy_const_val(ira->codegen, &result->value->data.x_array.data.s_none.elements[i], scalar_val); + } + return result; + } + + return ir_build_splat_gen(ira, &instruction->base.base, return_type, scalar); +} + +static IrInstGen *ir_analyze_instruction_bool_not(IrAnalyze *ira, IrInstSrcBoolNot *instruction) { + IrInstGen *value = instruction->value->child; + if (type_is_invalid(value->value->type)) + return ira->codegen->invalid_inst_gen; + + ZigType *bool_type = ira->codegen->builtin_types.entry_bool; + + IrInstGen *casted_value = ir_implicit_cast(ira, value, bool_type); + if (type_is_invalid(casted_value->value->type)) + return ira->codegen->invalid_inst_gen; + + if (instr_is_comptime(casted_value)) { + ZigValue *value = ir_resolve_const(ira, casted_value, UndefBad); + if (value == nullptr) + return ira->codegen->invalid_inst_gen; + + return ir_const_bool(ira, &instruction->base.base, !value->data.x_bool); + } + + return ir_build_bool_not_gen(ira, &instruction->base.base, casted_value); +} + +static IrInstGen *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstSrcMemset *instruction) { + Error err; + + IrInstGen *dest_ptr = instruction->dest_ptr->child; + if (type_is_invalid(dest_ptr->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *byte_value = instruction->byte->child; + if (type_is_invalid(byte_value->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *count_value = instruction->count->child; + if (type_is_invalid(count_value->value->type)) + return ira->codegen->invalid_inst_gen; + + ZigType *dest_uncasted_type = dest_ptr->value->type; + bool dest_is_volatile = (dest_uncasted_type->id == ZigTypeIdPointer) && + dest_uncasted_type->data.pointer.is_volatile; + + ZigType *usize = ira->codegen->builtin_types.entry_usize; + ZigType *u8 = ira->codegen->builtin_types.entry_u8; + uint32_t dest_align; + if (dest_uncasted_type->id == ZigTypeIdPointer) { + if ((err = resolve_ptr_align(ira, dest_uncasted_type, &dest_align))) + return ira->codegen->invalid_inst_gen; + } else { + dest_align = get_abi_alignment(ira->codegen, u8); + } + ZigType *u8_ptr = get_pointer_to_type_extra(ira->codegen, u8, false, dest_is_volatile, + PtrLenUnknown, dest_align, 0, 0, false); + + IrInstGen *casted_dest_ptr = ir_implicit_cast(ira, dest_ptr, u8_ptr); + if (type_is_invalid(casted_dest_ptr->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *casted_byte = ir_implicit_cast(ira, byte_value, u8); + if (type_is_invalid(casted_byte->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *casted_count = ir_implicit_cast(ira, count_value, usize); + if (type_is_invalid(casted_count->value->type)) + return ira->codegen->invalid_inst_gen; + + // TODO test this at comptime with u8 and non-u8 types + if (instr_is_comptime(casted_dest_ptr) && + instr_is_comptime(casted_byte) && + instr_is_comptime(casted_count)) + { + ZigValue *dest_ptr_val = ir_resolve_const(ira, casted_dest_ptr, UndefBad); + if (dest_ptr_val == nullptr) + return ira->codegen->invalid_inst_gen; + + ZigValue *byte_val = ir_resolve_const(ira, casted_byte, UndefOk); + if (byte_val == nullptr) + return ira->codegen->invalid_inst_gen; + + ZigValue *count_val = ir_resolve_const(ira, casted_count, UndefBad); + if (count_val == nullptr) + return ira->codegen->invalid_inst_gen; + + if (casted_dest_ptr->value->data.x_ptr.special != ConstPtrSpecialHardCodedAddr && + casted_dest_ptr->value->data.x_ptr.mut != ConstPtrMutRuntimeVar) + { + ZigValue *dest_elements; + size_t start; + size_t bound_end; + switch (dest_ptr_val->data.x_ptr.special) { + case ConstPtrSpecialInvalid: + case ConstPtrSpecialDiscard: + zig_unreachable(); + case ConstPtrSpecialRef: + dest_elements = dest_ptr_val->data.x_ptr.data.ref.pointee; + start = 0; + bound_end = 1; + break; + case ConstPtrSpecialSubArray: + case ConstPtrSpecialBaseArray: + { + ZigValue *array_val = dest_ptr_val->data.x_ptr.data.base_array.array_val; + expand_undef_array(ira->codegen, array_val); + dest_elements = array_val->data.x_array.data.s_none.elements; + start = dest_ptr_val->data.x_ptr.data.base_array.elem_index; + bound_end = array_val->type->data.array.len; + break; + } + case ConstPtrSpecialBaseStruct: + zig_panic("TODO memset on const inner struct"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO memset on const inner error union code"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO memset on const inner error union payload"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO memset on const inner optional payload"); + case ConstPtrSpecialHardCodedAddr: + zig_unreachable(); + case ConstPtrSpecialFunction: + zig_panic("TODO memset on ptr cast from function"); + case ConstPtrSpecialNull: + zig_panic("TODO memset on null ptr"); + } + + size_t count = bigint_as_usize(&count_val->data.x_bigint); + size_t end = start + count; + if (end > bound_end) { + ir_add_error(ira, &count_value->base, buf_sprintf("out of bounds pointer access")); + return ira->codegen->invalid_inst_gen; + } + + for (size_t i = start; i < end; i += 1) { + copy_const_val(ira->codegen, &dest_elements[i], byte_val); + } + + return ir_const_void(ira, &instruction->base.base); + } + } + + return ir_build_memset_gen(ira, &instruction->base.base, casted_dest_ptr, casted_byte, casted_count); +} + +static IrInstGen *ir_analyze_instruction_memcpy(IrAnalyze *ira, IrInstSrcMemcpy *instruction) { + Error err; + + IrInstGen *dest_ptr = instruction->dest_ptr->child; + if (type_is_invalid(dest_ptr->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *src_ptr = instruction->src_ptr->child; + if (type_is_invalid(src_ptr->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *count_value = instruction->count->child; + if (type_is_invalid(count_value->value->type)) + return ira->codegen->invalid_inst_gen; + + ZigType *u8 = ira->codegen->builtin_types.entry_u8; + ZigType *dest_uncasted_type = dest_ptr->value->type; + ZigType *src_uncasted_type = src_ptr->value->type; + bool dest_is_volatile = (dest_uncasted_type->id == ZigTypeIdPointer) && + dest_uncasted_type->data.pointer.is_volatile; + bool src_is_volatile = (src_uncasted_type->id == ZigTypeIdPointer) && + src_uncasted_type->data.pointer.is_volatile; + + uint32_t dest_align; + if (dest_uncasted_type->id == ZigTypeIdPointer) { + if ((err = resolve_ptr_align(ira, dest_uncasted_type, &dest_align))) + return ira->codegen->invalid_inst_gen; + } else { + dest_align = get_abi_alignment(ira->codegen, u8); + } + + uint32_t src_align; + if (src_uncasted_type->id == ZigTypeIdPointer) { + if ((err = resolve_ptr_align(ira, src_uncasted_type, &src_align))) + return ira->codegen->invalid_inst_gen; + } else { + src_align = get_abi_alignment(ira->codegen, u8); + } + + ZigType *usize = ira->codegen->builtin_types.entry_usize; + ZigType *u8_ptr_mut = get_pointer_to_type_extra(ira->codegen, u8, false, dest_is_volatile, + PtrLenUnknown, dest_align, 0, 0, false); + ZigType *u8_ptr_const = get_pointer_to_type_extra(ira->codegen, u8, true, src_is_volatile, + PtrLenUnknown, src_align, 0, 0, false); + + IrInstGen *casted_dest_ptr = ir_implicit_cast(ira, dest_ptr, u8_ptr_mut); + if (type_is_invalid(casted_dest_ptr->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *casted_src_ptr = ir_implicit_cast(ira, src_ptr, u8_ptr_const); + if (type_is_invalid(casted_src_ptr->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *casted_count = ir_implicit_cast(ira, count_value, usize); + if (type_is_invalid(casted_count->value->type)) + return ira->codegen->invalid_inst_gen; + + // TODO test this at comptime with u8 and non-u8 types + // TODO test with dest ptr being a global runtime variable + if (instr_is_comptime(casted_dest_ptr) && + instr_is_comptime(casted_src_ptr) && + instr_is_comptime(casted_count)) + { + ZigValue *dest_ptr_val = ir_resolve_const(ira, casted_dest_ptr, UndefBad); + if (dest_ptr_val == nullptr) + return ira->codegen->invalid_inst_gen; + + ZigValue *src_ptr_val = ir_resolve_const(ira, casted_src_ptr, UndefBad); + if (src_ptr_val == nullptr) + return ira->codegen->invalid_inst_gen; + + ZigValue *count_val = ir_resolve_const(ira, casted_count, UndefBad); + if (count_val == nullptr) + return ira->codegen->invalid_inst_gen; + + if (dest_ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) { + size_t count = bigint_as_usize(&count_val->data.x_bigint); + + ZigValue *dest_elements; + size_t dest_start; + size_t dest_end; + switch (dest_ptr_val->data.x_ptr.special) { + case ConstPtrSpecialInvalid: + case ConstPtrSpecialDiscard: + zig_unreachable(); + case ConstPtrSpecialRef: + dest_elements = dest_ptr_val->data.x_ptr.data.ref.pointee; + dest_start = 0; + dest_end = 1; + break; + case ConstPtrSpecialSubArray: + case ConstPtrSpecialBaseArray: + { + ZigValue *array_val = dest_ptr_val->data.x_ptr.data.base_array.array_val; + expand_undef_array(ira->codegen, array_val); + dest_elements = array_val->data.x_array.data.s_none.elements; + dest_start = dest_ptr_val->data.x_ptr.data.base_array.elem_index; + dest_end = array_val->type->data.array.len; + break; + } + case ConstPtrSpecialBaseStruct: + zig_panic("TODO memcpy on const inner struct"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO memcpy on const inner error union code"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO memcpy on const inner error union payload"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO memcpy on const inner optional payload"); + case ConstPtrSpecialHardCodedAddr: + zig_unreachable(); + case ConstPtrSpecialFunction: + zig_panic("TODO memcpy on ptr cast from function"); + case ConstPtrSpecialNull: + zig_panic("TODO memcpy on null ptr"); + } + + if (dest_start + count > dest_end) { + ir_add_error(ira, &instruction->base.base, buf_sprintf("out of bounds pointer access")); + return ira->codegen->invalid_inst_gen; + } + + ZigValue *src_elements; + size_t src_start; + size_t src_end; + + switch (src_ptr_val->data.x_ptr.special) { + case ConstPtrSpecialInvalid: + case ConstPtrSpecialDiscard: + zig_unreachable(); + case ConstPtrSpecialRef: + src_elements = src_ptr_val->data.x_ptr.data.ref.pointee; + src_start = 0; + src_end = 1; + break; + case ConstPtrSpecialSubArray: + case ConstPtrSpecialBaseArray: + { + ZigValue *array_val = src_ptr_val->data.x_ptr.data.base_array.array_val; + expand_undef_array(ira->codegen, array_val); + src_elements = array_val->data.x_array.data.s_none.elements; + src_start = src_ptr_val->data.x_ptr.data.base_array.elem_index; + src_end = array_val->type->data.array.len; + break; + } + case ConstPtrSpecialBaseStruct: + zig_panic("TODO memcpy on const inner struct"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO memcpy on const inner error union code"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO memcpy on const inner error union payload"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO memcpy on const inner optional payload"); + case ConstPtrSpecialHardCodedAddr: + zig_unreachable(); + case ConstPtrSpecialFunction: + zig_panic("TODO memcpy on ptr cast from function"); + case ConstPtrSpecialNull: + zig_panic("TODO memcpy on null ptr"); + } + + if (src_start + count > src_end) { + ir_add_error(ira, &instruction->base.base, buf_sprintf("out of bounds pointer access")); + return ira->codegen->invalid_inst_gen; + } + + // TODO check for noalias violations - this should be generalized to work for any function + + for (size_t i = 0; i < count; i += 1) { + copy_const_val(ira->codegen, &dest_elements[dest_start + i], &src_elements[src_start + i]); + } + + return ir_const_void(ira, &instruction->base.base); + } + } + + return ir_build_memcpy_gen(ira, &instruction->base.base, casted_dest_ptr, casted_src_ptr, casted_count); +} + +static ZigType *get_result_loc_type(IrAnalyze *ira, ResultLoc *result_loc) { + if (result_loc == nullptr) return nullptr; + + if (result_loc->id == ResultLocIdCast) { + return ir_resolve_type(ira, result_loc->source_instruction->child); + } + + return nullptr; +} + +static IrInstGen *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstSrcSlice *instruction) { + Error err; + + IrInstGen *ptr_ptr = instruction->ptr->child; + if (type_is_invalid(ptr_ptr->value->type)) + return ira->codegen->invalid_inst_gen; + + ZigType *ptr_ptr_type = ptr_ptr->value->type; + assert(ptr_ptr_type->id == ZigTypeIdPointer); + ZigType *array_type = ptr_ptr_type->data.pointer.child_type; + + IrInstGen *start = instruction->start->child; + if (type_is_invalid(start->value->type)) + return ira->codegen->invalid_inst_gen; + + ZigType *usize = ira->codegen->builtin_types.entry_usize; + IrInstGen *casted_start = ir_implicit_cast(ira, start, usize); + if (type_is_invalid(casted_start->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *end; + if (instruction->end) { + end = instruction->end->child; + if (type_is_invalid(end->value->type)) + return ira->codegen->invalid_inst_gen; + end = ir_implicit_cast(ira, end, usize); + if (type_is_invalid(end->value->type)) + return ira->codegen->invalid_inst_gen; + } else { + end = nullptr; + } + + ZigValue *slice_sentinel_val = nullptr; + ZigType *non_sentinel_slice_ptr_type; + ZigType *elem_type; + + bool generate_non_null_assert = false; + + if (array_type->id == ZigTypeIdArray) { + elem_type = array_type->data.array.child_type; + non_sentinel_slice_ptr_type = get_pointer_to_type_extra(ira->codegen, elem_type, + ptr_ptr_type->data.pointer.is_const, + ptr_ptr_type->data.pointer.is_volatile, + PtrLenUnknown, + ptr_ptr_type->data.pointer.explicit_alignment, 0, 0, false); + } else if (array_type->id == ZigTypeIdPointer) { + if (array_type->data.pointer.ptr_len == PtrLenSingle) { + ZigType *main_type = array_type->data.pointer.child_type; + if (main_type->id == ZigTypeIdArray) { + elem_type = main_type->data.pointer.child_type; + non_sentinel_slice_ptr_type = get_pointer_to_type_extra(ira->codegen, + elem_type, + array_type->data.pointer.is_const, array_type->data.pointer.is_volatile, + PtrLenUnknown, + array_type->data.pointer.explicit_alignment, 0, 0, false); + } else { + ir_add_error(ira, &instruction->base.base, buf_sprintf("slice of single-item pointer")); + return ira->codegen->invalid_inst_gen; + } + } else { + elem_type = array_type->data.pointer.child_type; + if (array_type->data.pointer.ptr_len == PtrLenC) { + array_type = adjust_ptr_len(ira->codegen, array_type, PtrLenUnknown); + + // C pointers are allowzero by default. + // However, we want to be able to slice them without generating an allowzero slice (see issue #4401). + // To achieve this, we generate a runtime safety check and make the slice type non-allowzero. + if (array_type->data.pointer.allow_zero) { + array_type = adjust_ptr_allow_zero(ira->codegen, array_type, false); + generate_non_null_assert = true; + } + } + ZigType *maybe_sentineled_slice_ptr_type = array_type; + non_sentinel_slice_ptr_type = adjust_ptr_sentinel(ira->codegen, maybe_sentineled_slice_ptr_type, nullptr); + if (!end) { + ir_add_error(ira, &instruction->base.base, buf_sprintf("slice of pointer must include end value")); + return ira->codegen->invalid_inst_gen; + } + } + } else if (is_slice(array_type)) { + ZigType *maybe_sentineled_slice_ptr_type = array_type->data.structure.fields[slice_ptr_index]->type_entry; + slice_sentinel_val = maybe_sentineled_slice_ptr_type->data.pointer.sentinel; + non_sentinel_slice_ptr_type = adjust_ptr_sentinel(ira->codegen, maybe_sentineled_slice_ptr_type, nullptr); + elem_type = non_sentinel_slice_ptr_type->data.pointer.child_type; + } else { + ir_add_error(ira, &instruction->base.base, + buf_sprintf("slice of non-array type '%s'", buf_ptr(&array_type->name))); + return ira->codegen->invalid_inst_gen; + } + + ZigValue *sentinel_val = nullptr; + if (instruction->sentinel) { + IrInstGen *uncasted_sentinel = instruction->sentinel->child; + if (type_is_invalid(uncasted_sentinel->value->type)) + return ira->codegen->invalid_inst_gen; + IrInstGen *sentinel = ir_implicit_cast(ira, uncasted_sentinel, elem_type); + if (type_is_invalid(sentinel->value->type)) + return ira->codegen->invalid_inst_gen; + sentinel_val = ir_resolve_const(ira, sentinel, UndefBad); + if (sentinel_val == nullptr) + return ira->codegen->invalid_inst_gen; + } + + ZigType *child_array_type = (array_type->id == ZigTypeIdPointer && + array_type->data.pointer.ptr_len == PtrLenSingle) ? array_type->data.pointer.child_type : array_type; + + ZigType *return_type; + + // If start index and end index are both comptime known, then the result type is a pointer to array + // not a slice. However, if the start or end index is a lazy value, and the result location is a slice, + // then the pointer-to-array would be casted to a slice anyway. So, we preserve the laziness of these + // values by making the return type a slice. + ZigType *res_loc_type = get_result_loc_type(ira, instruction->result_loc); + bool result_loc_is_slice = (res_loc_type != nullptr && is_slice(res_loc_type)); + bool end_is_known = !result_loc_is_slice && + ((end != nullptr && value_is_comptime(end->value)) || + (end == nullptr && child_array_type->id == ZigTypeIdArray)); + + ZigValue *array_sentinel = sentinel_val; + if (end_is_known) { + uint64_t end_scalar; + if (end != nullptr) { + ZigValue *end_val = ir_resolve_const(ira, end, UndefBad); + if (!end_val) + return ira->codegen->invalid_inst_gen; + end_scalar = bigint_as_u64(&end_val->data.x_bigint); + } else { + end_scalar = child_array_type->data.array.len; + } + array_sentinel = (child_array_type->id == ZigTypeIdArray && end_scalar == child_array_type->data.array.len) + ? child_array_type->data.array.sentinel : sentinel_val; + + if (value_is_comptime(casted_start->value)) { + ZigValue *start_val = ir_resolve_const(ira, casted_start, UndefBad); + if (!start_val) + return ira->codegen->invalid_inst_gen; + + uint64_t start_scalar = bigint_as_u64(&start_val->data.x_bigint); + + if (start_scalar > end_scalar) { + ir_add_error(ira, &instruction->base.base, buf_sprintf("out of bounds slice")); + return ira->codegen->invalid_inst_gen; + } + + uint32_t base_ptr_align = non_sentinel_slice_ptr_type->data.pointer.explicit_alignment; + uint32_t ptr_byte_alignment = 0; + if (end_scalar > start_scalar) { + if ((err = compute_elem_align(ira, elem_type, base_ptr_align, start_scalar, &ptr_byte_alignment))) + return ira->codegen->invalid_inst_gen; + } + + ZigType *return_array_type = get_array_type(ira->codegen, elem_type, end_scalar - start_scalar, + array_sentinel); + return_type = get_pointer_to_type_extra(ira->codegen, return_array_type, + non_sentinel_slice_ptr_type->data.pointer.is_const, + non_sentinel_slice_ptr_type->data.pointer.is_volatile, + PtrLenSingle, ptr_byte_alignment, 0, 0, false); + goto done_with_return_type; + } + } else if (array_sentinel == nullptr && end == nullptr) { + array_sentinel = slice_sentinel_val; + } + if (array_sentinel != nullptr) { + // TODO deal with non-abi-alignment here + ZigType *slice_ptr_type = adjust_ptr_sentinel(ira->codegen, non_sentinel_slice_ptr_type, array_sentinel); + return_type = get_slice_type(ira->codegen, slice_ptr_type); + } else { + // TODO deal with non-abi-alignment here + return_type = get_slice_type(ira->codegen, non_sentinel_slice_ptr_type); + } +done_with_return_type: + + if (instr_is_comptime(ptr_ptr) && + value_is_comptime(casted_start->value) && + (!end || value_is_comptime(end->value))) + { + ZigValue *array_val; + ZigValue *parent_ptr; + size_t abs_offset; + size_t rel_end; + bool ptr_is_undef = false; + if (child_array_type->id == ZigTypeIdArray) { + if (array_type->id == ZigTypeIdPointer) { + parent_ptr = const_ptr_pointee(ira, ira->codegen, ptr_ptr->value, instruction->base.base.source_node); + if (parent_ptr == nullptr) + return ira->codegen->invalid_inst_gen; + + if (parent_ptr->special == ConstValSpecialUndef) { + array_val = nullptr; + abs_offset = 0; + rel_end = SIZE_MAX; + ptr_is_undef = true; + } else if (parent_ptr->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) { + array_val = nullptr; + abs_offset = 0; + rel_end = SIZE_MAX; + } else { + array_val = const_ptr_pointee(ira, ira->codegen, parent_ptr, instruction->base.base.source_node); + if (array_val == nullptr) + return ira->codegen->invalid_inst_gen; + + rel_end = child_array_type->data.array.len; + abs_offset = 0; + } + } else { + array_val = const_ptr_pointee(ira, ira->codegen, ptr_ptr->value, instruction->base.base.source_node); + if (array_val == nullptr) + return ira->codegen->invalid_inst_gen; + rel_end = array_type->data.array.len; + parent_ptr = nullptr; + abs_offset = 0; + } + } else if (array_type->id == ZigTypeIdPointer) { + assert(array_type->data.pointer.ptr_len == PtrLenUnknown); + parent_ptr = const_ptr_pointee(ira, ira->codegen, ptr_ptr->value, instruction->base.base.source_node); + if (parent_ptr == nullptr) + return ira->codegen->invalid_inst_gen; + + if (parent_ptr->special == ConstValSpecialUndef) { + array_val = nullptr; + abs_offset = 0; + rel_end = SIZE_MAX; + ptr_is_undef = true; + } else switch (parent_ptr->data.x_ptr.special) { + case ConstPtrSpecialInvalid: + case ConstPtrSpecialDiscard: + zig_unreachable(); + case ConstPtrSpecialRef: + if (parent_ptr->data.x_ptr.data.ref.pointee->type->id == ZigTypeIdArray) { + array_val = parent_ptr->data.x_ptr.data.ref.pointee; + abs_offset = 0; + rel_end = array_val->type->data.array.len; + } else { + array_val = nullptr; + abs_offset = SIZE_MAX; + rel_end = 1; + } + break; + case ConstPtrSpecialSubArray: + case ConstPtrSpecialBaseArray: + array_val = parent_ptr->data.x_ptr.data.base_array.array_val; + abs_offset = parent_ptr->data.x_ptr.data.base_array.elem_index; + rel_end = array_val->type->data.array.len - abs_offset; + break; + case ConstPtrSpecialBaseStruct: + zig_panic("TODO slice const inner struct"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO slice const inner error union code"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO slice const inner error union payload"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO slice const inner optional payload"); + case ConstPtrSpecialHardCodedAddr: + array_val = nullptr; + abs_offset = 0; + rel_end = SIZE_MAX; + break; + case ConstPtrSpecialFunction: + zig_panic("TODO slice of ptr cast from function"); + case ConstPtrSpecialNull: + zig_panic("TODO slice of null ptr"); + } + } else if (is_slice(array_type)) { + ZigValue *slice_ptr = const_ptr_pointee(ira, ira->codegen, ptr_ptr->value, instruction->base.base.source_node); + if (slice_ptr == nullptr) + return ira->codegen->invalid_inst_gen; + + if (slice_ptr->special == ConstValSpecialUndef) { + ir_add_error(ira, &instruction->base.base, buf_sprintf("slice of undefined")); + return ira->codegen->invalid_inst_gen; + } + + parent_ptr = slice_ptr->data.x_struct.fields[slice_ptr_index]; + if (parent_ptr->special == ConstValSpecialUndef) { + ir_add_error(ira, &instruction->base.base, buf_sprintf("slice of undefined")); + return ira->codegen->invalid_inst_gen; + } + + ZigValue *len_val = slice_ptr->data.x_struct.fields[slice_len_index]; + + switch (parent_ptr->data.x_ptr.special) { + case ConstPtrSpecialInvalid: + case ConstPtrSpecialDiscard: + zig_unreachable(); + case ConstPtrSpecialRef: + array_val = nullptr; + abs_offset = SIZE_MAX; + rel_end = 1; + break; + case ConstPtrSpecialSubArray: + case ConstPtrSpecialBaseArray: + array_val = parent_ptr->data.x_ptr.data.base_array.array_val; + abs_offset = parent_ptr->data.x_ptr.data.base_array.elem_index; + rel_end = bigint_as_usize(&len_val->data.x_bigint); + break; + case ConstPtrSpecialBaseStruct: + zig_panic("TODO slice const inner struct"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO slice const inner error union code"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO slice const inner error union payload"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO slice const inner optional payload"); + case ConstPtrSpecialHardCodedAddr: + array_val = nullptr; + abs_offset = 0; + rel_end = bigint_as_usize(&len_val->data.x_bigint); + break; + case ConstPtrSpecialFunction: + zig_panic("TODO slice of slice cast from function"); + case ConstPtrSpecialNull: + zig_panic("TODO slice of null"); + } + } else { + zig_unreachable(); + } + + ZigValue *start_val = ir_resolve_const(ira, casted_start, UndefBad); + if (!start_val) + return ira->codegen->invalid_inst_gen; + + uint64_t start_scalar = bigint_as_u64(&start_val->data.x_bigint); + if (!ptr_is_undef && start_scalar > rel_end) { + ir_add_error(ira, &instruction->base.base, buf_sprintf("out of bounds slice")); + return ira->codegen->invalid_inst_gen; + } + + uint64_t end_scalar = rel_end; + if (end) { + ZigValue *end_val = ir_resolve_const(ira, end, UndefBad); + if (!end_val) + return ira->codegen->invalid_inst_gen; + end_scalar = bigint_as_u64(&end_val->data.x_bigint); + } + if (!ptr_is_undef) { + if (end_scalar > rel_end) { + ir_add_error(ira, &instruction->base.base, buf_sprintf("out of bounds slice")); + return ira->codegen->invalid_inst_gen; + } + if (start_scalar > end_scalar) { + ir_add_error(ira, &instruction->base.base, buf_sprintf("slice start is greater than end")); + return ira->codegen->invalid_inst_gen; + } + } + if (ptr_is_undef && start_scalar != end_scalar) { + ir_add_error(ira, &instruction->base.base, buf_sprintf("non-zero length slice of undefined pointer")); + return ira->codegen->invalid_inst_gen; + } + + // check sentinel when target is comptime-known + { + if (!sentinel_val) + goto exit_check_sentinel; + + switch (ptr_ptr->value->data.x_ptr.mut) { + case ConstPtrMutComptimeConst: + case ConstPtrMutComptimeVar: + break; + case ConstPtrMutRuntimeVar: + case ConstPtrMutInfer: + goto exit_check_sentinel; + } + + // prepare check parameters + ZigValue *target = const_ptr_pointee(ira, ira->codegen, ptr_ptr->value, instruction->base.base.source_node); + if (target == nullptr) + return ira->codegen->invalid_inst_gen; + + uint64_t target_len = 0; + ZigValue *target_sentinel = nullptr; + ZigValue *target_elements = nullptr; + + for (;;) { + if (target->type->id == ZigTypeIdArray) { + // handle `[N]T` + target_len = target->type->data.array.len; + target_sentinel = target->type->data.array.sentinel; + target_elements = target->data.x_array.data.s_none.elements; + break; + } else if (target->type->id == ZigTypeIdPointer && target->type->data.pointer.child_type->id == ZigTypeIdArray) { + // handle `*[N]T` + target = const_ptr_pointee(ira, ira->codegen, target, instruction->base.base.source_node); + if (target == nullptr) + return ira->codegen->invalid_inst_gen; + assert(target->type->id == ZigTypeIdArray); + continue; + } else if (target->type->id == ZigTypeIdPointer) { + // handle `[*]T` + // handle `[*c]T` + switch (target->data.x_ptr.special) { + case ConstPtrSpecialInvalid: + case ConstPtrSpecialDiscard: + zig_unreachable(); + case ConstPtrSpecialRef: + target = target->data.x_ptr.data.ref.pointee; + assert(target->type->id == ZigTypeIdArray); + continue; + case ConstPtrSpecialBaseArray: + case ConstPtrSpecialSubArray: + target = target->data.x_ptr.data.base_array.array_val; + assert(target->type->id == ZigTypeIdArray); + continue; + case ConstPtrSpecialBaseStruct: + zig_panic("TODO slice const inner struct"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO slice const inner error union code"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO slice const inner error union payload"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO slice const inner optional payload"); + case ConstPtrSpecialHardCodedAddr: + // skip check + goto exit_check_sentinel; + case ConstPtrSpecialFunction: + zig_panic("TODO slice of ptr cast from function"); + case ConstPtrSpecialNull: + zig_panic("TODO slice of null ptr"); + } + break; + } else if (is_slice(target->type)) { + // handle `[]T` + target = target->data.x_struct.fields[slice_ptr_index]; + assert(target->type->id == ZigTypeIdPointer); + continue; + } + + zig_unreachable(); + } + + // perform check + if (target_sentinel == nullptr) { + if (end_scalar >= target_len) { + ir_add_error(ira, &instruction->base.base, buf_sprintf("slice-sentinel is out of bounds")); + return ira->codegen->invalid_inst_gen; + } + if (!const_values_equal(ira->codegen, sentinel_val, &target_elements[end_scalar])) { + ir_add_error(ira, &instruction->base.base, buf_sprintf("slice-sentinel does not match memory at target index")); + return ira->codegen->invalid_inst_gen; + } + } else { + assert(end_scalar <= target_len); + if (end_scalar == target_len) { + if (!const_values_equal(ira->codegen, sentinel_val, target_sentinel)) { + ir_add_error(ira, &instruction->base.base, buf_sprintf("slice-sentinel does not match target-sentinel")); + return ira->codegen->invalid_inst_gen; + } + } else { + if (!const_values_equal(ira->codegen, sentinel_val, &target_elements[end_scalar])) { + ir_add_error(ira, &instruction->base.base, buf_sprintf("slice-sentinel does not match memory at target index")); + return ira->codegen->invalid_inst_gen; + } + } + } + } + exit_check_sentinel: + + IrInstGen *result = ir_const(ira, &instruction->base.base, return_type); + + ZigValue *ptr_val; + if (return_type->id == ZigTypeIdPointer) { + // pointer to array + ptr_val = result->value; + } else { + // slice + result->value->data.x_struct.fields = alloc_const_vals_ptrs(ira->codegen, 2); + + ptr_val = result->value->data.x_struct.fields[slice_ptr_index]; + + ZigValue *len_val = result->value->data.x_struct.fields[slice_len_index]; + init_const_usize(ira->codegen, len_val, end_scalar - start_scalar); + } + + bool return_type_is_const = non_sentinel_slice_ptr_type->data.pointer.is_const; + if (array_val) { + size_t index = abs_offset + start_scalar; + init_const_ptr_array(ira->codegen, ptr_val, array_val, index, return_type_is_const, PtrLenUnknown); + if (return_type->id == ZigTypeIdPointer) { + ptr_val->data.x_ptr.special = ConstPtrSpecialSubArray; + } + if (array_type->id == ZigTypeIdArray) { + ptr_val->data.x_ptr.mut = ptr_ptr->value->data.x_ptr.mut; + } else if (is_slice(array_type)) { + ptr_val->data.x_ptr.mut = parent_ptr->data.x_ptr.mut; + } else if (array_type->id == ZigTypeIdPointer) { + ptr_val->data.x_ptr.mut = parent_ptr->data.x_ptr.mut; + } + } else if (ptr_is_undef) { + ptr_val->type = get_pointer_to_type(ira->codegen, parent_ptr->type->data.pointer.child_type, + return_type_is_const); + ptr_val->special = ConstValSpecialUndef; + } else switch (parent_ptr->data.x_ptr.special) { + case ConstPtrSpecialInvalid: + case ConstPtrSpecialDiscard: + zig_unreachable(); + case ConstPtrSpecialRef: + init_const_ptr_ref(ira->codegen, ptr_val, parent_ptr->data.x_ptr.data.ref.pointee, + return_type_is_const); + break; + case ConstPtrSpecialSubArray: + case ConstPtrSpecialBaseArray: + zig_unreachable(); + case ConstPtrSpecialBaseStruct: + zig_panic("TODO"); + case ConstPtrSpecialBaseErrorUnionCode: + zig_panic("TODO"); + case ConstPtrSpecialBaseErrorUnionPayload: + zig_panic("TODO"); + case ConstPtrSpecialBaseOptionalPayload: + zig_panic("TODO"); + case ConstPtrSpecialHardCodedAddr: + init_const_ptr_hard_coded_addr(ira->codegen, ptr_val, + parent_ptr->type->data.pointer.child_type, + parent_ptr->data.x_ptr.data.hard_coded_addr.addr + start_scalar, + return_type_is_const); + break; + case ConstPtrSpecialFunction: + zig_panic("TODO"); + case ConstPtrSpecialNull: + zig_panic("TODO"); + } + + // In the case of pointer-to-array, we must restore this because above it overwrites ptr_val->type + result->value->type = return_type; + return result; + } + + if (generate_non_null_assert) { + IrInstGen *ptr_val = ir_get_deref(ira, &instruction->base.base, ptr_ptr, nullptr); + + if (type_is_invalid(ptr_val->value->type)) + return ira->codegen->invalid_inst_gen; + + ir_build_assert_non_null(ira, &instruction->base.base, ptr_val); + } + + IrInstGen *result_loc = nullptr; + + if (return_type->id != ZigTypeIdPointer) { + result_loc = ir_resolve_result(ira, &instruction->base.base, instruction->result_loc, + return_type, nullptr, true, true); + if (result_loc != nullptr) { + if (type_is_invalid(result_loc->value->type) || result_loc->value->type->id == ZigTypeIdUnreachable) { + return result_loc; + } + + ir_assert(result_loc->value->type->id == ZigTypeIdPointer, &instruction->base.base); + if (result_loc->value->type->data.pointer.is_const) { + ir_add_error(ira, &instruction->base.base, buf_sprintf("cannot assign to constant")); + return ira->codegen->invalid_inst_gen; + } + + IrInstGen *dummy_value = ir_const(ira, &instruction->base.base, return_type); + dummy_value->value->special = ConstValSpecialRuntime; + IrInstGen *dummy_result = ir_implicit_cast2(ira, &instruction->base.base, + dummy_value, result_loc->value->type->data.pointer.child_type); + if (type_is_invalid(dummy_result->value->type)) + return ira->codegen->invalid_inst_gen; + } + } + + return ir_build_slice_gen(ira, &instruction->base.base, return_type, ptr_ptr, + casted_start, end, instruction->safety_check_on, result_loc, sentinel_val); +} + +static IrInstGen *ir_analyze_instruction_has_field(IrAnalyze *ira, IrInstSrcHasField *instruction) { + Error err; + ZigType *container_type = ir_resolve_type(ira, instruction->container_type->child); + if (type_is_invalid(container_type)) + return ira->codegen->invalid_inst_gen; + + if ((err = type_resolve(ira->codegen, container_type, ResolveStatusZeroBitsKnown))) + return ira->codegen->invalid_inst_gen; + + Buf *field_name = ir_resolve_str(ira, instruction->field_name->child); + if (field_name == nullptr) + return ira->codegen->invalid_inst_gen; + + bool result; + if (container_type->id == ZigTypeIdStruct) { + result = find_struct_type_field(container_type, field_name) != nullptr; + } else if (container_type->id == ZigTypeIdEnum) { + result = find_enum_type_field(container_type, field_name) != nullptr; + } else if (container_type->id == ZigTypeIdUnion) { + result = find_union_type_field(container_type, field_name) != nullptr; + } else { + ir_add_error(ira, &instruction->container_type->base, + buf_sprintf("type '%s' does not support @hasField", buf_ptr(&container_type->name))); + return ira->codegen->invalid_inst_gen; + } + return ir_const_bool(ira, &instruction->base.base, result); +} + +static IrInstGen *ir_analyze_instruction_wasm_memory_size(IrAnalyze *ira, IrInstSrcWasmMemorySize *instruction) { + // TODO generate compile error for target_arch different than 32bit + if (!target_is_wasm(ira->codegen->zig_target)) { + ir_add_error_node(ira, instruction->base.base.source_node, + buf_sprintf("@wasmMemorySize is a wasm32 feature only")); + return ira->codegen->invalid_inst_gen; + } + + IrInstGen *index = instruction->index->child; + if (type_is_invalid(index->value->type)) + return ira->codegen->invalid_inst_gen; + + ZigType *u32 = ira->codegen->builtin_types.entry_u32; + + IrInstGen *casted_index = ir_implicit_cast(ira, index, u32); + if (type_is_invalid(casted_index->value->type)) + return ira->codegen->invalid_inst_gen; + + return ir_build_wasm_memory_size_gen(ira, &instruction->base.base, casted_index); +} + +static IrInstGen *ir_analyze_instruction_wasm_memory_grow(IrAnalyze *ira, IrInstSrcWasmMemoryGrow *instruction) { + // TODO generate compile error for target_arch different than 32bit + if (!target_is_wasm(ira->codegen->zig_target)) { + ir_add_error_node(ira, instruction->base.base.source_node, + buf_sprintf("@wasmMemoryGrow is a wasm32 feature only")); + return ira->codegen->invalid_inst_gen; + } + + IrInstGen *index = instruction->index->child; + if (type_is_invalid(index->value->type)) + return ira->codegen->invalid_inst_gen; + + ZigType *u32 = ira->codegen->builtin_types.entry_u32; + + IrInstGen *casted_index = ir_implicit_cast(ira, index, u32); + if (type_is_invalid(casted_index->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *delta = instruction->delta->child; + if (type_is_invalid(delta->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *casted_delta = ir_implicit_cast(ira, delta, u32); + if (type_is_invalid(casted_delta->value->type)) + return ira->codegen->invalid_inst_gen; + + return ir_build_wasm_memory_grow_gen(ira, &instruction->base.base, casted_index, casted_delta); +} + +static IrInstGen *ir_analyze_instruction_breakpoint(IrAnalyze *ira, IrInstSrcBreakpoint *instruction) { + return ir_build_breakpoint_gen(ira, &instruction->base.base); +} + +static IrInstGen *ir_analyze_instruction_return_address(IrAnalyze *ira, IrInstSrcReturnAddress *instruction) { + return ir_build_return_address_gen(ira, &instruction->base.base); +} + +static IrInstGen *ir_analyze_instruction_frame_address(IrAnalyze *ira, IrInstSrcFrameAddress *instruction) { + return ir_build_frame_address_gen(ira, &instruction->base.base); +} + +static IrInstGen *ir_analyze_instruction_frame_handle(IrAnalyze *ira, IrInstSrcFrameHandle *instruction) { + ZigFn *fn = ira->new_irb.exec->fn_entry; + ir_assert(fn != nullptr, &instruction->base.base); + + if (fn->inferred_async_node == nullptr) { + fn->inferred_async_node = instruction->base.base.source_node; + } + + ZigType *frame_type = get_fn_frame_type(ira->codegen, fn); + ZigType *ptr_frame_type = get_pointer_to_type(ira->codegen, frame_type, false); + + return ir_build_handle_gen(ira, &instruction->base.base, ptr_frame_type); +} + +static IrInstGen *ir_analyze_instruction_frame_type(IrAnalyze *ira, IrInstSrcFrameType *instruction) { + ZigFn *fn = ir_resolve_fn(ira, instruction->fn->child); + if (fn == nullptr) + return ira->codegen->invalid_inst_gen; + + if (fn->type_entry->data.fn.is_generic) { + ir_add_error(ira, &instruction->base.base, + buf_sprintf("@Frame() of generic function")); + return ira->codegen->invalid_inst_gen; + } + + ZigType *ty = get_fn_frame_type(ira->codegen, fn); + return ir_const_type(ira, &instruction->base.base, ty); +} + +static IrInstGen *ir_analyze_instruction_frame_size(IrAnalyze *ira, IrInstSrcFrameSize *instruction) { + IrInstGen *fn = instruction->fn->child; + if (type_is_invalid(fn->value->type)) + return ira->codegen->invalid_inst_gen; + + if (fn->value->type->id != ZigTypeIdFn) { + ir_add_error(ira, &fn->base, + buf_sprintf("expected function, found '%s'", buf_ptr(&fn->value->type->name))); + return ira->codegen->invalid_inst_gen; + } + + ira->codegen->need_frame_size_prefix_data = true; + + return ir_build_frame_size_gen(ira, &instruction->base.base, fn); +} + +static IrInstGen *ir_analyze_instruction_align_of(IrAnalyze *ira, IrInstSrcAlignOf *instruction) { + // Here we create a lazy value in order to avoid resolving the alignment of the type + // immediately. This avoids false positive dependency loops such as: + // const Node = struct { + // field: []align(@alignOf(Node)) Node, + // }; + IrInstGen *result = ir_const(ira, &instruction->base.base, ira->codegen->builtin_types.entry_num_lit_int); + result->value->special = ConstValSpecialLazy; + + LazyValueAlignOf *lazy_align_of = heap::c_allocator.create(); + lazy_align_of->ira = ira; ira_ref(ira); + result->value->data.x_lazy = &lazy_align_of->base; + lazy_align_of->base.id = LazyValueIdAlignOf; + + lazy_align_of->target_type = instruction->type_value->child; + if (ir_resolve_type_lazy(ira, lazy_align_of->target_type) == nullptr) + return ira->codegen->invalid_inst_gen; + + return result; +} + +static IrInstGen *ir_analyze_instruction_overflow_op(IrAnalyze *ira, IrInstSrcOverflowOp *instruction) { + Error err; + + IrInstGen *type_value = instruction->type_value->child; + if (type_is_invalid(type_value->value->type)) + return ira->codegen->invalid_inst_gen; + + ZigType *dest_type = ir_resolve_type(ira, type_value); + if (type_is_invalid(dest_type)) + return ira->codegen->invalid_inst_gen; + + if (dest_type->id != ZigTypeIdInt) { + ir_add_error(ira, &type_value->base, + buf_sprintf("expected integer type, found '%s'", buf_ptr(&dest_type->name))); + return ira->codegen->invalid_inst_gen; + } + + IrInstGen *op1 = instruction->op1->child; + if (type_is_invalid(op1->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *casted_op1 = ir_implicit_cast(ira, op1, dest_type); + if (type_is_invalid(casted_op1->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *op2 = instruction->op2->child; + if (type_is_invalid(op2->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *casted_op2; + if (instruction->op == IrOverflowOpShl) { + ZigType *shift_amt_type = get_smallest_unsigned_int_type(ira->codegen, + dest_type->data.integral.bit_count - 1); + casted_op2 = ir_implicit_cast(ira, op2, shift_amt_type); + } else { + casted_op2 = ir_implicit_cast(ira, op2, dest_type); + } + if (type_is_invalid(casted_op2->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *result_ptr = instruction->result_ptr->child; + if (type_is_invalid(result_ptr->value->type)) + return ira->codegen->invalid_inst_gen; + + ZigType *expected_ptr_type; + if (result_ptr->value->type->id == ZigTypeIdPointer) { + uint32_t alignment; + if ((err = resolve_ptr_align(ira, result_ptr->value->type, &alignment))) + return ira->codegen->invalid_inst_gen; + expected_ptr_type = get_pointer_to_type_extra(ira->codegen, dest_type, + false, result_ptr->value->type->data.pointer.is_volatile, + PtrLenSingle, + alignment, 0, 0, false); + } else { + expected_ptr_type = get_pointer_to_type(ira->codegen, dest_type, false); + } + + IrInstGen *casted_result_ptr = ir_implicit_cast(ira, result_ptr, expected_ptr_type); + if (type_is_invalid(casted_result_ptr->value->type)) + return ira->codegen->invalid_inst_gen; + + if (instr_is_comptime(casted_op1) && + instr_is_comptime(casted_op2) && + instr_is_comptime(casted_result_ptr)) + { + ZigValue *op1_val = ir_resolve_const(ira, casted_op1, UndefBad); + if (op1_val == nullptr) + return ira->codegen->invalid_inst_gen; + + ZigValue *op2_val = ir_resolve_const(ira, casted_op2, UndefBad); + if (op2_val == nullptr) + return ira->codegen->invalid_inst_gen; + + ZigValue *result_val = ir_resolve_const(ira, casted_result_ptr, UndefBad); + if (result_val == nullptr) + return ira->codegen->invalid_inst_gen; + + BigInt *op1_bigint = &op1_val->data.x_bigint; + BigInt *op2_bigint = &op2_val->data.x_bigint; + ZigValue *pointee_val = const_ptr_pointee(ira, ira->codegen, result_val, + casted_result_ptr->base.source_node); + if (pointee_val == nullptr) + return ira->codegen->invalid_inst_gen; + BigInt *dest_bigint = &pointee_val->data.x_bigint; + switch (instruction->op) { + case IrOverflowOpAdd: + bigint_add(dest_bigint, op1_bigint, op2_bigint); + break; + case IrOverflowOpSub: + bigint_sub(dest_bigint, op1_bigint, op2_bigint); + break; + case IrOverflowOpMul: + bigint_mul(dest_bigint, op1_bigint, op2_bigint); + break; + case IrOverflowOpShl: + bigint_shl(dest_bigint, op1_bigint, op2_bigint); + break; + } + bool result_bool = false; + if (!bigint_fits_in_bits(dest_bigint, dest_type->data.integral.bit_count, + dest_type->data.integral.is_signed)) + { + result_bool = true; + BigInt tmp_bigint; + bigint_init_bigint(&tmp_bigint, dest_bigint); + bigint_truncate(dest_bigint, &tmp_bigint, dest_type->data.integral.bit_count, + dest_type->data.integral.is_signed); + } + pointee_val->special = ConstValSpecialStatic; + return ir_const_bool(ira, &instruction->base.base, result_bool); + } + + return ir_build_overflow_op_gen(ira, &instruction->base.base, instruction->op, + casted_op1, casted_op2, casted_result_ptr, dest_type); +} + +static void ir_eval_mul_add(IrAnalyze *ira, IrInstSrcMulAdd *source_instr, ZigType *float_type, + ZigValue *op1, ZigValue *op2, ZigValue *op3, ZigValue *out_val) { + if (float_type->id == ZigTypeIdComptimeFloat) { + f128M_mulAdd(&out_val->data.x_bigfloat.value, &op1->data.x_bigfloat.value, &op2->data.x_bigfloat.value, + &op3->data.x_bigfloat.value); + } else if (float_type->id == ZigTypeIdFloat) { + switch (float_type->data.floating.bit_count) { + case 16: + out_val->data.x_f16 = f16_mulAdd(op1->data.x_f16, op2->data.x_f16, op3->data.x_f16); + break; + case 32: + out_val->data.x_f32 = fmaf(op1->data.x_f32, op2->data.x_f32, op3->data.x_f32); + break; + case 64: + out_val->data.x_f64 = fma(op1->data.x_f64, op2->data.x_f64, op3->data.x_f64); + break; + case 128: + f128M_mulAdd(&op1->data.x_f128, &op2->data.x_f128, &op3->data.x_f128, &out_val->data.x_f128); + break; + default: + zig_unreachable(); + } + } else { + zig_unreachable(); + } +} + +static IrInstGen *ir_analyze_instruction_mul_add(IrAnalyze *ira, IrInstSrcMulAdd *instruction) { + IrInstGen *type_value = instruction->type_value->child; + if (type_is_invalid(type_value->value->type)) + return ira->codegen->invalid_inst_gen; + + ZigType *expr_type = ir_resolve_type(ira, type_value); + if (type_is_invalid(expr_type)) + return ira->codegen->invalid_inst_gen; + + // Only allow float types, and vectors of floats. + ZigType *float_type = (expr_type->id == ZigTypeIdVector) ? expr_type->data.vector.elem_type : expr_type; + if (float_type->id != ZigTypeIdFloat) { + ir_add_error(ira, &type_value->base, + buf_sprintf("expected float or vector of float type, found '%s'", buf_ptr(&float_type->name))); + return ira->codegen->invalid_inst_gen; + } + + IrInstGen *op1 = instruction->op1->child; + if (type_is_invalid(op1->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *casted_op1 = ir_implicit_cast(ira, op1, expr_type); + if (type_is_invalid(casted_op1->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *op2 = instruction->op2->child; + if (type_is_invalid(op2->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *casted_op2 = ir_implicit_cast(ira, op2, expr_type); + if (type_is_invalid(casted_op2->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *op3 = instruction->op3->child; + if (type_is_invalid(op3->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *casted_op3 = ir_implicit_cast(ira, op3, expr_type); + if (type_is_invalid(casted_op3->value->type)) + return ira->codegen->invalid_inst_gen; + + if (instr_is_comptime(casted_op1) && + instr_is_comptime(casted_op2) && + instr_is_comptime(casted_op3)) { + ZigValue *op1_const = ir_resolve_const(ira, casted_op1, UndefBad); + if (!op1_const) + return ira->codegen->invalid_inst_gen; + ZigValue *op2_const = ir_resolve_const(ira, casted_op2, UndefBad); + if (!op2_const) + return ira->codegen->invalid_inst_gen; + ZigValue *op3_const = ir_resolve_const(ira, casted_op3, UndefBad); + if (!op3_const) + return ira->codegen->invalid_inst_gen; + + IrInstGen *result = ir_const(ira, &instruction->base.base, expr_type); + ZigValue *out_val = result->value; + + if (expr_type->id == ZigTypeIdVector) { + expand_undef_array(ira->codegen, op1_const); + expand_undef_array(ira->codegen, op2_const); + expand_undef_array(ira->codegen, op3_const); + out_val->special = ConstValSpecialUndef; + expand_undef_array(ira->codegen, out_val); + size_t len = expr_type->data.vector.len; + for (size_t i = 0; i < len; i += 1) { + ZigValue *float_operand_op1 = &op1_const->data.x_array.data.s_none.elements[i]; + ZigValue *float_operand_op2 = &op2_const->data.x_array.data.s_none.elements[i]; + ZigValue *float_operand_op3 = &op3_const->data.x_array.data.s_none.elements[i]; + ZigValue *float_out_val = &out_val->data.x_array.data.s_none.elements[i]; + assert(float_operand_op1->type == float_type); + assert(float_operand_op2->type == float_type); + assert(float_operand_op3->type == float_type); + assert(float_out_val->type == float_type); + ir_eval_mul_add(ira, instruction, float_type, + op1_const, op2_const, op3_const, float_out_val); + float_out_val->type = float_type; + } + out_val->type = expr_type; + out_val->special = ConstValSpecialStatic; + } else { + ir_eval_mul_add(ira, instruction, float_type, op1_const, op2_const, op3_const, out_val); + } + return result; + } + + return ir_build_mul_add_gen(ira, &instruction->base.base, casted_op1, casted_op2, casted_op3, expr_type); +} + +static IrInstGen *ir_analyze_instruction_test_err(IrAnalyze *ira, IrInstSrcTestErr *instruction) { + IrInstGen *base_ptr = instruction->base_ptr->child; + if (type_is_invalid(base_ptr->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *value; + if (instruction->base_ptr_is_payload) { + value = base_ptr; + } else { + value = ir_get_deref(ira, &instruction->base.base, base_ptr, nullptr); + } + + ZigType *type_entry = value->value->type; + if (type_is_invalid(type_entry)) + return ira->codegen->invalid_inst_gen; + if (type_entry->id == ZigTypeIdErrorUnion) { + if (instr_is_comptime(value)) { + ZigValue *err_union_val = ir_resolve_const(ira, value, UndefBad); + if (!err_union_val) + return ira->codegen->invalid_inst_gen; + + if (err_union_val->special != ConstValSpecialRuntime) { + ErrorTableEntry *err = err_union_val->data.x_err_union.error_set->data.x_err_set; + return ir_const_bool(ira, &instruction->base.base, (err != nullptr)); + } + } + + if (instruction->resolve_err_set) { + ZigType *err_set_type = type_entry->data.error_union.err_set_type; + if (!resolve_inferred_error_set(ira->codegen, err_set_type, instruction->base.base.source_node)) { + return ira->codegen->invalid_inst_gen; + } + if (!type_is_global_error_set(err_set_type) && + err_set_type->data.error_set.err_count == 0) + { + assert(!err_set_type->data.error_set.incomplete); + return ir_const_bool(ira, &instruction->base.base, false); + } + } + + return ir_build_test_err_gen(ira, &instruction->base.base, value); + } else if (type_entry->id == ZigTypeIdErrorSet) { + return ir_const_bool(ira, &instruction->base.base, true); + } else { + return ir_const_bool(ira, &instruction->base.base, false); + } +} + +static IrInstGen *ir_analyze_unwrap_err_code(IrAnalyze *ira, IrInst* source_instr, + IrInstGen *base_ptr, bool initializing) +{ + ZigType *ptr_type = base_ptr->value->type; + + // This will be a pointer type because unwrap err payload IR instruction operates on a pointer to a thing. + assert(ptr_type->id == ZigTypeIdPointer); + + ZigType *type_entry = ptr_type->data.pointer.child_type; + if (type_is_invalid(type_entry)) + return ira->codegen->invalid_inst_gen; + + if (type_entry->id != ZigTypeIdErrorUnion) { + ir_add_error(ira, &base_ptr->base, + buf_sprintf("expected error union type, found '%s'", buf_ptr(&type_entry->name))); + return ira->codegen->invalid_inst_gen; + } + + ZigType *err_set_type = type_entry->data.error_union.err_set_type; + ZigType *result_type = get_pointer_to_type_extra(ira->codegen, err_set_type, + ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, PtrLenSingle, + ptr_type->data.pointer.explicit_alignment, 0, 0, false); + + if (instr_is_comptime(base_ptr)) { + ZigValue *ptr_val = ir_resolve_const(ira, base_ptr, UndefBad); + if (!ptr_val) + return ira->codegen->invalid_inst_gen; + if (ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar && + ptr_val->data.x_ptr.special != ConstPtrSpecialHardCodedAddr) + { + ZigValue *err_union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node); + if (err_union_val == nullptr) + return ira->codegen->invalid_inst_gen; + + if (initializing && err_union_val->special == ConstValSpecialUndef) { + ZigValue *vals = ira->codegen->pass1_arena->allocate(2); + ZigValue *err_set_val = &vals[0]; + ZigValue *payload_val = &vals[1]; + + err_set_val->special = ConstValSpecialUndef; + err_set_val->type = err_set_type; + err_set_val->parent.id = ConstParentIdErrUnionCode; + err_set_val->parent.data.p_err_union_code.err_union_val = err_union_val; + + payload_val->special = ConstValSpecialUndef; + payload_val->type = type_entry->data.error_union.payload_type; + payload_val->parent.id = ConstParentIdErrUnionPayload; + payload_val->parent.data.p_err_union_payload.err_union_val = err_union_val; + + err_union_val->special = ConstValSpecialStatic; + err_union_val->data.x_err_union.error_set = err_set_val; + err_union_val->data.x_err_union.payload = payload_val; + } + ir_assert(err_union_val->special != ConstValSpecialRuntime, source_instr); + + IrInstGen *result; + if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { + result = ir_build_unwrap_err_code_gen(ira, source_instr->scope, + source_instr->source_node, base_ptr, result_type); + result->value->special = ConstValSpecialStatic; + } else { + result = ir_const(ira, source_instr, result_type); + } + ZigValue *const_val = result->value; + const_val->data.x_ptr.special = ConstPtrSpecialBaseErrorUnionCode; + const_val->data.x_ptr.data.base_err_union_code.err_union_val = err_union_val; + const_val->data.x_ptr.mut = ptr_val->data.x_ptr.mut; + return result; + } + } + + return ir_build_unwrap_err_code_gen(ira, source_instr->scope, source_instr->source_node, base_ptr, result_type); +} + +static IrInstGen *ir_analyze_instruction_unwrap_err_code(IrAnalyze *ira, IrInstSrcUnwrapErrCode *instruction) { + IrInstGen *base_ptr = instruction->err_union_ptr->child; + if (type_is_invalid(base_ptr->value->type)) + return ira->codegen->invalid_inst_gen; + return ir_analyze_unwrap_err_code(ira, &instruction->base.base, base_ptr, false); +} + +static IrInstGen *ir_analyze_unwrap_error_payload(IrAnalyze *ira, IrInst* source_instr, + IrInstGen *base_ptr, bool safety_check_on, bool initializing) +{ + ZigType *ptr_type = base_ptr->value->type; + + // This will be a pointer type because unwrap err payload IR instruction operates on a pointer to a thing. + assert(ptr_type->id == ZigTypeIdPointer); + + ZigType *type_entry = ptr_type->data.pointer.child_type; + if (type_is_invalid(type_entry)) + return ira->codegen->invalid_inst_gen; + + if (type_entry->id != ZigTypeIdErrorUnion) { + ir_add_error(ira, &base_ptr->base, + buf_sprintf("expected error union type, found '%s'", buf_ptr(&type_entry->name))); + return ira->codegen->invalid_inst_gen; + } + + ZigType *payload_type = type_entry->data.error_union.payload_type; + if (type_is_invalid(payload_type)) + return ira->codegen->invalid_inst_gen; + + ZigType *result_type = get_pointer_to_type_extra(ira->codegen, payload_type, + ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, + PtrLenSingle, 0, 0, 0, false); + + if (instr_is_comptime(base_ptr)) { + ZigValue *ptr_val = ir_resolve_const(ira, base_ptr, UndefBad); + if (!ptr_val) + return ira->codegen->invalid_inst_gen; + if (ptr_val->data.x_ptr.mut != ConstPtrMutRuntimeVar) { + ZigValue *err_union_val = const_ptr_pointee(ira, ira->codegen, ptr_val, source_instr->source_node); + if (err_union_val == nullptr) + return ira->codegen->invalid_inst_gen; + if (initializing && err_union_val->special == ConstValSpecialUndef) { + ZigValue *vals = ira->codegen->pass1_arena->allocate(2); + ZigValue *err_set_val = &vals[0]; + ZigValue *payload_val = &vals[1]; + + err_set_val->special = ConstValSpecialStatic; + err_set_val->type = type_entry->data.error_union.err_set_type; + err_set_val->data.x_err_set = nullptr; + + payload_val->special = ConstValSpecialUndef; + payload_val->type = payload_type; + + err_union_val->special = ConstValSpecialStatic; + err_union_val->data.x_err_union.error_set = err_set_val; + err_union_val->data.x_err_union.payload = payload_val; + } + + if (err_union_val->special != ConstValSpecialRuntime) { + ErrorTableEntry *err = err_union_val->data.x_err_union.error_set->data.x_err_set; + if (err != nullptr) { + ir_add_error(ira, source_instr, + buf_sprintf("caught unexpected error '%s'", buf_ptr(&err->name))); + return ira->codegen->invalid_inst_gen; + } + + IrInstGen *result; + if (ptr_val->data.x_ptr.mut == ConstPtrMutInfer) { + result = ir_build_unwrap_err_payload_gen(ira, source_instr->scope, + source_instr->source_node, base_ptr, safety_check_on, initializing, result_type); + result->value->special = ConstValSpecialStatic; + } else { + result = ir_const(ira, source_instr, result_type); + } + result->value->data.x_ptr.special = ConstPtrSpecialRef; + result->value->data.x_ptr.data.ref.pointee = err_union_val->data.x_err_union.payload; + result->value->data.x_ptr.mut = ptr_val->data.x_ptr.mut; + return result; + } + } + } + + return ir_build_unwrap_err_payload_gen(ira, source_instr->scope, source_instr->source_node, + base_ptr, safety_check_on, initializing, result_type); +} + +static IrInstGen *ir_analyze_instruction_unwrap_err_payload(IrAnalyze *ira, + IrInstSrcUnwrapErrPayload *instruction) +{ + assert(instruction->value->child); + IrInstGen *value = instruction->value->child; + if (type_is_invalid(value->value->type)) + return ira->codegen->invalid_inst_gen; + + return ir_analyze_unwrap_error_payload(ira, &instruction->base.base, value, instruction->safety_check_on, false); +} + +static IrInstGen *ir_analyze_instruction_fn_proto(IrAnalyze *ira, IrInstSrcFnProto *instruction) { + AstNode *proto_node = instruction->base.base.source_node; + assert(proto_node->type == NodeTypeFnProto); + + IrInstGen *result = ir_const(ira, &instruction->base.base, ira->codegen->builtin_types.entry_type); + result->value->special = ConstValSpecialLazy; + + LazyValueFnType *lazy_fn_type = heap::c_allocator.create(); + lazy_fn_type->ira = ira; ira_ref(ira); + result->value->data.x_lazy = &lazy_fn_type->base; + lazy_fn_type->base.id = LazyValueIdFnType; + + if (proto_node->data.fn_proto.auto_err_set) { + ir_add_error(ira, &instruction->base.base, + buf_sprintf("inferring error set of return type valid only for function definitions")); + return ira->codegen->invalid_inst_gen; + } + + lazy_fn_type->cc = cc_from_fn_proto(&proto_node->data.fn_proto); + if (instruction->callconv_value != nullptr) { + ZigType *cc_enum_type = get_builtin_type(ira->codegen, "CallingConvention"); + + IrInstGen *casted_value = ir_implicit_cast(ira, instruction->callconv_value->child, cc_enum_type); + if (type_is_invalid(casted_value->value->type)) + return ira->codegen->invalid_inst_gen; + + ZigValue *const_value = ir_resolve_const(ira, casted_value, UndefBad); + if (const_value == nullptr) + return ira->codegen->invalid_inst_gen; + + lazy_fn_type->cc = (CallingConvention)bigint_as_u32(&const_value->data.x_enum_tag); + } + + size_t param_count = proto_node->data.fn_proto.params.length; + lazy_fn_type->proto_node = proto_node; + lazy_fn_type->param_types = heap::c_allocator.allocate(param_count); + + for (size_t param_index = 0; param_index < param_count; param_index += 1) { + AstNode *param_node = proto_node->data.fn_proto.params.at(param_index); + assert(param_node->type == NodeTypeParamDecl); + + bool param_is_var_args = param_node->data.param_decl.is_var_args; + if (param_is_var_args) { + const CallingConvention cc = lazy_fn_type->cc; + + if (cc == CallingConventionC) { + break; + } else { + ir_add_error(ira, &instruction->base.base, + buf_sprintf("var args only allowed in functions with C calling convention")); + return ira->codegen->invalid_inst_gen; + } + } + + if (instruction->param_types[param_index] == nullptr) { + lazy_fn_type->is_generic = true; + return result; + } + + IrInstGen *param_type_value = instruction->param_types[param_index]->child; + if (type_is_invalid(param_type_value->value->type)) + return ira->codegen->invalid_inst_gen; + if (ir_resolve_const(ira, param_type_value, LazyOk) == nullptr) + return ira->codegen->invalid_inst_gen; + lazy_fn_type->param_types[param_index] = param_type_value; + } + + if (instruction->align_value != nullptr) { + lazy_fn_type->align_inst = instruction->align_value->child; + if (ir_resolve_const(ira, lazy_fn_type->align_inst, LazyOk) == nullptr) + return ira->codegen->invalid_inst_gen; + } + + lazy_fn_type->return_type = instruction->return_type->child; + if (ir_resolve_const(ira, lazy_fn_type->return_type, LazyOk) == nullptr) + return ira->codegen->invalid_inst_gen; + + return result; +} + +static IrInstGen *ir_analyze_instruction_test_comptime(IrAnalyze *ira, IrInstSrcTestComptime *instruction) { + IrInstGen *value = instruction->value->child; + if (type_is_invalid(value->value->type)) + return ira->codegen->invalid_inst_gen; + + return ir_const_bool(ira, &instruction->base.base, instr_is_comptime(value)); +} + +static IrInstGen *ir_analyze_instruction_check_switch_prongs(IrAnalyze *ira, + IrInstSrcCheckSwitchProngs *instruction) +{ + IrInstGen *target_value = instruction->target_value->child; + ZigType *switch_type = target_value->value->type; + if (type_is_invalid(switch_type)) + return ira->codegen->invalid_inst_gen; + + ZigValue *original_value = ((IrInstSrcSwitchTarget *)(instruction->target_value))->target_value_ptr->child->value; + bool target_is_originally_union = original_value->type->id == ZigTypeIdPointer && + original_value->type->data.pointer.child_type->id == ZigTypeIdUnion; + + if (switch_type->id == ZigTypeIdEnum) { + HashMap field_prev_uses = {}; + field_prev_uses.init(switch_type->data.enumeration.src_field_count); + + for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) { + IrInstSrcCheckSwitchProngsRange *range = &instruction->ranges[range_i]; + + IrInstGen *start_value_uncasted = range->start->child; + if (type_is_invalid(start_value_uncasted->value->type)) + return ira->codegen->invalid_inst_gen; + IrInstGen *start_value = ir_implicit_cast(ira, start_value_uncasted, switch_type); + if (type_is_invalid(start_value->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *end_value_uncasted = range->end->child; + if (type_is_invalid(end_value_uncasted->value->type)) + return ira->codegen->invalid_inst_gen; + IrInstGen *end_value = ir_implicit_cast(ira, end_value_uncasted, switch_type); + if (type_is_invalid(end_value->value->type)) + return ira->codegen->invalid_inst_gen; + + assert(start_value->value->type->id == ZigTypeIdEnum); + BigInt start_index; + bigint_init_bigint(&start_index, &start_value->value->data.x_enum_tag); + + assert(end_value->value->type->id == ZigTypeIdEnum); + BigInt end_index; + bigint_init_bigint(&end_index, &end_value->value->data.x_enum_tag); + + if (bigint_cmp(&start_index, &end_index) == CmpGT) { + ir_add_error(ira, &start_value->base, + buf_sprintf("range start value is greater than the end value")); + } + + BigInt field_index; + bigint_init_bigint(&field_index, &start_index); + for (;;) { + Cmp cmp = bigint_cmp(&field_index, &end_index); + if (cmp == CmpGT) { + break; + } + auto entry = field_prev_uses.put_unique(field_index, start_value->base.source_node); + if (entry) { + AstNode *prev_node = entry->value; + TypeEnumField *enum_field = find_enum_field_by_tag(switch_type, &field_index); + assert(enum_field != nullptr); + ErrorMsg *msg = ir_add_error(ira, &start_value->base, + buf_sprintf("duplicate switch value: '%s.%s'", buf_ptr(&switch_type->name), + buf_ptr(enum_field->name))); + add_error_note(ira->codegen, msg, prev_node, buf_sprintf("other value is here")); + } + bigint_incr(&field_index); + } + } + if (instruction->have_underscore_prong) { + if (!switch_type->data.enumeration.non_exhaustive) { + ir_add_error(ira, &instruction->base.base, + buf_sprintf("switch on exhaustive enum has `_` prong")); + } else if (target_is_originally_union) { + ir_add_error(ira, &instruction->base.base, + buf_sprintf("`_` prong not allowed when switching on tagged union")); + } + for (uint32_t i = 0; i < switch_type->data.enumeration.src_field_count; i += 1) { + TypeEnumField *enum_field = &switch_type->data.enumeration.fields[i]; + if (buf_eql_str(enum_field->name, "_")) + continue; + + auto entry = field_prev_uses.maybe_get(enum_field->value); + if (!entry) { + ir_add_error(ira, &instruction->base.base, + buf_sprintf("enumeration value '%s.%s' not handled in switch", buf_ptr(&switch_type->name), + buf_ptr(enum_field->name))); + } + } + } else if (instruction->else_prong == nullptr) { + if (switch_type->data.enumeration.non_exhaustive && !target_is_originally_union) { + ir_add_error(ira, &instruction->base.base, + buf_sprintf("switch on non-exhaustive enum must include `else` or `_` prong")); + } + for (uint32_t i = 0; i < switch_type->data.enumeration.src_field_count; i += 1) { + TypeEnumField *enum_field = &switch_type->data.enumeration.fields[i]; + + auto entry = field_prev_uses.maybe_get(enum_field->value); + if (!entry) { + ir_add_error(ira, &instruction->base.base, + buf_sprintf("enumeration value '%s.%s' not handled in switch", buf_ptr(&switch_type->name), + buf_ptr(enum_field->name))); + } + } + } else if(!switch_type->data.enumeration.non_exhaustive && switch_type->data.enumeration.src_field_count == instruction->range_count) { + ir_add_error_node(ira, instruction->else_prong, + buf_sprintf("unreachable else prong, all cases already handled")); + return ira->codegen->invalid_inst_gen; + } + } else if (switch_type->id == ZigTypeIdErrorSet) { + if (!resolve_inferred_error_set(ira->codegen, switch_type, target_value->base.source_node)) { + return ira->codegen->invalid_inst_gen; + } + + size_t field_prev_uses_count = ira->codegen->errors_by_index.length; + AstNode **field_prev_uses = heap::c_allocator.allocate(field_prev_uses_count); + + for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) { + IrInstSrcCheckSwitchProngsRange *range = &instruction->ranges[range_i]; + + IrInstGen *start_value_uncasted = range->start->child; + if (type_is_invalid(start_value_uncasted->value->type)) + return ira->codegen->invalid_inst_gen; + IrInstGen *start_value = ir_implicit_cast(ira, start_value_uncasted, switch_type); + if (type_is_invalid(start_value->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *end_value_uncasted = range->end->child; + if (type_is_invalid(end_value_uncasted->value->type)) + return ira->codegen->invalid_inst_gen; + IrInstGen *end_value = ir_implicit_cast(ira, end_value_uncasted, switch_type); + if (type_is_invalid(end_value->value->type)) + return ira->codegen->invalid_inst_gen; + + ir_assert(start_value->value->type->id == ZigTypeIdErrorSet, &instruction->base.base); + uint32_t start_index = start_value->value->data.x_err_set->value; + + ir_assert(end_value->value->type->id == ZigTypeIdErrorSet, &instruction->base.base); + uint32_t end_index = end_value->value->data.x_err_set->value; + + if (start_index != end_index) { + ir_add_error(ira, &end_value->base, buf_sprintf("ranges not allowed when switching on errors")); + return ira->codegen->invalid_inst_gen; + } + + AstNode *prev_node = field_prev_uses[start_index]; + if (prev_node != nullptr) { + Buf *err_name = &ira->codegen->errors_by_index.at(start_index)->name; + ErrorMsg *msg = ir_add_error(ira, &start_value->base, + buf_sprintf("duplicate switch value: '%s.%s'", buf_ptr(&switch_type->name), buf_ptr(err_name))); + add_error_note(ira->codegen, msg, prev_node, buf_sprintf("other value is here")); + } + field_prev_uses[start_index] = start_value->base.source_node; + } + if (instruction->else_prong == nullptr) { + if (type_is_global_error_set(switch_type)) { + ir_add_error(ira, &instruction->base.base, + buf_sprintf("else prong required when switching on type 'anyerror'")); + return ira->codegen->invalid_inst_gen; + } else { + for (uint32_t i = 0; i < switch_type->data.error_set.err_count; i += 1) { + ErrorTableEntry *err_entry = switch_type->data.error_set.errors[i]; + + AstNode *prev_node = field_prev_uses[err_entry->value]; + if (prev_node == nullptr) { + ir_add_error(ira, &instruction->base.base, + buf_sprintf("error.%s not handled in switch", buf_ptr(&err_entry->name))); + } + } + } + } + + heap::c_allocator.deallocate(field_prev_uses, field_prev_uses_count); + } else if (switch_type->id == ZigTypeIdInt) { + RangeSet rs = {0}; + for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) { + IrInstSrcCheckSwitchProngsRange *range = &instruction->ranges[range_i]; + + IrInstGen *start_value = range->start->child; + if (type_is_invalid(start_value->value->type)) + return ira->codegen->invalid_inst_gen; + IrInstGen *casted_start_value = ir_implicit_cast(ira, start_value, switch_type); + if (type_is_invalid(casted_start_value->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *end_value = range->end->child; + if (type_is_invalid(end_value->value->type)) + return ira->codegen->invalid_inst_gen; + IrInstGen *casted_end_value = ir_implicit_cast(ira, end_value, switch_type); + if (type_is_invalid(casted_end_value->value->type)) + return ira->codegen->invalid_inst_gen; + + ZigValue *start_val = ir_resolve_const(ira, casted_start_value, UndefBad); + if (!start_val) + return ira->codegen->invalid_inst_gen; + + ZigValue *end_val = ir_resolve_const(ira, casted_end_value, UndefBad); + if (!end_val) + return ira->codegen->invalid_inst_gen; + + assert(start_val->type->id == ZigTypeIdInt || start_val->type->id == ZigTypeIdComptimeInt); + assert(end_val->type->id == ZigTypeIdInt || end_val->type->id == ZigTypeIdComptimeInt); + + if (bigint_cmp(&start_val->data.x_bigint, &end_val->data.x_bigint) == CmpGT) { + ir_add_error(ira, &start_value->base, + buf_sprintf("range start value is greater than the end value")); + } + + AstNode *prev_node = rangeset_add_range(&rs, &start_val->data.x_bigint, &end_val->data.x_bigint, + start_value->base.source_node); + if (prev_node != nullptr) { + ErrorMsg *msg = ir_add_error(ira, &start_value->base, buf_sprintf("duplicate switch value")); + add_error_note(ira->codegen, msg, prev_node, buf_sprintf("previous value is here")); + return ira->codegen->invalid_inst_gen; + } + } + + BigInt min_val; + eval_min_max_value_int(ira->codegen, switch_type, &min_val, false); + BigInt max_val; + eval_min_max_value_int(ira->codegen, switch_type, &max_val, true); + bool handles_all_cases = rangeset_spans(&rs, &min_val, &max_val); + if (!handles_all_cases && instruction->else_prong == nullptr) { + ir_add_error(ira, &instruction->base.base, buf_sprintf("switch must handle all possibilities")); + return ira->codegen->invalid_inst_gen; + } else if(handles_all_cases && instruction->else_prong != nullptr) { + ir_add_error_node(ira, instruction->else_prong, + buf_sprintf("unreachable else prong, all cases already handled")); + return ira->codegen->invalid_inst_gen; + } + } else if (switch_type->id == ZigTypeIdBool) { + int seenTrue = 0; + int seenFalse = 0; + for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) { + IrInstSrcCheckSwitchProngsRange *range = &instruction->ranges[range_i]; + + IrInstGen *value = range->start->child; + + IrInstGen *casted_value = ir_implicit_cast(ira, value, switch_type); + if (type_is_invalid(casted_value->value->type)) + return ira->codegen->invalid_inst_gen; + + ZigValue *const_expr_val = ir_resolve_const(ira, casted_value, UndefBad); + if (!const_expr_val) + return ira->codegen->invalid_inst_gen; + + assert(const_expr_val->type->id == ZigTypeIdBool); + + if (const_expr_val->data.x_bool == true) { + seenTrue += 1; + } else { + seenFalse += 1; + } + + if ((seenTrue > 1) || (seenFalse > 1)) { + ir_add_error(ira, &value->base, buf_sprintf("duplicate switch value")); + return ira->codegen->invalid_inst_gen; + } + } + if (((seenTrue < 1) || (seenFalse < 1)) && instruction->else_prong == nullptr) { + ir_add_error(ira, &instruction->base.base, buf_sprintf("switch must handle all possibilities")); + return ira->codegen->invalid_inst_gen; + } + + if(seenTrue == 1 && seenFalse == 1 && instruction->else_prong != nullptr) { + ir_add_error_node(ira, instruction->else_prong, + buf_sprintf("unreachable else prong, all cases already handled")); + return ira->codegen->invalid_inst_gen; + } + } else if (instruction->else_prong == nullptr) { + ir_add_error(ira, &instruction->base.base, + buf_sprintf("else prong required when switching on type '%s'", buf_ptr(&switch_type->name))); + return ira->codegen->invalid_inst_gen; + } else if(switch_type->id == ZigTypeIdMetaType) { + HashMap prevs; + // HashMap doubles capacity when reaching 60% capacity, + // because we know the size at init we can avoid reallocation by doubling it here + prevs.init(instruction->range_count * 2); + for (size_t range_i = 0; range_i < instruction->range_count; range_i += 1) { + IrInstSrcCheckSwitchProngsRange *range = &instruction->ranges[range_i]; + + IrInstGen *value = range->start->child; + IrInstGen *casted_value = ir_implicit_cast(ira, value, switch_type); + if (type_is_invalid(casted_value->value->type)) { + prevs.deinit(); + return ira->codegen->invalid_inst_gen; + } + + ZigValue *const_expr_val = ir_resolve_const(ira, casted_value, UndefBad); + if (!const_expr_val) { + prevs.deinit(); + return ira->codegen->invalid_inst_gen; + } + + auto entry = prevs.put_unique(const_expr_val->data.x_type, value); + if(entry != nullptr) { + ErrorMsg *msg = ir_add_error(ira, &value->base, buf_sprintf("duplicate switch value")); + add_error_note(ira->codegen, msg, entry->value->base.source_node, buf_sprintf("previous value is here")); + prevs.deinit(); + return ira->codegen->invalid_inst_gen; + } + } + prevs.deinit(); + } + return ir_const_void(ira, &instruction->base.base); +} + +static IrInstGen *ir_analyze_instruction_check_statement_is_void(IrAnalyze *ira, + IrInstSrcCheckStatementIsVoid *instruction) +{ + IrInstGen *statement_value = instruction->statement_value->child; + ZigType *statement_type = statement_value->value->type; + if (type_is_invalid(statement_type)) + return ira->codegen->invalid_inst_gen; + + if (statement_type->id != ZigTypeIdVoid && statement_type->id != ZigTypeIdUnreachable) { + ir_add_error(ira, &instruction->base.base, buf_sprintf("expression value is ignored")); + } + + return ir_const_void(ira, &instruction->base.base); +} + +static IrInstGen *ir_analyze_instruction_panic(IrAnalyze *ira, IrInstSrcPanic *instruction) { + IrInstGen *msg = instruction->msg->child; + if (type_is_invalid(msg->value->type)) + return ir_unreach_error(ira); + + if (ir_should_inline(ira->old_irb.exec, instruction->base.base.scope)) { + ir_add_error(ira, &instruction->base.base, buf_sprintf("encountered @panic at compile-time")); + return ir_unreach_error(ira); + } + + ZigType *u8_ptr_type = get_pointer_to_type_extra(ira->codegen, ira->codegen->builtin_types.entry_u8, + true, false, PtrLenUnknown, 0, 0, 0, false); + ZigType *str_type = get_slice_type(ira->codegen, u8_ptr_type); + IrInstGen *casted_msg = ir_implicit_cast(ira, msg, str_type); + if (type_is_invalid(casted_msg->value->type)) + return ir_unreach_error(ira); + + IrInstGen *new_instruction = ir_build_panic_gen(ira, &instruction->base.base, casted_msg); + return ir_finish_anal(ira, new_instruction); +} + +static IrInstGen *ir_align_cast(IrAnalyze *ira, IrInstGen *target, uint32_t align_bytes, bool safety_check_on) { + Error err; + + ZigType *target_type = target->value->type; + assert(!type_is_invalid(target_type)); + + ZigType *result_type; + uint32_t old_align_bytes; + + ZigType *actual_ptr = target_type; + if (actual_ptr->id == ZigTypeIdOptional) { + actual_ptr = actual_ptr->data.maybe.child_type; + } else if (is_slice(actual_ptr)) { + actual_ptr = actual_ptr->data.structure.fields[slice_ptr_index]->type_entry; + } + + if (safety_check_on && !type_has_bits(ira->codegen, actual_ptr)) { + ir_add_error(ira, &target->base, + buf_sprintf("cannot adjust alignment of zero sized type '%s'", buf_ptr(&target_type->name))); + return ira->codegen->invalid_inst_gen; + } + + if (target_type->id == ZigTypeIdPointer) { + result_type = adjust_ptr_align(ira->codegen, target_type, align_bytes); + if ((err = resolve_ptr_align(ira, target_type, &old_align_bytes))) + return ira->codegen->invalid_inst_gen; + } else if (target_type->id == ZigTypeIdFn) { + FnTypeId fn_type_id = target_type->data.fn.fn_type_id; + old_align_bytes = fn_type_id.alignment; + fn_type_id.alignment = align_bytes; + result_type = get_fn_type(ira->codegen, &fn_type_id); + } else if (target_type->id == ZigTypeIdAnyFrame) { + if (align_bytes >= target_fn_align(ira->codegen->zig_target)) { + result_type = target_type; + } else { + ir_add_error(ira, &target->base, buf_sprintf("sub-aligned anyframe not allowed")); + return ira->codegen->invalid_inst_gen; + } + } else if (target_type->id == ZigTypeIdOptional && + target_type->data.maybe.child_type->id == ZigTypeIdPointer) + { + ZigType *ptr_type = target_type->data.maybe.child_type; + if ((err = resolve_ptr_align(ira, ptr_type, &old_align_bytes))) + return ira->codegen->invalid_inst_gen; + ZigType *better_ptr_type = adjust_ptr_align(ira->codegen, ptr_type, align_bytes); + + result_type = get_optional_type(ira->codegen, better_ptr_type); + } else if (target_type->id == ZigTypeIdOptional && + target_type->data.maybe.child_type->id == ZigTypeIdFn) + { + FnTypeId fn_type_id = target_type->data.maybe.child_type->data.fn.fn_type_id; + old_align_bytes = fn_type_id.alignment; + fn_type_id.alignment = align_bytes; + ZigType *fn_type = get_fn_type(ira->codegen, &fn_type_id); + result_type = get_optional_type(ira->codegen, fn_type); + } else if (is_slice(target_type)) { + ZigType *slice_ptr_type = target_type->data.structure.fields[slice_ptr_index]->type_entry; + if ((err = resolve_ptr_align(ira, slice_ptr_type, &old_align_bytes))) + return ira->codegen->invalid_inst_gen; + ZigType *result_ptr_type = adjust_ptr_align(ira->codegen, slice_ptr_type, align_bytes); + result_type = get_slice_type(ira->codegen, result_ptr_type); + } else { + ir_add_error(ira, &target->base, + buf_sprintf("expected pointer or slice, found '%s'", buf_ptr(&target_type->name))); + return ira->codegen->invalid_inst_gen; + } + + if (instr_is_comptime(target)) { + ZigValue *val = ir_resolve_const(ira, target, UndefBad); + if (!val) + return ira->codegen->invalid_inst_gen; + + if (val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr && + val->data.x_ptr.data.hard_coded_addr.addr % align_bytes != 0) + { + ir_add_error(ira, &target->base, + buf_sprintf("pointer address 0x%" ZIG_PRI_x64 " is not aligned to %" PRIu32 " bytes", + val->data.x_ptr.data.hard_coded_addr.addr, align_bytes)); + return ira->codegen->invalid_inst_gen; + } + + IrInstGen *result = ir_const(ira, &target->base, result_type); + copy_const_val(ira->codegen, result->value, val); + result->value->type = result_type; + return result; + } + + if (safety_check_on && align_bytes > old_align_bytes && align_bytes != 1) { + return ir_build_align_cast_gen(ira, target->base.scope, target->base.source_node, target, result_type); + } else { + return ir_build_cast(ira, &target->base, result_type, target, CastOpNoop); + } +} + +static IrInstGen *ir_analyze_ptr_cast(IrAnalyze *ira, IrInst* source_instr, IrInstGen *ptr, + IrInst *ptr_src, ZigType *dest_type, IrInst *dest_type_src, bool safety_check_on, + bool keep_bigger_alignment) +{ + Error err; + + ZigType *src_type = ptr->value->type; + assert(!type_is_invalid(src_type)); + + if (src_type == dest_type) { + return ptr; + } + + // We have a check for zero bits later so we use get_src_ptr_type to + // validate src_type and dest_type. + + ZigType *if_slice_ptr_type; + if (is_slice(src_type)) { + TypeStructField *ptr_field = src_type->data.structure.fields[slice_ptr_index]; + if_slice_ptr_type = resolve_struct_field_type(ira->codegen, ptr_field); + } else { + if_slice_ptr_type = src_type; + + ZigType *src_ptr_type = get_src_ptr_type(src_type); + if (src_ptr_type == nullptr) { + ir_add_error(ira, ptr_src, buf_sprintf("expected pointer, found '%s'", buf_ptr(&src_type->name))); + return ira->codegen->invalid_inst_gen; + } + } + + ZigType *dest_ptr_type = get_src_ptr_type(dest_type); + if (dest_ptr_type == nullptr) { + ir_add_error(ira, dest_type_src, + buf_sprintf("expected pointer, found '%s'", buf_ptr(&dest_type->name))); + return ira->codegen->invalid_inst_gen; + } + + if (get_ptr_const(ira->codegen, src_type) && !get_ptr_const(ira->codegen, dest_type)) { + ir_add_error(ira, source_instr, buf_sprintf("cast discards const qualifier")); + return ira->codegen->invalid_inst_gen; + } + uint32_t dest_align_bytes; + if ((err = resolve_ptr_align(ira, dest_type, &dest_align_bytes))) + return ira->codegen->invalid_inst_gen; + + uint32_t src_align_bytes = 0; + if (keep_bigger_alignment || dest_align_bytes != 1) { + if ((err = resolve_ptr_align(ira, src_type, &src_align_bytes))) + return ira->codegen->invalid_inst_gen; + } + + if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusZeroBitsKnown))) + return ira->codegen->invalid_inst_gen; + + if ((err = type_resolve(ira->codegen, src_type, ResolveStatusZeroBitsKnown))) + return ira->codegen->invalid_inst_gen; + + if (safety_check_on && + type_has_bits(ira->codegen, dest_type) && + !type_has_bits(ira->codegen, if_slice_ptr_type)) + { + ErrorMsg *msg = ir_add_error(ira, source_instr, + buf_sprintf("'%s' and '%s' do not have the same in-memory representation", + buf_ptr(&src_type->name), buf_ptr(&dest_type->name))); + add_error_note(ira->codegen, msg, ptr_src->source_node, + buf_sprintf("'%s' has no in-memory bits", buf_ptr(&src_type->name))); + add_error_note(ira->codegen, msg, dest_type_src->source_node, + buf_sprintf("'%s' has in-memory bits", buf_ptr(&dest_type->name))); + return ira->codegen->invalid_inst_gen; + } + + // For slices, follow the `ptr` field. + if (is_slice(src_type)) { + TypeStructField *ptr_field = src_type->data.structure.fields[slice_ptr_index]; + IrInstGen *ptr_ref = ir_get_ref(ira, source_instr, ptr, true, false); + IrInstGen *ptr_ptr = ir_analyze_struct_field_ptr(ira, source_instr, ptr_field, ptr_ref, src_type, false); + ptr = ir_get_deref(ira, source_instr, ptr_ptr, nullptr); + } + + if (instr_is_comptime(ptr)) { + bool dest_allows_addr_zero = ptr_allows_addr_zero(dest_type); + UndefAllowed is_undef_allowed = dest_allows_addr_zero ? UndefOk : UndefBad; + ZigValue *val = ir_resolve_const(ira, ptr, is_undef_allowed); + if (val == nullptr) + return ira->codegen->invalid_inst_gen; + + if (value_is_comptime(val) && val->special != ConstValSpecialUndef) { + bool is_addr_zero = val->data.x_ptr.special == ConstPtrSpecialNull || + (val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr && + val->data.x_ptr.data.hard_coded_addr.addr == 0); + if (is_addr_zero && !dest_allows_addr_zero) { + ir_add_error(ira, source_instr, + buf_sprintf("null pointer casted to type '%s'", buf_ptr(&dest_type->name))); + return ira->codegen->invalid_inst_gen; + } + } + + IrInstGen *result; + if (val->data.x_ptr.mut == ConstPtrMutInfer) { + result = ir_build_ptr_cast_gen(ira, source_instr, dest_type, ptr, safety_check_on); + } else { + result = ir_const(ira, source_instr, dest_type); + } + InferredStructField *isf = (val->type->id == ZigTypeIdPointer) ? + val->type->data.pointer.inferred_struct_field : nullptr; + if (isf == nullptr) { + copy_const_val(ira->codegen, result->value, val); + } else { + // The destination value should have x_ptr struct pointing to underlying struct value + result->value->data.x_ptr.mut = val->data.x_ptr.mut; + TypeStructField *field = find_struct_type_field(isf->inferred_struct_type, isf->field_name); + assert(field != nullptr); + if (field->is_comptime) { + result->value->data.x_ptr.special = ConstPtrSpecialRef; + result->value->data.x_ptr.data.ref.pointee = field->init_val; + } else { + assert(val->data.x_ptr.special == ConstPtrSpecialRef); + result->value->data.x_ptr.special = ConstPtrSpecialBaseStruct; + result->value->data.x_ptr.data.base_struct.struct_val = val->data.x_ptr.data.ref.pointee; + result->value->data.x_ptr.data.base_struct.field_index = field->src_index; + } + result->value->special = ConstValSpecialStatic; + } + result->value->type = dest_type; + + // Keep the bigger alignment, it can only help- unless the target is zero bits. + if (keep_bigger_alignment && src_align_bytes > dest_align_bytes && type_has_bits(ira->codegen, dest_type)) { + result = ir_align_cast(ira, result, src_align_bytes, false); + } + + return result; + } + + if (src_align_bytes != 0 && dest_align_bytes > src_align_bytes) { + ErrorMsg *msg = ir_add_error(ira, source_instr, buf_sprintf("cast increases pointer alignment")); + add_error_note(ira->codegen, msg, ptr_src->source_node, + buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&src_type->name), src_align_bytes)); + add_error_note(ira->codegen, msg, dest_type_src->source_node, + buf_sprintf("'%s' has alignment %" PRIu32, buf_ptr(&dest_type->name), dest_align_bytes)); + return ira->codegen->invalid_inst_gen; + } + + IrInstGen *casted_ptr = ir_build_ptr_cast_gen(ira, source_instr, dest_type, ptr, safety_check_on); + + // Keep the bigger alignment, it can only help- unless the target is zero bits. + IrInstGen *result; + if (keep_bigger_alignment && src_align_bytes > dest_align_bytes && type_has_bits(ira->codegen, dest_type)) { + result = ir_align_cast(ira, casted_ptr, src_align_bytes, false); + if (type_is_invalid(result->value->type)) + return ira->codegen->invalid_inst_gen; + } else { + result = casted_ptr; + } + return result; +} + +static IrInstGen *ir_analyze_instruction_ptr_cast(IrAnalyze *ira, IrInstSrcPtrCast *instruction) { + IrInstGen *dest_type_value = instruction->dest_type->child; + ZigType *dest_type = ir_resolve_type(ira, dest_type_value); + if (type_is_invalid(dest_type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *ptr = instruction->ptr->child; + ZigType *src_type = ptr->value->type; + if (type_is_invalid(src_type)) + return ira->codegen->invalid_inst_gen; + + bool keep_bigger_alignment = true; + return ir_analyze_ptr_cast(ira, &instruction->base.base, ptr, &instruction->ptr->base, + dest_type, &dest_type_value->base, instruction->safety_check_on, keep_bigger_alignment); +} + +static void buf_write_value_bytes_array(CodeGen *codegen, uint8_t *buf, ZigValue *val, size_t len) { + size_t buf_i = 0; + // TODO optimize the buf case + expand_undef_array(codegen, val); + for (size_t elem_i = 0; elem_i < val->type->data.array.len; elem_i += 1) { + ZigValue *elem = &val->data.x_array.data.s_none.elements[elem_i]; + buf_write_value_bytes(codegen, &buf[buf_i], elem); + buf_i += type_size(codegen, elem->type); + } + if (val->type->id == ZigTypeIdArray && val->type->data.array.sentinel != nullptr) { + buf_write_value_bytes(codegen, &buf[buf_i], val->type->data.array.sentinel); + } +} + +static void buf_write_value_bytes(CodeGen *codegen, uint8_t *buf, ZigValue *val) { + if (val->special == ConstValSpecialUndef) { + expand_undef_struct(codegen, val); + val->special = ConstValSpecialStatic; + } + assert(val->special == ConstValSpecialStatic); + switch (val->type->id) { + case ZigTypeIdInvalid: + case ZigTypeIdMetaType: + case ZigTypeIdOpaque: + case ZigTypeIdBoundFn: + case ZigTypeIdUnreachable: + case ZigTypeIdComptimeFloat: + case ZigTypeIdComptimeInt: + case ZigTypeIdEnumLiteral: + case ZigTypeIdUndefined: + case ZigTypeIdNull: + case ZigTypeIdErrorUnion: + case ZigTypeIdErrorSet: + zig_unreachable(); + case ZigTypeIdVoid: + return; + case ZigTypeIdBool: + buf[0] = val->data.x_bool ? 1 : 0; + return; + case ZigTypeIdInt: + bigint_write_twos_complement(&val->data.x_bigint, buf, val->type->data.integral.bit_count, + codegen->is_big_endian); + return; + case ZigTypeIdEnum: + bigint_write_twos_complement(&val->data.x_enum_tag, buf, + val->type->data.enumeration.tag_int_type->data.integral.bit_count, + codegen->is_big_endian); + return; + case ZigTypeIdFloat: + float_write_ieee597(val, buf, codegen->is_big_endian); + return; + case ZigTypeIdPointer: + if (val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) { + BigInt bn; + bigint_init_unsigned(&bn, val->data.x_ptr.data.hard_coded_addr.addr); + bigint_write_twos_complement(&bn, buf, codegen->builtin_types.entry_usize->data.integral.bit_count, codegen->is_big_endian); + return; + } else { + zig_unreachable(); + } + case ZigTypeIdArray: + return buf_write_value_bytes_array(codegen, buf, val, val->type->data.array.len); + case ZigTypeIdVector: + return buf_write_value_bytes_array(codegen, buf, val, val->type->data.vector.len); + case ZigTypeIdStruct: + switch (val->type->data.structure.layout) { + case ContainerLayoutAuto: + zig_unreachable(); + case ContainerLayoutExtern: { + size_t src_field_count = val->type->data.structure.src_field_count; + for (size_t field_i = 0; field_i < src_field_count; field_i += 1) { + TypeStructField *struct_field = val->type->data.structure.fields[field_i]; + if (struct_field->gen_index == SIZE_MAX) + continue; + ZigValue *field_val = val->data.x_struct.fields[field_i]; + size_t offset = struct_field->offset; + buf_write_value_bytes(codegen, buf + offset, field_val); + } + return; + } + case ContainerLayoutPacked: { + size_t src_field_count = val->type->data.structure.src_field_count; + size_t gen_field_count = val->type->data.structure.gen_field_count; + size_t gen_i = 0; + size_t src_i = 0; + size_t offset = 0; + bool is_big_endian = codegen->is_big_endian; + uint8_t child_buf_prealloc[16]; + size_t child_buf_len = 16; + uint8_t *child_buf = child_buf_prealloc; + while (gen_i < gen_field_count) { + size_t big_int_byte_count = val->type->data.structure.host_int_bytes[gen_i]; + if (big_int_byte_count > child_buf_len) { + child_buf = heap::c_allocator.allocate_nonzero(big_int_byte_count); + child_buf_len = big_int_byte_count; + } + BigInt big_int; + bigint_init_unsigned(&big_int, 0); + size_t used_bits = 0; + while (src_i < src_field_count) { + TypeStructField *field = val->type->data.structure.fields[src_i]; + assert(field->gen_index != SIZE_MAX); + if (field->gen_index != gen_i) + break; + uint32_t packed_bits_size = type_size_bits(codegen, field->type_entry); + buf_write_value_bytes(codegen, child_buf, val->data.x_struct.fields[src_i]); + BigInt child_val; + bigint_read_twos_complement(&child_val, child_buf, packed_bits_size, is_big_endian, + false); + if (is_big_endian) { + BigInt shift_amt; + bigint_init_unsigned(&shift_amt, packed_bits_size); + BigInt shifted; + bigint_shl(&shifted, &big_int, &shift_amt); + bigint_or(&big_int, &shifted, &child_val); + } else { + BigInt shift_amt; + bigint_init_unsigned(&shift_amt, used_bits); + BigInt child_val_shifted; + bigint_shl(&child_val_shifted, &child_val, &shift_amt); + BigInt tmp; + bigint_or(&tmp, &big_int, &child_val_shifted); + big_int = tmp; + used_bits += packed_bits_size; + } + src_i += 1; + } + bigint_write_twos_complement(&big_int, buf + offset, big_int_byte_count * 8, is_big_endian); + offset += big_int_byte_count; + gen_i += 1; + } + return; + } + } + zig_unreachable(); + case ZigTypeIdOptional: + zig_panic("TODO buf_write_value_bytes maybe type"); + case ZigTypeIdFn: + zig_panic("TODO buf_write_value_bytes fn type"); + case ZigTypeIdUnion: + zig_panic("TODO buf_write_value_bytes union type"); + case ZigTypeIdFnFrame: + zig_panic("TODO buf_write_value_bytes async fn frame type"); + case ZigTypeIdAnyFrame: + zig_panic("TODO buf_write_value_bytes anyframe type"); + } + zig_unreachable(); +} + +static Error buf_read_value_bytes_array(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, uint8_t *buf, + ZigValue *val, ZigType *elem_type, size_t len) +{ + Error err; + uint64_t elem_size = type_size(codegen, elem_type); + + switch (val->data.x_array.special) { + case ConstArraySpecialNone: + val->data.x_array.data.s_none.elements = codegen->pass1_arena->allocate(len); + for (size_t i = 0; i < len; i++) { + ZigValue *elem = &val->data.x_array.data.s_none.elements[i]; + elem->special = ConstValSpecialStatic; + elem->type = elem_type; + if ((err = buf_read_value_bytes(ira, codegen, source_node, buf + (elem_size * i), elem))) + return err; + } + return ErrorNone; + case ConstArraySpecialUndef: + zig_panic("TODO buf_read_value_bytes ConstArraySpecialUndef array type"); + case ConstArraySpecialBuf: + zig_panic("TODO buf_read_value_bytes ConstArraySpecialBuf array type"); + } + zig_unreachable(); +} + +static Error buf_read_value_bytes(IrAnalyze *ira, CodeGen *codegen, AstNode *source_node, uint8_t *buf, ZigValue *val) { + Error err; + src_assert(val->special == ConstValSpecialStatic, source_node); + switch (val->type->id) { + case ZigTypeIdInvalid: + case ZigTypeIdMetaType: + case ZigTypeIdOpaque: + case ZigTypeIdBoundFn: + case ZigTypeIdUnreachable: + case ZigTypeIdComptimeFloat: + case ZigTypeIdComptimeInt: + case ZigTypeIdEnumLiteral: + case ZigTypeIdUndefined: + case ZigTypeIdNull: + zig_unreachable(); + case ZigTypeIdVoid: + return ErrorNone; + case ZigTypeIdBool: + val->data.x_bool = (buf[0] != 0); + return ErrorNone; + case ZigTypeIdInt: + bigint_read_twos_complement(&val->data.x_bigint, buf, val->type->data.integral.bit_count, + codegen->is_big_endian, val->type->data.integral.is_signed); + return ErrorNone; + case ZigTypeIdFloat: + float_read_ieee597(val, buf, codegen->is_big_endian); + return ErrorNone; + case ZigTypeIdPointer: + { + val->data.x_ptr.special = ConstPtrSpecialHardCodedAddr; + BigInt bn; + bigint_read_twos_complement(&bn, buf, codegen->builtin_types.entry_usize->data.integral.bit_count, + codegen->is_big_endian, false); + val->data.x_ptr.data.hard_coded_addr.addr = bigint_as_usize(&bn); + return ErrorNone; + } + case ZigTypeIdArray: + return buf_read_value_bytes_array(ira, codegen, source_node, buf, val, val->type->data.array.child_type, + val->type->data.array.len); + case ZigTypeIdVector: + return buf_read_value_bytes_array(ira, codegen, source_node, buf, val, val->type->data.vector.elem_type, + val->type->data.vector.len); + case ZigTypeIdEnum: + switch (val->type->data.enumeration.layout) { + case ContainerLayoutAuto: + zig_panic("TODO buf_read_value_bytes enum auto"); + case ContainerLayoutPacked: + zig_panic("TODO buf_read_value_bytes enum packed"); + case ContainerLayoutExtern: { + ZigType *tag_int_type = val->type->data.enumeration.tag_int_type; + src_assert(tag_int_type->id == ZigTypeIdInt, source_node); + bigint_read_twos_complement(&val->data.x_enum_tag, buf, tag_int_type->data.integral.bit_count, + codegen->is_big_endian, tag_int_type->data.integral.is_signed); + return ErrorNone; + } + } + zig_unreachable(); + case ZigTypeIdStruct: + switch (val->type->data.structure.layout) { + case ContainerLayoutAuto: { + switch(val->type->data.structure.special){ + case StructSpecialNone: + case StructSpecialInferredTuple: + case StructSpecialInferredStruct: { + ErrorMsg *msg = opt_ir_add_error_node(ira, codegen, source_node, + buf_sprintf("non-extern, non-packed struct '%s' cannot have its bytes reinterpreted", + buf_ptr(&val->type->name))); + add_error_note(codegen, msg, val->type->data.structure.decl_node, + buf_sprintf("declared here")); + break; + } + case StructSpecialSlice: { + opt_ir_add_error_node(ira, codegen, source_node, + buf_sprintf("slice '%s' cannot have its bytes reinterpreted", + buf_ptr(&val->type->name))); + break; + } + } + return ErrorSemanticAnalyzeFail; + } + case ContainerLayoutExtern: { + size_t src_field_count = val->type->data.structure.src_field_count; + val->data.x_struct.fields = alloc_const_vals_ptrs(codegen, src_field_count); + for (size_t field_i = 0; field_i < src_field_count; field_i += 1) { + ZigValue *field_val = val->data.x_struct.fields[field_i]; + field_val->special = ConstValSpecialStatic; + TypeStructField *struct_field = val->type->data.structure.fields[field_i]; + field_val->type = struct_field->type_entry; + if (struct_field->gen_index == SIZE_MAX) + continue; + size_t offset = struct_field->offset; + uint8_t *new_buf = buf + offset; + if ((err = buf_read_value_bytes(ira, codegen, source_node, new_buf, field_val))) + return err; + } + return ErrorNone; + } + case ContainerLayoutPacked: { + size_t src_field_count = val->type->data.structure.src_field_count; + val->data.x_struct.fields = alloc_const_vals_ptrs(codegen, src_field_count); + size_t gen_field_count = val->type->data.structure.gen_field_count; + size_t gen_i = 0; + size_t src_i = 0; + size_t offset = 0; + bool is_big_endian = codegen->is_big_endian; + uint8_t child_buf_prealloc[16]; + size_t child_buf_len = 16; + uint8_t *child_buf = child_buf_prealloc; + while (gen_i < gen_field_count) { + size_t big_int_byte_count = val->type->data.structure.host_int_bytes[gen_i]; + if (big_int_byte_count > child_buf_len) { + child_buf = heap::c_allocator.allocate_nonzero(big_int_byte_count); + child_buf_len = big_int_byte_count; + } + BigInt big_int; + bigint_read_twos_complement(&big_int, buf + offset, big_int_byte_count * 8, is_big_endian, false); + uint64_t bit_offset = 0; + while (src_i < src_field_count) { + TypeStructField *field = val->type->data.structure.fields[src_i]; + src_assert(field->gen_index != SIZE_MAX, source_node); + if (field->gen_index != gen_i) + break; + ZigValue *field_val = val->data.x_struct.fields[src_i]; + field_val->special = ConstValSpecialStatic; + field_val->type = field->type_entry; + uint32_t packed_bits_size = type_size_bits(codegen, field->type_entry); + + BigInt child_val; + if (is_big_endian) { + BigInt packed_bits_size_bi; + bigint_init_unsigned(&packed_bits_size_bi, big_int_byte_count * 8 - packed_bits_size - bit_offset); + BigInt tmp; + bigint_shr(&tmp, &big_int, &packed_bits_size_bi); + bigint_truncate(&child_val, &tmp, packed_bits_size, false); + } else { + BigInt packed_bits_size_bi; + bigint_init_unsigned(&packed_bits_size_bi, packed_bits_size); + bigint_truncate(&child_val, &big_int, packed_bits_size, false); + BigInt tmp; + bigint_shr(&tmp, &big_int, &packed_bits_size_bi); + big_int = tmp; + } + + bigint_write_twos_complement(&child_val, child_buf, packed_bits_size, is_big_endian); + if ((err = buf_read_value_bytes(ira, codegen, source_node, child_buf, field_val))) { + return err; + } + + bit_offset += packed_bits_size; + src_i += 1; + } + offset += big_int_byte_count; + gen_i += 1; + } + return ErrorNone; + } + } + zig_unreachable(); + case ZigTypeIdOptional: + zig_panic("TODO buf_read_value_bytes maybe type"); + case ZigTypeIdErrorUnion: + zig_panic("TODO buf_read_value_bytes error union"); + case ZigTypeIdErrorSet: + zig_panic("TODO buf_read_value_bytes pure error type"); + case ZigTypeIdFn: + zig_panic("TODO buf_read_value_bytes fn type"); + case ZigTypeIdUnion: + zig_panic("TODO buf_read_value_bytes union type"); + case ZigTypeIdFnFrame: + zig_panic("TODO buf_read_value_bytes async fn frame type"); + case ZigTypeIdAnyFrame: + zig_panic("TODO buf_read_value_bytes anyframe type"); + } + zig_unreachable(); +} + +static IrInstGen *ir_analyze_bit_cast(IrAnalyze *ira, IrInst* source_instr, IrInstGen *value, + ZigType *dest_type) +{ + Error err; + + ZigType *src_type = value->value->type; + ir_assert(type_can_bit_cast(src_type), source_instr); + ir_assert(type_can_bit_cast(dest_type), source_instr); + + if (dest_type->id == ZigTypeIdEnum) { + ErrorMsg *msg = ir_add_error_node(ira, source_instr->source_node, + buf_sprintf("cannot cast a value of type '%s'", buf_ptr(&dest_type->name))); + add_error_note(ira->codegen, msg, source_instr->source_node, + buf_sprintf("use @intToEnum for type coercion")); + return ira->codegen->invalid_inst_gen; + } + + if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusSizeKnown))) + return ira->codegen->invalid_inst_gen; + + if ((err = type_resolve(ira->codegen, src_type, ResolveStatusSizeKnown))) + return ira->codegen->invalid_inst_gen; + + const bool src_is_ptr = handle_is_ptr(ira->codegen, src_type); + const bool dest_is_ptr = handle_is_ptr(ira->codegen, dest_type); + + const uint64_t dest_size_bytes = type_size(ira->codegen, dest_type); + const uint64_t src_size_bytes = type_size(ira->codegen, src_type); + if (dest_size_bytes != src_size_bytes) { + ir_add_error(ira, source_instr, + buf_sprintf("destination type '%s' has size %" ZIG_PRI_u64 " but source type '%s' has size %" ZIG_PRI_u64, + buf_ptr(&dest_type->name), dest_size_bytes, + buf_ptr(&src_type->name), src_size_bytes)); + return ira->codegen->invalid_inst_gen; + } + + const uint64_t dest_size_bits = type_size_bits(ira->codegen, dest_type); + const uint64_t src_size_bits = type_size_bits(ira->codegen, src_type); + if (dest_size_bits != src_size_bits) { + ir_add_error(ira, source_instr, + buf_sprintf("destination type '%s' has %" ZIG_PRI_u64 " bits but source type '%s' has %" ZIG_PRI_u64 " bits", + buf_ptr(&dest_type->name), dest_size_bits, + buf_ptr(&src_type->name), src_size_bits)); + return ira->codegen->invalid_inst_gen; + } + + if (instr_is_comptime(value)) { + ZigValue *val = ir_resolve_const(ira, value, UndefBad); + if (!val) + return ira->codegen->invalid_inst_gen; + + IrInstGen *result = ir_const(ira, source_instr, dest_type); + uint8_t *buf = heap::c_allocator.allocate_nonzero(src_size_bytes); + buf_write_value_bytes(ira->codegen, buf, val); + if ((err = buf_read_value_bytes(ira, ira->codegen, source_instr->source_node, buf, result->value))) + return ira->codegen->invalid_inst_gen; + return result; + } + + if (dest_is_ptr && !src_is_ptr) { + // Spill the scalar into a local memory location and take its address + value = ir_get_ref(ira, source_instr, value, false, false); + } + + return ir_build_bit_cast_gen(ira, source_instr, value, dest_type); +} + +static IrInstGen *ir_analyze_int_to_ptr(IrAnalyze *ira, IrInst* source_instr, IrInstGen *target, + ZigType *ptr_type) +{ + Error err; + + ir_assert(get_src_ptr_type(ptr_type) != nullptr, source_instr); + ir_assert(type_has_bits(ira->codegen, ptr_type), source_instr); + + IrInstGen *casted_int = ir_implicit_cast(ira, target, ira->codegen->builtin_types.entry_usize); + if (type_is_invalid(casted_int->value->type)) + return ira->codegen->invalid_inst_gen; + + if (instr_is_comptime(casted_int)) { + ZigValue *val = ir_resolve_const(ira, casted_int, UndefBad); + if (!val) + return ira->codegen->invalid_inst_gen; + + uint64_t addr = bigint_as_u64(&val->data.x_bigint); + if (!ptr_allows_addr_zero(ptr_type) && addr == 0) { + ir_add_error(ira, source_instr, + buf_sprintf("pointer type '%s' does not allow address zero", buf_ptr(&ptr_type->name))); + return ira->codegen->invalid_inst_gen; + } + + uint32_t align_bytes; + if ((err = resolve_ptr_align(ira, ptr_type, &align_bytes))) + return ira->codegen->invalid_inst_gen; + + if (addr != 0 && addr % align_bytes != 0) { + ir_add_error(ira, source_instr, + buf_sprintf("pointer type '%s' requires aligned address", + buf_ptr(&ptr_type->name))); + return ira->codegen->invalid_inst_gen; + } + + IrInstGen *result = ir_const(ira, source_instr, ptr_type); + if (ptr_type->id == ZigTypeIdOptional && addr == 0) { + result->value->data.x_ptr.special = ConstPtrSpecialNull; + result->value->data.x_ptr.mut = ConstPtrMutComptimeConst; + } else { + result->value->data.x_ptr.special = ConstPtrSpecialHardCodedAddr; + result->value->data.x_ptr.mut = ConstPtrMutRuntimeVar; + result->value->data.x_ptr.data.hard_coded_addr.addr = addr; + } + + return result; + } + + return ir_build_int_to_ptr_gen(ira, source_instr->scope, source_instr->source_node, casted_int, ptr_type); +} + +static IrInstGen *ir_analyze_instruction_int_to_ptr(IrAnalyze *ira, IrInstSrcIntToPtr *instruction) { + Error err; + IrInstGen *dest_type_value = instruction->dest_type->child; + ZigType *dest_type = ir_resolve_type(ira, dest_type_value); + if (type_is_invalid(dest_type)) + return ira->codegen->invalid_inst_gen; + + // We explicitly check for the size, so we can use get_src_ptr_type + if (get_src_ptr_type(dest_type) == nullptr) { + ir_add_error(ira, &dest_type_value->base, buf_sprintf("expected pointer, found '%s'", buf_ptr(&dest_type->name))); + return ira->codegen->invalid_inst_gen; + } + + bool has_bits; + if ((err = type_has_bits2(ira->codegen, dest_type, &has_bits))) + return ira->codegen->invalid_inst_gen; + + if (!has_bits) { + ir_add_error(ira, &dest_type_value->base, + buf_sprintf("type '%s' has 0 bits and cannot store information", buf_ptr(&dest_type->name))); + return ira->codegen->invalid_inst_gen; + } + + IrInstGen *target = instruction->target->child; + if (type_is_invalid(target->value->type)) + return ira->codegen->invalid_inst_gen; + + return ir_analyze_int_to_ptr(ira, &instruction->base.base, target, dest_type); +} + +static IrInstGen *ir_analyze_instruction_decl_ref(IrAnalyze *ira, IrInstSrcDeclRef *instruction) { + IrInstGen *ref_instruction = ir_analyze_decl_ref(ira, &instruction->base.base, instruction->tld); + if (type_is_invalid(ref_instruction->value->type)) { + return ira->codegen->invalid_inst_gen; + } + + if (instruction->lval == LValPtr || instruction->lval == LValAssign) { + return ref_instruction; + } else { + return ir_get_deref(ira, &instruction->base.base, ref_instruction, nullptr); + } +} + +static IrInstGen *ir_analyze_instruction_ptr_to_int(IrAnalyze *ira, IrInstSrcPtrToInt *instruction) { + Error err; + IrInstGen *target = instruction->target->child; + if (type_is_invalid(target->value->type)) + return ira->codegen->invalid_inst_gen; + + ZigType *usize = ira->codegen->builtin_types.entry_usize; + + ZigType *src_ptr_type = get_src_ptr_type(target->value->type); + if (src_ptr_type == nullptr) { + ir_add_error(ira, &target->base, + buf_sprintf("expected pointer, found '%s'", buf_ptr(&target->value->type->name))); + return ira->codegen->invalid_inst_gen; + } + + bool has_bits; + if ((err = type_has_bits2(ira->codegen, src_ptr_type, &has_bits))) + return ira->codegen->invalid_inst_gen; + + if (!has_bits) { + ir_add_error(ira, &target->base, + buf_sprintf("pointer to size 0 type has no address")); + return ira->codegen->invalid_inst_gen; + } + + if (instr_is_comptime(target)) { + ZigValue *val = ir_resolve_const(ira, target, UndefBad); + if (!val) + return ira->codegen->invalid_inst_gen; + + // Since we've already run this type trough get_src_ptr_type it is + // safe to access the x_ptr fields + if (val->data.x_ptr.special == ConstPtrSpecialHardCodedAddr) { + IrInstGen *result = ir_const(ira, &instruction->base.base, usize); + bigint_init_unsigned(&result->value->data.x_bigint, val->data.x_ptr.data.hard_coded_addr.addr); + result->value->type = usize; + return result; + } else if (val->data.x_ptr.special == ConstPtrSpecialNull) { + IrInstGen *result = ir_const(ira, &instruction->base.base, usize); + bigint_init_unsigned(&result->value->data.x_bigint, 0); + result->value->type = usize; + return result; + } + } + + return ir_build_ptr_to_int_gen(ira, &instruction->base.base, target); +} + +static IrInstGen *ir_analyze_instruction_ptr_type(IrAnalyze *ira, IrInstSrcPtrType *instruction) { + IrInstGen *result = ir_const(ira, &instruction->base.base, ira->codegen->builtin_types.entry_type); + result->value->special = ConstValSpecialLazy; + + LazyValuePtrType *lazy_ptr_type = heap::c_allocator.create(); + lazy_ptr_type->ira = ira; ira_ref(ira); + result->value->data.x_lazy = &lazy_ptr_type->base; + lazy_ptr_type->base.id = LazyValueIdPtrType; + + if (instruction->sentinel != nullptr) { + if (instruction->ptr_len != PtrLenUnknown) { + ir_add_error(ira, &instruction->base.base, + buf_sprintf("sentinels are only allowed on unknown-length pointers")); + return ira->codegen->invalid_inst_gen; + } + + lazy_ptr_type->sentinel = instruction->sentinel->child; + if (ir_resolve_const(ira, lazy_ptr_type->sentinel, LazyOk) == nullptr) + return ira->codegen->invalid_inst_gen; + } + + lazy_ptr_type->elem_type = instruction->child_type->child; + if (ir_resolve_type_lazy(ira, lazy_ptr_type->elem_type) == nullptr) + return ira->codegen->invalid_inst_gen; + + if (instruction->align_value != nullptr) { + lazy_ptr_type->align_inst = instruction->align_value->child; + if (ir_resolve_const(ira, lazy_ptr_type->align_inst, LazyOk) == nullptr) + return ira->codegen->invalid_inst_gen; + } + + lazy_ptr_type->ptr_len = instruction->ptr_len; + lazy_ptr_type->is_const = instruction->is_const; + lazy_ptr_type->is_volatile = instruction->is_volatile; + lazy_ptr_type->is_allowzero = instruction->is_allow_zero; + lazy_ptr_type->bit_offset_in_host = instruction->bit_offset_start; + lazy_ptr_type->host_int_bytes = instruction->host_int_bytes; + + return result; +} + +static IrInstGen *ir_analyze_instruction_align_cast(IrAnalyze *ira, IrInstSrcAlignCast *instruction) { + IrInstGen *target = instruction->target->child; + if (type_is_invalid(target->value->type)) + return ira->codegen->invalid_inst_gen; + + ZigType *elem_type = nullptr; + if (is_slice(target->value->type)) { + ZigType *slice_ptr_type = target->value->type->data.structure.fields[slice_ptr_index]->type_entry; + elem_type = slice_ptr_type->data.pointer.child_type; + } else if (target->value->type->id == ZigTypeIdPointer) { + elem_type = target->value->type->data.pointer.child_type; + } + + uint32_t align_bytes; + IrInstGen *align_bytes_inst = instruction->align_bytes->child; + if (!ir_resolve_align(ira, align_bytes_inst, elem_type, &align_bytes)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *result = ir_align_cast(ira, target, align_bytes, true); + if (type_is_invalid(result->value->type)) + return ira->codegen->invalid_inst_gen; + + return result; +} + +static IrInstGen *ir_analyze_instruction_set_align_stack(IrAnalyze *ira, IrInstSrcSetAlignStack *instruction) { + uint32_t align_bytes; + IrInstGen *align_bytes_inst = instruction->align_bytes->child; + if (!ir_resolve_align(ira, align_bytes_inst, nullptr, &align_bytes)) + return ira->codegen->invalid_inst_gen; + + if (align_bytes > 256) { + ir_add_error(ira, &instruction->base.base, buf_sprintf("attempt to @setAlignStack(%" PRIu32 "); maximum is 256", align_bytes)); + return ira->codegen->invalid_inst_gen; + } + + ZigFn *fn_entry = ira->new_irb.exec->fn_entry; + if (fn_entry == nullptr) { + ir_add_error(ira, &instruction->base.base, buf_sprintf("@setAlignStack outside function")); + return ira->codegen->invalid_inst_gen; + } + if (fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionNaked) { + ir_add_error(ira, &instruction->base.base, buf_sprintf("@setAlignStack in naked function")); + return ira->codegen->invalid_inst_gen; + } + + if (fn_entry->fn_inline == FnInlineAlways) { + ir_add_error(ira, &instruction->base.base, buf_sprintf("@setAlignStack in inline function")); + return ira->codegen->invalid_inst_gen; + } + + if (fn_entry->set_alignstack_node != nullptr) { + ErrorMsg *msg = ir_add_error(ira, &instruction->base.base, + buf_sprintf("alignstack set twice")); + add_error_note(ira->codegen, msg, fn_entry->set_alignstack_node, buf_sprintf("first set here")); + return ira->codegen->invalid_inst_gen; + } + + fn_entry->set_alignstack_node = instruction->base.base.source_node; + fn_entry->alignstack_value = align_bytes; + + return ir_const_void(ira, &instruction->base.base); +} + +static IrInstGen *ir_analyze_instruction_arg_type(IrAnalyze *ira, IrInstSrcArgType *instruction) { + IrInstGen *fn_type_inst = instruction->fn_type->child; + ZigType *fn_type = ir_resolve_type(ira, fn_type_inst); + if (type_is_invalid(fn_type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *arg_index_inst = instruction->arg_index->child; + uint64_t arg_index; + if (!ir_resolve_usize(ira, arg_index_inst, &arg_index)) + return ira->codegen->invalid_inst_gen; + + if (fn_type->id == ZigTypeIdBoundFn) { + fn_type = fn_type->data.bound_fn.fn_type; + arg_index += 1; + } + if (fn_type->id != ZigTypeIdFn) { + ir_add_error(ira, &fn_type_inst->base, buf_sprintf("expected function, found '%s'", buf_ptr(&fn_type->name))); + return ira->codegen->invalid_inst_gen; + } + + FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id; + if (arg_index >= fn_type_id->param_count) { + if (instruction->allow_var) { + // TODO remove this with var args + return ir_const_type(ira, &instruction->base.base, ira->codegen->builtin_types.entry_anytype); + } + ir_add_error(ira, &arg_index_inst->base, + buf_sprintf("arg index %" ZIG_PRI_u64 " out of bounds; '%s' has %" ZIG_PRI_usize " argument(s)", + arg_index, buf_ptr(&fn_type->name), fn_type_id->param_count)); + return ira->codegen->invalid_inst_gen; + } + + ZigType *result_type = fn_type_id->param_info[arg_index].type; + if (result_type == nullptr) { + // Args are only unresolved if our function is generic. + ir_assert(fn_type->data.fn.is_generic, &instruction->base.base); + + if (instruction->allow_var) { + return ir_const_type(ira, &instruction->base.base, ira->codegen->builtin_types.entry_anytype); + } else { + ir_add_error(ira, &arg_index_inst->base, + buf_sprintf("@ArgType could not resolve the type of arg %" ZIG_PRI_u64 " because '%s' is generic", + arg_index, buf_ptr(&fn_type->name))); + return ira->codegen->invalid_inst_gen; + } + } + return ir_const_type(ira, &instruction->base.base, result_type); +} + +static IrInstGen *ir_analyze_instruction_tag_type(IrAnalyze *ira, IrInstSrcTagType *instruction) { + Error err; + IrInstGen *target_inst = instruction->target->child; + ZigType *enum_type = ir_resolve_type(ira, target_inst); + if (type_is_invalid(enum_type)) + return ira->codegen->invalid_inst_gen; + + if (enum_type->id == ZigTypeIdEnum) { + if ((err = type_resolve(ira->codegen, enum_type, ResolveStatusSizeKnown))) + return ira->codegen->invalid_inst_gen; + + return ir_const_type(ira, &instruction->base.base, enum_type->data.enumeration.tag_int_type); + } else if (enum_type->id == ZigTypeIdUnion) { + ZigType *tag_type = ir_resolve_union_tag_type(ira, instruction->target->base.source_node, enum_type); + if (type_is_invalid(tag_type)) + return ira->codegen->invalid_inst_gen; + return ir_const_type(ira, &instruction->base.base, tag_type); + } else { + ir_add_error(ira, &target_inst->base, buf_sprintf("expected enum or union, found '%s'", + buf_ptr(&enum_type->name))); + return ira->codegen->invalid_inst_gen; + } +} + +static ZigType *ir_resolve_atomic_operand_type(IrAnalyze *ira, IrInstGen *op) { + ZigType *operand_type = ir_resolve_type(ira, op); + if (type_is_invalid(operand_type)) + return ira->codegen->builtin_types.entry_invalid; + + if (operand_type->id == ZigTypeIdInt || operand_type->id == ZigTypeIdEnum) { + ZigType *int_type; + if (operand_type->id == ZigTypeIdEnum) { + int_type = operand_type->data.enumeration.tag_int_type; + } else { + int_type = operand_type; + } + auto bit_count = int_type->data.integral.bit_count; + uint32_t max_atomic_bits = target_arch_largest_atomic_bits(ira->codegen->zig_target->arch); + + if (bit_count > max_atomic_bits) { + ir_add_error(ira, &op->base, + buf_sprintf("expected %" PRIu32 "-bit integer type or smaller, found %" PRIu32 "-bit integer type", + max_atomic_bits, bit_count)); + return ira->codegen->builtin_types.entry_invalid; + } + } else if (operand_type->id == ZigTypeIdFloat) { + uint32_t max_atomic_bits = target_arch_largest_atomic_bits(ira->codegen->zig_target->arch); + if (operand_type->data.floating.bit_count > max_atomic_bits) { + ir_add_error(ira, &op->base, + buf_sprintf("expected %" PRIu32 "-bit float or smaller, found %" PRIu32 "-bit float", + max_atomic_bits, (uint32_t) operand_type->data.floating.bit_count)); + return ira->codegen->builtin_types.entry_invalid; + } + } else if (operand_type->id == ZigTypeIdBool) { + // will be treated as u8 + } else { + Error err; + ZigType *operand_ptr_type; + if ((err = get_codegen_ptr_type(ira->codegen, operand_type, &operand_ptr_type))) + return ira->codegen->builtin_types.entry_invalid; + if (operand_ptr_type == nullptr) { + ir_add_error(ira, &op->base, + buf_sprintf("expected bool, integer, float, enum or pointer type, found '%s'", + buf_ptr(&operand_type->name))); + return ira->codegen->builtin_types.entry_invalid; + } + } + + return operand_type; +} + +static IrInstGen *ir_analyze_instruction_atomic_rmw(IrAnalyze *ira, IrInstSrcAtomicRmw *instruction) { + ZigType *operand_type = ir_resolve_atomic_operand_type(ira, instruction->operand_type->child); + if (type_is_invalid(operand_type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *ptr_inst = instruction->ptr->child; + if (type_is_invalid(ptr_inst->value->type)) + return ira->codegen->invalid_inst_gen; + + // TODO let this be volatile + ZigType *ptr_type = get_pointer_to_type(ira->codegen, operand_type, false); + IrInstGen *casted_ptr = ir_implicit_cast(ira, ptr_inst, ptr_type); + if (type_is_invalid(casted_ptr->value->type)) + return ira->codegen->invalid_inst_gen; + + AtomicRmwOp op; + if (!ir_resolve_atomic_rmw_op(ira, instruction->op->child, &op)) { + return ira->codegen->invalid_inst_gen; + } + + if (operand_type->id == ZigTypeIdEnum && op != AtomicRmwOp_xchg) { + ir_add_error(ira, &instruction->op->base, + buf_sprintf("@atomicRmw with enum only allowed with .Xchg")); + return ira->codegen->invalid_inst_gen; + } else if (operand_type->id == ZigTypeIdBool && op != AtomicRmwOp_xchg) { + ir_add_error(ira, &instruction->op->base, + buf_sprintf("@atomicRmw with bool only allowed with .Xchg")); + return ira->codegen->invalid_inst_gen; + } else if (operand_type->id == ZigTypeIdFloat && op > AtomicRmwOp_sub) { + ir_add_error(ira, &instruction->op->base, + buf_sprintf("@atomicRmw with float only allowed with .Xchg, .Add and .Sub")); + return ira->codegen->invalid_inst_gen; + } + + IrInstGen *operand = instruction->operand->child; + if (type_is_invalid(operand->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *casted_operand = ir_implicit_cast(ira, operand, operand_type); + if (type_is_invalid(casted_operand->value->type)) + return ira->codegen->invalid_inst_gen; + + AtomicOrder ordering; + if (!ir_resolve_atomic_order(ira, instruction->ordering->child, &ordering)) + return ira->codegen->invalid_inst_gen; + if (ordering == AtomicOrderUnordered) { + ir_add_error(ira, &instruction->ordering->base, + buf_sprintf("@atomicRmw atomic ordering must not be Unordered")); + return ira->codegen->invalid_inst_gen; + } + + // special case zero bit types + switch (type_has_one_possible_value(ira->codegen, operand_type)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_inst_gen; + case OnePossibleValueYes: + return ir_const_move(ira, &instruction->base.base, get_the_one_possible_value(ira->codegen, operand_type)); + case OnePossibleValueNo: + break; + } + + IrInst *source_inst = &instruction->base.base; + if (instr_is_comptime(casted_operand) && instr_is_comptime(casted_ptr) && casted_ptr->value->data.x_ptr.mut == ConstPtrMutComptimeVar) { + ZigValue *ptr_val = ir_resolve_const(ira, casted_ptr, UndefBad); + if (ptr_val == nullptr) + return ira->codegen->invalid_inst_gen; + + ZigValue *op1_val = const_ptr_pointee(ira, ira->codegen, ptr_val, instruction->base.base.source_node); + if (op1_val == nullptr) + return ira->codegen->invalid_inst_gen; + + ZigValue *op2_val = ir_resolve_const(ira, casted_operand, UndefBad); + if (op2_val == nullptr) + return ira->codegen->invalid_inst_gen; + + IrInstGen *result = ir_const(ira, source_inst, operand_type); + copy_const_val(ira->codegen, result->value, op1_val); + if (op == AtomicRmwOp_xchg) { + copy_const_val(ira->codegen, op1_val, op2_val); + return result; + } + + if (operand_type->id == ZigTypeIdPointer || operand_type->id == ZigTypeIdOptional) { + ir_add_error(ira, &instruction->ordering->base, + buf_sprintf("TODO comptime @atomicRmw with pointers other than .Xchg")); + return ira->codegen->invalid_inst_gen; + } + + ErrorMsg *msg; + if (op == AtomicRmwOp_min || op == AtomicRmwOp_max) { + IrBinOp bin_op; + if (op == AtomicRmwOp_min) + // store op2 if op2 < op1 + bin_op = IrBinOpCmpGreaterThan; + else + // store op2 if op2 > op1 + bin_op = IrBinOpCmpLessThan; + + IrInstGen *dummy_value = ir_const(ira, source_inst, operand_type); + msg = ir_eval_bin_op_cmp_scalar(ira, source_inst, op1_val, bin_op, op2_val, dummy_value->value); + if (msg != nullptr) { + return ira->codegen->invalid_inst_gen; + } + if (dummy_value->value->data.x_bool) + copy_const_val(ira->codegen, op1_val, op2_val); + } else { + IrBinOp bin_op; + switch (op) { + case AtomicRmwOp_xchg: + case AtomicRmwOp_max: + case AtomicRmwOp_min: + zig_unreachable(); + case AtomicRmwOp_add: + if (operand_type->id == ZigTypeIdFloat) + bin_op = IrBinOpAdd; + else + bin_op = IrBinOpAddWrap; + break; + case AtomicRmwOp_sub: + if (operand_type->id == ZigTypeIdFloat) + bin_op = IrBinOpSub; + else + bin_op = IrBinOpSubWrap; + break; + case AtomicRmwOp_and: + case AtomicRmwOp_nand: + bin_op = IrBinOpBinAnd; + break; + case AtomicRmwOp_or: + bin_op = IrBinOpBinOr; + break; + case AtomicRmwOp_xor: + bin_op = IrBinOpBinXor; + break; + } + msg = ir_eval_math_op_scalar(ira, source_inst, operand_type, op1_val, bin_op, op2_val, op1_val); + if (msg != nullptr) { + return ira->codegen->invalid_inst_gen; + } + if (op == AtomicRmwOp_nand) { + bigint_not(&op1_val->data.x_bigint, &op1_val->data.x_bigint, + operand_type->data.integral.bit_count, operand_type->data.integral.is_signed); + } + } + return result; + } + + return ir_build_atomic_rmw_gen(ira, source_inst, casted_ptr, casted_operand, op, + ordering, operand_type); +} + +static IrInstGen *ir_analyze_instruction_atomic_load(IrAnalyze *ira, IrInstSrcAtomicLoad *instruction) { + ZigType *operand_type = ir_resolve_atomic_operand_type(ira, instruction->operand_type->child); + if (type_is_invalid(operand_type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *ptr_inst = instruction->ptr->child; + if (type_is_invalid(ptr_inst->value->type)) + return ira->codegen->invalid_inst_gen; + + ZigType *ptr_type = get_pointer_to_type(ira->codegen, operand_type, true); + IrInstGen *casted_ptr = ir_implicit_cast(ira, ptr_inst, ptr_type); + if (type_is_invalid(casted_ptr->value->type)) + return ira->codegen->invalid_inst_gen; + + AtomicOrder ordering; + if (!ir_resolve_atomic_order(ira, instruction->ordering->child, &ordering)) + return ira->codegen->invalid_inst_gen; + + if (ordering == AtomicOrderRelease || ordering == AtomicOrderAcqRel) { + ir_assert(instruction->ordering != nullptr, &instruction->base.base); + ir_add_error(ira, &instruction->ordering->base, + buf_sprintf("@atomicLoad atomic ordering must not be Release or AcqRel")); + return ira->codegen->invalid_inst_gen; + } + + if (instr_is_comptime(casted_ptr)) { + IrInstGen *result = ir_get_deref(ira, &instruction->base.base, casted_ptr, nullptr); + ir_assert(result->value->type != nullptr, &instruction->base.base); + return result; + } + + return ir_build_atomic_load_gen(ira, &instruction->base.base, casted_ptr, ordering, operand_type); +} + +static IrInstGen *ir_analyze_instruction_atomic_store(IrAnalyze *ira, IrInstSrcAtomicStore *instruction) { + ZigType *operand_type = ir_resolve_atomic_operand_type(ira, instruction->operand_type->child); + if (type_is_invalid(operand_type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *ptr_inst = instruction->ptr->child; + if (type_is_invalid(ptr_inst->value->type)) + return ira->codegen->invalid_inst_gen; + + ZigType *ptr_type = get_pointer_to_type(ira->codegen, operand_type, false); + IrInstGen *casted_ptr = ir_implicit_cast(ira, ptr_inst, ptr_type); + if (type_is_invalid(casted_ptr->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *value = instruction->value->child; + if (type_is_invalid(value->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *casted_value = ir_implicit_cast(ira, value, operand_type); + if (type_is_invalid(casted_value->value->type)) + return ira->codegen->invalid_inst_gen; + + + AtomicOrder ordering; + if (!ir_resolve_atomic_order(ira, instruction->ordering->child, &ordering)) + return ira->codegen->invalid_inst_gen; + + if (ordering == AtomicOrderAcquire || ordering == AtomicOrderAcqRel) { + ir_assert(instruction->ordering != nullptr, &instruction->base.base); + ir_add_error(ira, &instruction->ordering->base, + buf_sprintf("@atomicStore atomic ordering must not be Acquire or AcqRel")); + return ira->codegen->invalid_inst_gen; + } + + // special case zero bit types + switch (type_has_one_possible_value(ira->codegen, operand_type)) { + case OnePossibleValueInvalid: + return ira->codegen->invalid_inst_gen; + case OnePossibleValueYes: + return ir_const_void(ira, &instruction->base.base); + case OnePossibleValueNo: + break; + } + + if (instr_is_comptime(casted_value) && instr_is_comptime(casted_ptr)) { + IrInstGen *result = ir_analyze_store_ptr(ira, &instruction->base.base, casted_ptr, value, false); + result->value->type = ira->codegen->builtin_types.entry_void; + return result; + } + + return ir_build_atomic_store_gen(ira, &instruction->base.base, casted_ptr, casted_value, ordering); +} + +static IrInstGen *ir_analyze_instruction_save_err_ret_addr(IrAnalyze *ira, IrInstSrcSaveErrRetAddr *instruction) { + return ir_build_save_err_ret_addr_gen(ira, &instruction->base.base); +} + +static ErrorMsg *ir_eval_float_op(IrAnalyze *ira, IrInst* source_instr, BuiltinFnId fop, ZigType *float_type, + ZigValue *op, ZigValue *out_val) +{ + assert(ira && source_instr && float_type && out_val && op); + assert(float_type->id == ZigTypeIdFloat || + float_type->id == ZigTypeIdComptimeFloat); + + unsigned bits; + + switch (float_type->id) { + case ZigTypeIdComptimeFloat: + bits = 128; + break; + case ZigTypeIdFloat: + bits = float_type->data.floating.bit_count; + break; + default: + zig_unreachable(); + } + + switch (bits) { + case 16: { + switch (fop) { + case BuiltinFnIdSqrt: + out_val->data.x_f16 = f16_sqrt(op->data.x_f16); + break; + case BuiltinFnIdSin: + out_val->data.x_f16 = zig_double_to_f16(sin(zig_f16_to_double(op->data.x_f16))); + break; + case BuiltinFnIdCos: + out_val->data.x_f16 = zig_double_to_f16(cos(zig_f16_to_double(op->data.x_f16))); + break; + case BuiltinFnIdExp: + out_val->data.x_f16 = zig_double_to_f16(exp(zig_f16_to_double(op->data.x_f16))); + break; + case BuiltinFnIdExp2: + out_val->data.x_f16 = zig_double_to_f16(exp2(zig_f16_to_double(op->data.x_f16))); + break; + case BuiltinFnIdLog: + out_val->data.x_f16 = zig_double_to_f16(log(zig_f16_to_double(op->data.x_f16))); + break; + case BuiltinFnIdLog10: + out_val->data.x_f16 = zig_double_to_f16(log10(zig_f16_to_double(op->data.x_f16))); + break; + case BuiltinFnIdLog2: + out_val->data.x_f16 = zig_double_to_f16(log2(zig_f16_to_double(op->data.x_f16))); + break; + case BuiltinFnIdFabs: + out_val->data.x_f16 = zig_double_to_f16(fabs(zig_f16_to_double(op->data.x_f16))); + break; + case BuiltinFnIdFloor: + out_val->data.x_f16 = zig_double_to_f16(floor(zig_f16_to_double(op->data.x_f16))); + break; + case BuiltinFnIdCeil: + out_val->data.x_f16 = zig_double_to_f16(ceil(zig_f16_to_double(op->data.x_f16))); + break; + case BuiltinFnIdTrunc: + out_val->data.x_f16 = zig_double_to_f16(trunc(zig_f16_to_double(op->data.x_f16))); + break; + case BuiltinFnIdNearbyInt: + out_val->data.x_f16 = zig_double_to_f16(nearbyint(zig_f16_to_double(op->data.x_f16))); + break; + case BuiltinFnIdRound: + out_val->data.x_f16 = zig_double_to_f16(round(zig_f16_to_double(op->data.x_f16))); + break; + default: + zig_unreachable(); + }; + break; + } + case 32: { + switch (fop) { + case BuiltinFnIdSqrt: + out_val->data.x_f32 = sqrtf(op->data.x_f32); + break; + case BuiltinFnIdSin: + out_val->data.x_f32 = sinf(op->data.x_f32); + break; + case BuiltinFnIdCos: + out_val->data.x_f32 = cosf(op->data.x_f32); + break; + case BuiltinFnIdExp: + out_val->data.x_f32 = expf(op->data.x_f32); + break; + case BuiltinFnIdExp2: + out_val->data.x_f32 = exp2f(op->data.x_f32); + break; + case BuiltinFnIdLog: + out_val->data.x_f32 = logf(op->data.x_f32); + break; + case BuiltinFnIdLog10: + out_val->data.x_f32 = log10f(op->data.x_f32); + break; + case BuiltinFnIdLog2: + out_val->data.x_f32 = log2f(op->data.x_f32); + break; + case BuiltinFnIdFabs: + out_val->data.x_f32 = fabsf(op->data.x_f32); + break; + case BuiltinFnIdFloor: + out_val->data.x_f32 = floorf(op->data.x_f32); + break; + case BuiltinFnIdCeil: + out_val->data.x_f32 = ceilf(op->data.x_f32); + break; + case BuiltinFnIdTrunc: + out_val->data.x_f32 = truncf(op->data.x_f32); + break; + case BuiltinFnIdNearbyInt: + out_val->data.x_f32 = nearbyintf(op->data.x_f32); + break; + case BuiltinFnIdRound: + out_val->data.x_f32 = roundf(op->data.x_f32); + break; + default: + zig_unreachable(); + }; + break; + } + case 64: { + switch (fop) { + case BuiltinFnIdSqrt: + out_val->data.x_f64 = sqrt(op->data.x_f64); + break; + case BuiltinFnIdSin: + out_val->data.x_f64 = sin(op->data.x_f64); + break; + case BuiltinFnIdCos: + out_val->data.x_f64 = cos(op->data.x_f64); + break; + case BuiltinFnIdExp: + out_val->data.x_f64 = exp(op->data.x_f64); + break; + case BuiltinFnIdExp2: + out_val->data.x_f64 = exp2(op->data.x_f64); + break; + case BuiltinFnIdLog: + out_val->data.x_f64 = log(op->data.x_f64); + break; + case BuiltinFnIdLog10: + out_val->data.x_f64 = log10(op->data.x_f64); + break; + case BuiltinFnIdLog2: + out_val->data.x_f64 = log2(op->data.x_f64); + break; + case BuiltinFnIdFabs: + out_val->data.x_f64 = fabs(op->data.x_f64); + break; + case BuiltinFnIdFloor: + out_val->data.x_f64 = floor(op->data.x_f64); + break; + case BuiltinFnIdCeil: + out_val->data.x_f64 = ceil(op->data.x_f64); + break; + case BuiltinFnIdTrunc: + out_val->data.x_f64 = trunc(op->data.x_f64); + break; + case BuiltinFnIdNearbyInt: + out_val->data.x_f64 = nearbyint(op->data.x_f64); + break; + case BuiltinFnIdRound: + out_val->data.x_f64 = round(op->data.x_f64); + break; + default: + zig_unreachable(); + } + break; + } + case 80: + return ir_add_error(ira, source_instr, + buf_sprintf("compiler bug: TODO: implement '%s' for type '%s'. See https://github.com/ziglang/zig/issues/4026", + float_op_to_name(fop), buf_ptr(&float_type->name))); + case 128: { + float128_t *out, *in; + if (float_type->id == ZigTypeIdComptimeFloat) { + out = &out_val->data.x_bigfloat.value; + in = &op->data.x_bigfloat.value; + } else { + out = &out_val->data.x_f128; + in = &op->data.x_f128; + } + switch (fop) { + case BuiltinFnIdSqrt: + f128M_sqrt(in, out); + break; + case BuiltinFnIdFabs: + f128M_abs(in, out); + break; + case BuiltinFnIdFloor: + f128M_roundToInt(in, softfloat_round_min, false, out); + break; + case BuiltinFnIdCeil: + f128M_roundToInt(in, softfloat_round_max, false, out); + break; + case BuiltinFnIdTrunc: + f128M_trunc(in, out); + break; + case BuiltinFnIdRound: + f128M_roundToInt(in, softfloat_round_near_maxMag, false, out); + break; + case BuiltinFnIdNearbyInt: + case BuiltinFnIdSin: + case BuiltinFnIdCos: + case BuiltinFnIdExp: + case BuiltinFnIdExp2: + case BuiltinFnIdLog: + case BuiltinFnIdLog10: + case BuiltinFnIdLog2: + return ir_add_error(ira, source_instr, + buf_sprintf("compiler bug: TODO: implement '%s' for type '%s'. See https://github.com/ziglang/zig/issues/4026", + float_op_to_name(fop), buf_ptr(&float_type->name))); + default: + zig_unreachable(); + } + break; + } + default: + zig_unreachable(); + } + out_val->special = ConstValSpecialStatic; + return nullptr; +} + +static IrInstGen *ir_analyze_instruction_float_op(IrAnalyze *ira, IrInstSrcFloatOp *instruction) { + IrInstGen *operand = instruction->operand->child; + ZigType *operand_type = operand->value->type; + if (type_is_invalid(operand_type)) + return ira->codegen->invalid_inst_gen; + + // This instruction accepts floats and vectors of floats. + ZigType *scalar_type = (operand_type->id == ZigTypeIdVector) ? + operand_type->data.vector.elem_type : operand_type; + + if (scalar_type->id != ZigTypeIdFloat && scalar_type->id != ZigTypeIdComptimeFloat) { + ir_add_error(ira, &operand->base, + buf_sprintf("expected float type, found '%s'", buf_ptr(&scalar_type->name))); + return ira->codegen->invalid_inst_gen; + } + + if (instr_is_comptime(operand)) { + ZigValue *operand_val = ir_resolve_const(ira, operand, UndefOk); + if (operand_val == nullptr) + return ira->codegen->invalid_inst_gen; + if (operand_val->special == ConstValSpecialUndef) + return ir_const_undef(ira, &instruction->base.base, operand_type); + + IrInstGen *result = ir_const(ira, &instruction->base.base, operand_type); + ZigValue *out_val = result->value; + + if (operand_type->id == ZigTypeIdVector) { + expand_undef_array(ira->codegen, operand_val); + out_val->special = ConstValSpecialUndef; + expand_undef_array(ira->codegen, out_val); + size_t len = operand_type->data.vector.len; + for (size_t i = 0; i < len; i += 1) { + ZigValue *elem_operand = &operand_val->data.x_array.data.s_none.elements[i]; + ZigValue *float_out_val = &out_val->data.x_array.data.s_none.elements[i]; + ir_assert(elem_operand->type == scalar_type, &instruction->base.base); + ir_assert(float_out_val->type == scalar_type, &instruction->base.base); + ErrorMsg *msg = ir_eval_float_op(ira, &instruction->base.base, instruction->fn_id, scalar_type, + elem_operand, float_out_val); + if (msg != nullptr) { + add_error_note(ira->codegen, msg, instruction->base.base.source_node, + buf_sprintf("when computing vector element at index %" ZIG_PRI_usize, i)); + return ira->codegen->invalid_inst_gen; + } + float_out_val->type = scalar_type; + } + out_val->type = operand_type; + out_val->special = ConstValSpecialStatic; + } else { + if (ir_eval_float_op(ira, &instruction->base.base, instruction->fn_id, scalar_type, + operand_val, out_val) != nullptr) + { + return ira->codegen->invalid_inst_gen; + } + } + return result; + } + + ir_assert(scalar_type->id == ZigTypeIdFloat, &instruction->base.base); + + return ir_build_float_op_gen(ira, &instruction->base.base, operand, instruction->fn_id, operand_type); +} + +static IrInstGen *ir_analyze_instruction_bswap(IrAnalyze *ira, IrInstSrcBswap *instruction) { + Error err; + + ZigType *int_type = ir_resolve_int_type(ira, instruction->type->child); + if (type_is_invalid(int_type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *uncasted_op = instruction->op->child; + if (type_is_invalid(uncasted_op->value->type)) + return ira->codegen->invalid_inst_gen; + + uint32_t vector_len = UINT32_MAX; // means not a vector + if (uncasted_op->value->type->id == ZigTypeIdArray) { + bool can_be_vec_elem; + if ((err = is_valid_vector_elem_type(ira->codegen, uncasted_op->value->type->data.array.child_type, + &can_be_vec_elem))) + { + return ira->codegen->invalid_inst_gen; + } + if (can_be_vec_elem) { + vector_len = uncasted_op->value->type->data.array.len; + } + } else if (uncasted_op->value->type->id == ZigTypeIdVector) { + vector_len = uncasted_op->value->type->data.vector.len; + } + + bool is_vector = (vector_len != UINT32_MAX); + ZigType *op_type = is_vector ? get_vector_type(ira->codegen, vector_len, int_type) : int_type; + + IrInstGen *op = ir_implicit_cast(ira, uncasted_op, op_type); + if (type_is_invalid(op->value->type)) + return ira->codegen->invalid_inst_gen; + + if (int_type->data.integral.bit_count == 8 || int_type->data.integral.bit_count == 0) + return op; + + if (int_type->data.integral.bit_count % 8 != 0) { + ir_add_error(ira, &instruction->op->base, + buf_sprintf("@byteSwap integer type '%s' has %" PRIu32 " bits which is not evenly divisible by 8", + buf_ptr(&int_type->name), int_type->data.integral.bit_count)); + return ira->codegen->invalid_inst_gen; + } + + if (instr_is_comptime(op)) { + ZigValue *val = ir_resolve_const(ira, op, UndefOk); + if (val == nullptr) + return ira->codegen->invalid_inst_gen; + if (val->special == ConstValSpecialUndef) + return ir_const_undef(ira, &instruction->base.base, op_type); + + IrInstGen *result = ir_const(ira, &instruction->base.base, op_type); + const size_t buf_size = int_type->data.integral.bit_count / 8; + uint8_t *buf = heap::c_allocator.allocate_nonzero(buf_size); + if (is_vector) { + expand_undef_array(ira->codegen, val); + result->value->data.x_array.data.s_none.elements = ira->codegen->pass1_arena->allocate(op_type->data.vector.len); + for (unsigned i = 0; i < op_type->data.vector.len; i += 1) { + ZigValue *op_elem_val = &val->data.x_array.data.s_none.elements[i]; + if ((err = ir_resolve_const_val(ira->codegen, ira->new_irb.exec, instruction->base.base.source_node, + op_elem_val, UndefOk))) + { + return ira->codegen->invalid_inst_gen; + } + ZigValue *result_elem_val = &result->value->data.x_array.data.s_none.elements[i]; + result_elem_val->type = int_type; + result_elem_val->special = op_elem_val->special; + if (op_elem_val->special == ConstValSpecialUndef) + continue; + + bigint_write_twos_complement(&op_elem_val->data.x_bigint, buf, int_type->data.integral.bit_count, true); + bigint_read_twos_complement(&result->value->data.x_array.data.s_none.elements[i].data.x_bigint, + buf, int_type->data.integral.bit_count, false, + int_type->data.integral.is_signed); + } + } else { + bigint_write_twos_complement(&val->data.x_bigint, buf, int_type->data.integral.bit_count, true); + bigint_read_twos_complement(&result->value->data.x_bigint, buf, int_type->data.integral.bit_count, false, + int_type->data.integral.is_signed); + } + heap::c_allocator.deallocate(buf, buf_size); + return result; + } + + return ir_build_bswap_gen(ira, &instruction->base.base, op_type, op); +} + +static IrInstGen *ir_analyze_instruction_bit_reverse(IrAnalyze *ira, IrInstSrcBitReverse *instruction) { + ZigType *int_type = ir_resolve_int_type(ira, instruction->type->child); + if (type_is_invalid(int_type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *op = ir_implicit_cast(ira, instruction->op->child, int_type); + if (type_is_invalid(op->value->type)) + return ira->codegen->invalid_inst_gen; + + if (int_type->data.integral.bit_count == 0) { + IrInstGen *result = ir_const(ira, &instruction->base.base, int_type); + bigint_init_unsigned(&result->value->data.x_bigint, 0); + return result; + } + + if (instr_is_comptime(op)) { + ZigValue *val = ir_resolve_const(ira, op, UndefOk); + if (val == nullptr) + return ira->codegen->invalid_inst_gen; + if (val->special == ConstValSpecialUndef) + return ir_const_undef(ira, &instruction->base.base, int_type); + + IrInstGen *result = ir_const(ira, &instruction->base.base, int_type); + size_t num_bits = int_type->data.integral.bit_count; + size_t buf_size = (num_bits + 7) / 8; + uint8_t *comptime_buf = heap::c_allocator.allocate_nonzero(buf_size); + uint8_t *result_buf = heap::c_allocator.allocate_nonzero(buf_size); + memset(comptime_buf,0,buf_size); + memset(result_buf,0,buf_size); + + bigint_write_twos_complement(&val->data.x_bigint,comptime_buf,num_bits,ira->codegen->is_big_endian); + + size_t bit_i = 0; + size_t bit_rev_i = num_bits - 1; + for (; bit_i < num_bits; bit_i++, bit_rev_i--) { + if (comptime_buf[bit_i / 8] & (1 << (bit_i % 8))) { + result_buf[bit_rev_i / 8] |= (1 << (bit_rev_i % 8)); + } + } + + bigint_read_twos_complement(&result->value->data.x_bigint, + result_buf, + int_type->data.integral.bit_count, + ira->codegen->is_big_endian, + int_type->data.integral.is_signed); + + return result; + } + + return ir_build_bit_reverse_gen(ira, &instruction->base.base, int_type, op); +} + + +static IrInstGen *ir_analyze_instruction_enum_to_int(IrAnalyze *ira, IrInstSrcEnumToInt *instruction) { + IrInstGen *target = instruction->target->child; + if (type_is_invalid(target->value->type)) + return ira->codegen->invalid_inst_gen; + + return ir_analyze_enum_to_int(ira, &instruction->base.base, target); +} + +static IrInstGen *ir_analyze_instruction_int_to_enum(IrAnalyze *ira, IrInstSrcIntToEnum *instruction) { + Error err; + IrInstGen *dest_type_value = instruction->dest_type->child; + ZigType *dest_type = ir_resolve_type(ira, dest_type_value); + if (type_is_invalid(dest_type)) + return ira->codegen->invalid_inst_gen; + + if (dest_type->id != ZigTypeIdEnum) { + ir_add_error(ira, &instruction->dest_type->base, + buf_sprintf("expected enum, found type '%s'", buf_ptr(&dest_type->name))); + return ira->codegen->invalid_inst_gen; + } + + if ((err = type_resolve(ira->codegen, dest_type, ResolveStatusZeroBitsKnown))) + return ira->codegen->invalid_inst_gen; + + ZigType *tag_type = dest_type->data.enumeration.tag_int_type; + + IrInstGen *target = instruction->target->child; + if (type_is_invalid(target->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *casted_target = ir_implicit_cast(ira, target, tag_type); + if (type_is_invalid(casted_target->value->type)) + return ira->codegen->invalid_inst_gen; + + return ir_analyze_int_to_enum(ira, &instruction->base.base, casted_target, dest_type); +} + +static IrInstGen *ir_analyze_instruction_check_runtime_scope(IrAnalyze *ira, IrInstSrcCheckRuntimeScope *instruction) { + IrInstGen *block_comptime_inst = instruction->scope_is_comptime->child; + bool scope_is_comptime; + if (!ir_resolve_bool(ira, block_comptime_inst, &scope_is_comptime)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *is_comptime_inst = instruction->is_comptime->child; + bool is_comptime; + if (!ir_resolve_bool(ira, is_comptime_inst, &is_comptime)) + return ira->codegen->invalid_inst_gen; + + if (!scope_is_comptime && is_comptime) { + ErrorMsg *msg = ir_add_error(ira, &instruction->base.base, + buf_sprintf("comptime control flow inside runtime block")); + add_error_note(ira->codegen, msg, block_comptime_inst->base.source_node, + buf_sprintf("runtime block created here")); + return ira->codegen->invalid_inst_gen; + } + + return ir_const_void(ira, &instruction->base.base); +} + +static IrInstGen *ir_analyze_instruction_has_decl(IrAnalyze *ira, IrInstSrcHasDecl *instruction) { + ZigType *container_type = ir_resolve_type(ira, instruction->container->child); + if (type_is_invalid(container_type)) + return ira->codegen->invalid_inst_gen; + + Buf *name = ir_resolve_str(ira, instruction->name->child); + if (name == nullptr) + return ira->codegen->invalid_inst_gen; + + if (!is_container(container_type)) { + ir_add_error(ira, &instruction->container->base, + buf_sprintf("expected struct, enum, or union; found '%s'", buf_ptr(&container_type->name))); + return ira->codegen->invalid_inst_gen; + } + + ScopeDecls *container_scope = get_container_scope(container_type); + Tld *tld = find_container_decl(ira->codegen, container_scope, name); + if (tld == nullptr) + return ir_const_bool(ira, &instruction->base.base, false); + + if (tld->visib_mod == VisibModPrivate && tld->import != get_scope_import(instruction->base.base.scope)) { + return ir_const_bool(ira, &instruction->base.base, false); + } + + return ir_const_bool(ira, &instruction->base.base, true); +} + +static IrInstGen *ir_analyze_instruction_undeclared_ident(IrAnalyze *ira, IrInstSrcUndeclaredIdent *instruction) { + // put a variable of same name with invalid type in global scope + // so that future references to this same name will find a variable with an invalid type + populate_invalid_variable_in_scope(ira->codegen, instruction->base.base.scope, + instruction->base.base.source_node, instruction->name); + ir_add_error(ira, &instruction->base.base, + buf_sprintf("use of undeclared identifier '%s'", buf_ptr(instruction->name))); + return ira->codegen->invalid_inst_gen; +} + +static IrInstGen *ir_analyze_instruction_end_expr(IrAnalyze *ira, IrInstSrcEndExpr *instruction) { + IrInstGen *value = instruction->value->child; + if (type_is_invalid(value->value->type)) + return ira->codegen->invalid_inst_gen; + + bool was_written = instruction->result_loc->written; + IrInstGen *result_loc = ir_resolve_result(ira, &instruction->base.base, instruction->result_loc, + value->value->type, value, false, true); + if (result_loc != nullptr) { + if (type_is_invalid(result_loc->value->type)) + return ira->codegen->invalid_inst_gen; + if (result_loc->value->type->id == ZigTypeIdUnreachable) + return result_loc; + + if (!was_written || instruction->result_loc->id == ResultLocIdPeer) { + IrInstGen *store_ptr = ir_analyze_store_ptr(ira, &instruction->base.base, result_loc, value, + instruction->result_loc->allow_write_through_const); + if (type_is_invalid(store_ptr->value->type)) { + if (instruction->result_loc->id == ResultLocIdReturn && + (value->value->type->id == ZigTypeIdErrorUnion || value->value->type->id == ZigTypeIdErrorSet) && + ira->explicit_return_type->id != ZigTypeIdErrorUnion && ira->explicit_return_type->id != ZigTypeIdErrorSet) + { + add_error_note(ira->codegen, ira->new_irb.exec->first_err_trace_msg, + ira->explicit_return_type_source_node, buf_create_from_str("function cannot return an error")); + } + return ira->codegen->invalid_inst_gen; + } + } + + if (result_loc->value->data.x_ptr.mut == ConstPtrMutInfer && + instruction->result_loc->id != ResultLocIdPeer) + { + if (instr_is_comptime(value)) { + result_loc->value->data.x_ptr.mut = ConstPtrMutComptimeConst; + } else { + result_loc->value->special = ConstValSpecialRuntime; + } + } + } + + return ir_const_void(ira, &instruction->base.base); +} + +static IrInstGen *ir_analyze_instruction_implicit_cast(IrAnalyze *ira, IrInstSrcImplicitCast *instruction) { + IrInstGen *operand = instruction->operand->child; + if (type_is_invalid(operand->value->type)) + return operand; + + ZigType *dest_type = ir_resolve_type(ira, instruction->result_loc_cast->base.source_instruction->child); + if (type_is_invalid(dest_type)) + return ira->codegen->invalid_inst_gen; + return ir_implicit_cast2(ira, &instruction->base.base, operand, dest_type); +} + +static IrInstGen *ir_analyze_instruction_bit_cast_src(IrAnalyze *ira, IrInstSrcBitCast *instruction) { + IrInstGen *operand = instruction->operand->child; + if (type_is_invalid(operand->value->type)) + return operand; + + IrInstGen *result_loc = ir_resolve_result(ira, &instruction->base.base, + &instruction->result_loc_bit_cast->base, operand->value->type, operand, false, true); + if (result_loc != nullptr && + (type_is_invalid(result_loc->value->type) || result_loc->value->type->id == ZigTypeIdUnreachable)) + { + return result_loc; + } + + ZigType *dest_type = ir_resolve_type(ira, + instruction->result_loc_bit_cast->base.source_instruction->child); + if (type_is_invalid(dest_type)) + return ira->codegen->invalid_inst_gen; + return ir_analyze_bit_cast(ira, &instruction->base.base, operand, dest_type); +} + +static IrInstGen *ir_analyze_instruction_union_init_named_field(IrAnalyze *ira, + IrInstSrcUnionInitNamedField *instruction) +{ + ZigType *union_type = ir_resolve_type(ira, instruction->union_type->child); + if (type_is_invalid(union_type)) + return ira->codegen->invalid_inst_gen; + + if (union_type->id != ZigTypeIdUnion) { + ir_add_error(ira, &instruction->union_type->base, + buf_sprintf("non-union type '%s' passed to @unionInit", buf_ptr(&union_type->name))); + return ira->codegen->invalid_inst_gen; + } + + Buf *field_name = ir_resolve_str(ira, instruction->field_name->child); + if (field_name == nullptr) + return ira->codegen->invalid_inst_gen; + + IrInstGen *field_result_loc = instruction->field_result_loc->child; + if (type_is_invalid(field_result_loc->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *result_loc = instruction->result_loc->child; + if (type_is_invalid(result_loc->value->type)) + return ira->codegen->invalid_inst_gen; + + return ir_analyze_union_init(ira, &instruction->base.base, instruction->base.base.source_node, + union_type, field_name, field_result_loc, result_loc); +} + +static IrInstGen *ir_analyze_instruction_suspend_begin(IrAnalyze *ira, IrInstSrcSuspendBegin *instruction) { + return ir_build_suspend_begin_gen(ira, &instruction->base.base); +} + +static IrInstGen *ir_analyze_instruction_suspend_finish(IrAnalyze *ira, IrInstSrcSuspendFinish *instruction) { + IrInstGen *begin_base = instruction->begin->base.child; + if (type_is_invalid(begin_base->value->type)) + return ira->codegen->invalid_inst_gen; + ir_assert(begin_base->id == IrInstGenIdSuspendBegin, &instruction->base.base); + IrInstGenSuspendBegin *begin = reinterpret_cast(begin_base); + + ZigFn *fn_entry = ira->new_irb.exec->fn_entry; + ir_assert(fn_entry != nullptr, &instruction->base.base); + + if (fn_entry->inferred_async_node == nullptr) { + fn_entry->inferred_async_node = instruction->base.base.source_node; + } + + return ir_build_suspend_finish_gen(ira, &instruction->base.base, begin); +} + +static IrInstGen *analyze_frame_ptr_to_anyframe_T(IrAnalyze *ira, IrInst* source_instr, + IrInstGen *frame_ptr, ZigFn **target_fn) +{ + if (type_is_invalid(frame_ptr->value->type)) + return ira->codegen->invalid_inst_gen; + + *target_fn = nullptr; + + ZigType *result_type; + IrInstGen *frame; + if (frame_ptr->value->type->id == ZigTypeIdPointer && + frame_ptr->value->type->data.pointer.ptr_len == PtrLenSingle && + frame_ptr->value->type->data.pointer.child_type->id == ZigTypeIdFnFrame) + { + ZigFn *func = frame_ptr->value->type->data.pointer.child_type->data.frame.fn; + result_type = func->type_entry->data.fn.fn_type_id.return_type; + *target_fn = func; + frame = frame_ptr; + } else { + frame = ir_get_deref(ira, source_instr, frame_ptr, nullptr); + if (frame->value->type->id == ZigTypeIdPointer && + frame->value->type->data.pointer.ptr_len == PtrLenSingle && + frame->value->type->data.pointer.child_type->id == ZigTypeIdFnFrame) + { + ZigFn *func = frame->value->type->data.pointer.child_type->data.frame.fn; + result_type = func->type_entry->data.fn.fn_type_id.return_type; + *target_fn = func; + } else if (frame->value->type->id != ZigTypeIdAnyFrame || + frame->value->type->data.any_frame.result_type == nullptr) + { + ir_add_error(ira, source_instr, + buf_sprintf("expected anyframe->T, found '%s'", buf_ptr(&frame->value->type->name))); + return ira->codegen->invalid_inst_gen; + } else { + result_type = frame->value->type->data.any_frame.result_type; + } + } + + ZigType *any_frame_type = get_any_frame_type(ira->codegen, result_type); + IrInstGen *casted_frame = ir_implicit_cast(ira, frame, any_frame_type); + if (type_is_invalid(casted_frame->value->type)) + return ira->codegen->invalid_inst_gen; + + return casted_frame; +} + +static IrInstGen *ir_analyze_instruction_await(IrAnalyze *ira, IrInstSrcAwait *instruction) { + IrInstGen *operand = instruction->frame->child; + if (type_is_invalid(operand->value->type)) + return ira->codegen->invalid_inst_gen; + ZigFn *target_fn; + IrInstGen *frame = analyze_frame_ptr_to_anyframe_T(ira, &instruction->base.base, operand, &target_fn); + if (type_is_invalid(frame->value->type)) + return ira->codegen->invalid_inst_gen; + + ZigType *result_type = frame->value->type->data.any_frame.result_type; + + ZigFn *fn_entry = ira->new_irb.exec->fn_entry; + ir_assert(fn_entry != nullptr, &instruction->base.base); + + // If it's not @Frame(func) then it's definitely a suspend point + if (target_fn == nullptr && !instruction->is_nosuspend) { + if (fn_entry->inferred_async_node == nullptr) { + fn_entry->inferred_async_node = instruction->base.base.source_node; + } + } + + if (type_can_fail(result_type)) { + fn_entry->calls_or_awaits_errorable_fn = true; + } + + IrInstGen *result_loc; + if (type_has_bits(ira->codegen, result_type)) { + result_loc = ir_resolve_result(ira, &instruction->base.base, instruction->result_loc, + result_type, nullptr, true, true); + if (result_loc != nullptr && + (type_is_invalid(result_loc->value->type) || result_loc->value->type->id == ZigTypeIdUnreachable)) + { + return result_loc; + } + } else { + result_loc = nullptr; + } + + IrInstGenAwait *result = ir_build_await_gen(ira, &instruction->base.base, frame, result_type, result_loc, + instruction->is_nosuspend); + result->target_fn = target_fn; + fn_entry->await_list.append(result); + return ir_finish_anal(ira, &result->base); +} + +static IrInstGen *ir_analyze_instruction_resume(IrAnalyze *ira, IrInstSrcResume *instruction) { + IrInstGen *frame_ptr = instruction->frame->child; + if (type_is_invalid(frame_ptr->value->type)) + return ira->codegen->invalid_inst_gen; + + IrInstGen *frame; + if (frame_ptr->value->type->id == ZigTypeIdPointer && + frame_ptr->value->type->data.pointer.ptr_len == PtrLenSingle && + frame_ptr->value->type->data.pointer.child_type->id == ZigTypeIdFnFrame) + { + frame = frame_ptr; + } else { + frame = ir_get_deref(ira, &instruction->base.base, frame_ptr, nullptr); + } + + ZigType *any_frame_type = get_any_frame_type(ira->codegen, nullptr); + IrInstGen *casted_frame = ir_implicit_cast2(ira, &instruction->frame->base, frame, any_frame_type); + if (type_is_invalid(casted_frame->value->type)) + return ira->codegen->invalid_inst_gen; + + return ir_build_resume_gen(ira, &instruction->base.base, casted_frame); +} + +static IrInstGen *ir_analyze_instruction_spill_begin(IrAnalyze *ira, IrInstSrcSpillBegin *instruction) { + if (ir_should_inline(ira->old_irb.exec, instruction->base.base.scope)) + return ir_const_void(ira, &instruction->base.base); + + IrInstGen *operand = instruction->operand->child; + if (type_is_invalid(operand->value->type)) + return ira->codegen->invalid_inst_gen; + + if (!type_has_bits(ira->codegen, operand->value->type)) + return ir_const_void(ira, &instruction->base.base); + + switch (instruction->spill_id) { + case SpillIdInvalid: + zig_unreachable(); + case SpillIdRetErrCode: + ira->new_irb.exec->need_err_code_spill = true; + break; + } + + return ir_build_spill_begin_gen(ira, &instruction->base.base, operand, instruction->spill_id); +} + +static IrInstGen *ir_analyze_instruction_spill_end(IrAnalyze *ira, IrInstSrcSpillEnd *instruction) { + IrInstGen *operand = instruction->begin->operand->child; + if (type_is_invalid(operand->value->type)) + return ira->codegen->invalid_inst_gen; + + if (ir_should_inline(ira->old_irb.exec, instruction->base.base.scope) || + !type_has_bits(ira->codegen, operand->value->type) || + instr_is_comptime(operand)) + { + return operand; + } + + ir_assert(instruction->begin->base.child->id == IrInstGenIdSpillBegin, &instruction->base.base); + IrInstGenSpillBegin *begin = reinterpret_cast(instruction->begin->base.child); + + return ir_build_spill_end_gen(ira, &instruction->base.base, begin, operand->value->type); +} + +static IrInstGen *ir_analyze_instruction_src(IrAnalyze *ira, IrInstSrcSrc *instruction) { + ZigFn *fn_entry = scope_fn_entry(instruction->base.base.scope); + if (fn_entry == nullptr) { + ir_add_error(ira, &instruction->base.base, buf_sprintf("@src outside function")); + return ira->codegen->invalid_inst_gen; + } + + ZigType *u8_ptr = get_pointer_to_type_extra2( + ira->codegen, ira->codegen->builtin_types.entry_u8, + true, false, PtrLenUnknown, + 0, 0, 0, false, VECTOR_INDEX_NONE, nullptr, ira->codegen->intern.for_zero_byte()); + ZigType *u8_slice = get_slice_type(ira->codegen, u8_ptr); + + ZigType *source_location_type = get_builtin_type(ira->codegen, "SourceLocation"); + if (type_resolve(ira->codegen, source_location_type, ResolveStatusSizeKnown)) { + zig_unreachable(); + } + + ZigValue *result = ira->codegen->pass1_arena->create(); + result->special = ConstValSpecialStatic; + result->type = source_location_type; + + ZigValue **fields = alloc_const_vals_ptrs(ira->codegen, 4); + result->data.x_struct.fields = fields; + + // file: [:0]const u8 + ensure_field_index(source_location_type, "file", 0); + fields[0]->special = ConstValSpecialStatic; + + ZigType *import = instruction->base.base.source_node->owner; + Buf *path = import->data.structure.root_struct->path; + ZigValue *file_name = create_const_str_lit(ira->codegen, path)->data.x_ptr.data.ref.pointee; + init_const_slice(ira->codegen, fields[0], file_name, 0, buf_len(path), true); + fields[0]->type = u8_slice; + + // fn_name: [:0]const u8 + ensure_field_index(source_location_type, "fn_name", 1); + fields[1]->special = ConstValSpecialStatic; + + ZigValue *fn_name = create_const_str_lit(ira->codegen, &fn_entry->symbol_name)->data.x_ptr.data.ref.pointee; + init_const_slice(ira->codegen, fields[1], fn_name, 0, buf_len(&fn_entry->symbol_name), true); + fields[1]->type = u8_slice; + + // line: u32 + ensure_field_index(source_location_type, "line", 2); + fields[2]->special = ConstValSpecialStatic; + fields[2]->type = ira->codegen->builtin_types.entry_u32; + bigint_init_unsigned(&fields[2]->data.x_bigint, instruction->base.base.source_node->line + 1); + + // column: u32 + ensure_field_index(source_location_type, "column", 3); + fields[3]->special = ConstValSpecialStatic; + fields[3]->type = ira->codegen->builtin_types.entry_u32; + bigint_init_unsigned(&fields[3]->data.x_bigint, instruction->base.base.source_node->column + 1); + + return ir_const_move(ira, &instruction->base.base, result); +} + +static IrInstGen *ir_analyze_instruction_base(IrAnalyze *ira, IrInstSrc *instruction) { + switch (instruction->id) { + case IrInstSrcIdInvalid: + zig_unreachable(); + + case IrInstSrcIdReturn: + return ir_analyze_instruction_return(ira, (IrInstSrcReturn *)instruction); + case IrInstSrcIdConst: + return ir_analyze_instruction_const(ira, (IrInstSrcConst *)instruction); + case IrInstSrcIdUnOp: + return ir_analyze_instruction_un_op(ira, (IrInstSrcUnOp *)instruction); + case IrInstSrcIdBinOp: + return ir_analyze_instruction_bin_op(ira, (IrInstSrcBinOp *)instruction); + case IrInstSrcIdMergeErrSets: + return ir_analyze_instruction_merge_err_sets(ira, (IrInstSrcMergeErrSets *)instruction); + case IrInstSrcIdDeclVar: + return ir_analyze_instruction_decl_var(ira, (IrInstSrcDeclVar *)instruction); + case IrInstSrcIdLoadPtr: + return ir_analyze_instruction_load_ptr(ira, (IrInstSrcLoadPtr *)instruction); + case IrInstSrcIdStorePtr: + return ir_analyze_instruction_store_ptr(ira, (IrInstSrcStorePtr *)instruction); + case IrInstSrcIdElemPtr: + return ir_analyze_instruction_elem_ptr(ira, (IrInstSrcElemPtr *)instruction); + case IrInstSrcIdVarPtr: + return ir_analyze_instruction_var_ptr(ira, (IrInstSrcVarPtr *)instruction); + case IrInstSrcIdFieldPtr: + return ir_analyze_instruction_field_ptr(ira, (IrInstSrcFieldPtr *)instruction); + case IrInstSrcIdCall: + return ir_analyze_instruction_call(ira, (IrInstSrcCall *)instruction); + case IrInstSrcIdCallArgs: + return ir_analyze_instruction_call_args(ira, (IrInstSrcCallArgs *)instruction); + case IrInstSrcIdCallExtra: + return ir_analyze_instruction_call_extra(ira, (IrInstSrcCallExtra *)instruction); + case IrInstSrcIdAsyncCallExtra: + return ir_analyze_instruction_async_call_extra(ira, (IrInstSrcAsyncCallExtra *)instruction); + case IrInstSrcIdBr: + return ir_analyze_instruction_br(ira, (IrInstSrcBr *)instruction); + case IrInstSrcIdCondBr: + return ir_analyze_instruction_cond_br(ira, (IrInstSrcCondBr *)instruction); + case IrInstSrcIdUnreachable: + return ir_analyze_instruction_unreachable(ira, (IrInstSrcUnreachable *)instruction); + case IrInstSrcIdPhi: + return ir_analyze_instruction_phi(ira, (IrInstSrcPhi *)instruction); + case IrInstSrcIdTypeOf: + return ir_analyze_instruction_typeof(ira, (IrInstSrcTypeOf *)instruction); + case IrInstSrcIdSetCold: + return ir_analyze_instruction_set_cold(ira, (IrInstSrcSetCold *)instruction); + case IrInstSrcIdSetRuntimeSafety: + return ir_analyze_instruction_set_runtime_safety(ira, (IrInstSrcSetRuntimeSafety *)instruction); + case IrInstSrcIdSetFloatMode: + return ir_analyze_instruction_set_float_mode(ira, (IrInstSrcSetFloatMode *)instruction); + case IrInstSrcIdAnyFrameType: + return ir_analyze_instruction_any_frame_type(ira, (IrInstSrcAnyFrameType *)instruction); + case IrInstSrcIdSliceType: + return ir_analyze_instruction_slice_type(ira, (IrInstSrcSliceType *)instruction); + case IrInstSrcIdAsm: + return ir_analyze_instruction_asm(ira, (IrInstSrcAsm *)instruction); + case IrInstSrcIdArrayType: + return ir_analyze_instruction_array_type(ira, (IrInstSrcArrayType *)instruction); + case IrInstSrcIdSizeOf: + return ir_analyze_instruction_size_of(ira, (IrInstSrcSizeOf *)instruction); + case IrInstSrcIdTestNonNull: + return ir_analyze_instruction_test_non_null(ira, (IrInstSrcTestNonNull *)instruction); + case IrInstSrcIdOptionalUnwrapPtr: + return ir_analyze_instruction_optional_unwrap_ptr(ira, (IrInstSrcOptionalUnwrapPtr *)instruction); + case IrInstSrcIdClz: + return ir_analyze_instruction_clz(ira, (IrInstSrcClz *)instruction); + case IrInstSrcIdCtz: + return ir_analyze_instruction_ctz(ira, (IrInstSrcCtz *)instruction); + case IrInstSrcIdPopCount: + return ir_analyze_instruction_pop_count(ira, (IrInstSrcPopCount *)instruction); + case IrInstSrcIdBswap: + return ir_analyze_instruction_bswap(ira, (IrInstSrcBswap *)instruction); + case IrInstSrcIdBitReverse: + return ir_analyze_instruction_bit_reverse(ira, (IrInstSrcBitReverse *)instruction); + case IrInstSrcIdSwitchBr: + return ir_analyze_instruction_switch_br(ira, (IrInstSrcSwitchBr *)instruction); + case IrInstSrcIdSwitchTarget: + return ir_analyze_instruction_switch_target(ira, (IrInstSrcSwitchTarget *)instruction); + case IrInstSrcIdSwitchVar: + return ir_analyze_instruction_switch_var(ira, (IrInstSrcSwitchVar *)instruction); + case IrInstSrcIdSwitchElseVar: + return ir_analyze_instruction_switch_else_var(ira, (IrInstSrcSwitchElseVar *)instruction); + case IrInstSrcIdImport: + return ir_analyze_instruction_import(ira, (IrInstSrcImport *)instruction); + case IrInstSrcIdRef: + return ir_analyze_instruction_ref(ira, (IrInstSrcRef *)instruction); + case IrInstSrcIdContainerInitList: + return ir_analyze_instruction_container_init_list(ira, (IrInstSrcContainerInitList *)instruction); + case IrInstSrcIdContainerInitFields: + return ir_analyze_instruction_container_init_fields(ira, (IrInstSrcContainerInitFields *)instruction); + case IrInstSrcIdCompileErr: + return ir_analyze_instruction_compile_err(ira, (IrInstSrcCompileErr *)instruction); + case IrInstSrcIdCompileLog: + return ir_analyze_instruction_compile_log(ira, (IrInstSrcCompileLog *)instruction); + case IrInstSrcIdErrName: + return ir_analyze_instruction_err_name(ira, (IrInstSrcErrName *)instruction); + case IrInstSrcIdTypeName: + return ir_analyze_instruction_type_name(ira, (IrInstSrcTypeName *)instruction); + case IrInstSrcIdCImport: + return ir_analyze_instruction_c_import(ira, (IrInstSrcCImport *)instruction); + case IrInstSrcIdCInclude: + return ir_analyze_instruction_c_include(ira, (IrInstSrcCInclude *)instruction); + case IrInstSrcIdCDefine: + return ir_analyze_instruction_c_define(ira, (IrInstSrcCDefine *)instruction); + case IrInstSrcIdCUndef: + return ir_analyze_instruction_c_undef(ira, (IrInstSrcCUndef *)instruction); + case IrInstSrcIdEmbedFile: + return ir_analyze_instruction_embed_file(ira, (IrInstSrcEmbedFile *)instruction); + case IrInstSrcIdCmpxchg: + return ir_analyze_instruction_cmpxchg(ira, (IrInstSrcCmpxchg *)instruction); + case IrInstSrcIdFence: + return ir_analyze_instruction_fence(ira, (IrInstSrcFence *)instruction); + case IrInstSrcIdTruncate: + return ir_analyze_instruction_truncate(ira, (IrInstSrcTruncate *)instruction); + case IrInstSrcIdIntCast: + return ir_analyze_instruction_int_cast(ira, (IrInstSrcIntCast *)instruction); + case IrInstSrcIdFloatCast: + return ir_analyze_instruction_float_cast(ira, (IrInstSrcFloatCast *)instruction); + case IrInstSrcIdErrSetCast: + return ir_analyze_instruction_err_set_cast(ira, (IrInstSrcErrSetCast *)instruction); + case IrInstSrcIdIntToFloat: + return ir_analyze_instruction_int_to_float(ira, (IrInstSrcIntToFloat *)instruction); + case IrInstSrcIdFloatToInt: + return ir_analyze_instruction_float_to_int(ira, (IrInstSrcFloatToInt *)instruction); + case IrInstSrcIdBoolToInt: + return ir_analyze_instruction_bool_to_int(ira, (IrInstSrcBoolToInt *)instruction); + case IrInstSrcIdVectorType: + return ir_analyze_instruction_vector_type(ira, (IrInstSrcVectorType *)instruction); + case IrInstSrcIdShuffleVector: + return ir_analyze_instruction_shuffle_vector(ira, (IrInstSrcShuffleVector *)instruction); + case IrInstSrcIdSplat: + return ir_analyze_instruction_splat(ira, (IrInstSrcSplat *)instruction); + case IrInstSrcIdBoolNot: + return ir_analyze_instruction_bool_not(ira, (IrInstSrcBoolNot *)instruction); + case IrInstSrcIdMemset: + return ir_analyze_instruction_memset(ira, (IrInstSrcMemset *)instruction); + case IrInstSrcIdMemcpy: + return ir_analyze_instruction_memcpy(ira, (IrInstSrcMemcpy *)instruction); + case IrInstSrcIdSlice: + return ir_analyze_instruction_slice(ira, (IrInstSrcSlice *)instruction); + case IrInstSrcIdBreakpoint: + return ir_analyze_instruction_breakpoint(ira, (IrInstSrcBreakpoint *)instruction); + case IrInstSrcIdReturnAddress: + return ir_analyze_instruction_return_address(ira, (IrInstSrcReturnAddress *)instruction); + case IrInstSrcIdFrameAddress: + return ir_analyze_instruction_frame_address(ira, (IrInstSrcFrameAddress *)instruction); + case IrInstSrcIdFrameHandle: + return ir_analyze_instruction_frame_handle(ira, (IrInstSrcFrameHandle *)instruction); + case IrInstSrcIdFrameType: + return ir_analyze_instruction_frame_type(ira, (IrInstSrcFrameType *)instruction); + case IrInstSrcIdFrameSize: + return ir_analyze_instruction_frame_size(ira, (IrInstSrcFrameSize *)instruction); + case IrInstSrcIdAlignOf: + return ir_analyze_instruction_align_of(ira, (IrInstSrcAlignOf *)instruction); + case IrInstSrcIdOverflowOp: + return ir_analyze_instruction_overflow_op(ira, (IrInstSrcOverflowOp *)instruction); + case IrInstSrcIdTestErr: + return ir_analyze_instruction_test_err(ira, (IrInstSrcTestErr *)instruction); + case IrInstSrcIdUnwrapErrCode: + return ir_analyze_instruction_unwrap_err_code(ira, (IrInstSrcUnwrapErrCode *)instruction); + case IrInstSrcIdUnwrapErrPayload: + return ir_analyze_instruction_unwrap_err_payload(ira, (IrInstSrcUnwrapErrPayload *)instruction); + case IrInstSrcIdFnProto: + return ir_analyze_instruction_fn_proto(ira, (IrInstSrcFnProto *)instruction); + case IrInstSrcIdTestComptime: + return ir_analyze_instruction_test_comptime(ira, (IrInstSrcTestComptime *)instruction); + case IrInstSrcIdCheckSwitchProngs: + return ir_analyze_instruction_check_switch_prongs(ira, (IrInstSrcCheckSwitchProngs *)instruction); + case IrInstSrcIdCheckStatementIsVoid: + return ir_analyze_instruction_check_statement_is_void(ira, (IrInstSrcCheckStatementIsVoid *)instruction); + case IrInstSrcIdDeclRef: + return ir_analyze_instruction_decl_ref(ira, (IrInstSrcDeclRef *)instruction); + case IrInstSrcIdPanic: + return ir_analyze_instruction_panic(ira, (IrInstSrcPanic *)instruction); + case IrInstSrcIdPtrCast: + return ir_analyze_instruction_ptr_cast(ira, (IrInstSrcPtrCast *)instruction); + case IrInstSrcIdIntToPtr: + return ir_analyze_instruction_int_to_ptr(ira, (IrInstSrcIntToPtr *)instruction); + case IrInstSrcIdPtrToInt: + return ir_analyze_instruction_ptr_to_int(ira, (IrInstSrcPtrToInt *)instruction); + case IrInstSrcIdTagName: + return ir_analyze_instruction_enum_tag_name(ira, (IrInstSrcTagName *)instruction); + case IrInstSrcIdFieldParentPtr: + return ir_analyze_instruction_field_parent_ptr(ira, (IrInstSrcFieldParentPtr *)instruction); + case IrInstSrcIdByteOffsetOf: + return ir_analyze_instruction_byte_offset_of(ira, (IrInstSrcByteOffsetOf *)instruction); + case IrInstSrcIdBitOffsetOf: + return ir_analyze_instruction_bit_offset_of(ira, (IrInstSrcBitOffsetOf *)instruction); + case IrInstSrcIdTypeInfo: + return ir_analyze_instruction_type_info(ira, (IrInstSrcTypeInfo *) instruction); + case IrInstSrcIdType: + return ir_analyze_instruction_type(ira, (IrInstSrcType *)instruction); + case IrInstSrcIdHasField: + return ir_analyze_instruction_has_field(ira, (IrInstSrcHasField *) instruction); + case IrInstSrcIdSetEvalBranchQuota: + return ir_analyze_instruction_set_eval_branch_quota(ira, (IrInstSrcSetEvalBranchQuota *)instruction); + case IrInstSrcIdPtrType: + return ir_analyze_instruction_ptr_type(ira, (IrInstSrcPtrType *)instruction); + case IrInstSrcIdAlignCast: + return ir_analyze_instruction_align_cast(ira, (IrInstSrcAlignCast *)instruction); + case IrInstSrcIdImplicitCast: + return ir_analyze_instruction_implicit_cast(ira, (IrInstSrcImplicitCast *)instruction); + case IrInstSrcIdResolveResult: + return ir_analyze_instruction_resolve_result(ira, (IrInstSrcResolveResult *)instruction); + case IrInstSrcIdResetResult: + return ir_analyze_instruction_reset_result(ira, (IrInstSrcResetResult *)instruction); + case IrInstSrcIdSetAlignStack: + return ir_analyze_instruction_set_align_stack(ira, (IrInstSrcSetAlignStack *)instruction); + case IrInstSrcIdArgType: + return ir_analyze_instruction_arg_type(ira, (IrInstSrcArgType *)instruction); + case IrInstSrcIdTagType: + return ir_analyze_instruction_tag_type(ira, (IrInstSrcTagType *)instruction); + case IrInstSrcIdExport: + return ir_analyze_instruction_export(ira, (IrInstSrcExport *)instruction); + case IrInstSrcIdErrorReturnTrace: + return ir_analyze_instruction_error_return_trace(ira, (IrInstSrcErrorReturnTrace *)instruction); + case IrInstSrcIdErrorUnion: + return ir_analyze_instruction_error_union(ira, (IrInstSrcErrorUnion *)instruction); + case IrInstSrcIdAtomicRmw: + return ir_analyze_instruction_atomic_rmw(ira, (IrInstSrcAtomicRmw *)instruction); + case IrInstSrcIdAtomicLoad: + return ir_analyze_instruction_atomic_load(ira, (IrInstSrcAtomicLoad *)instruction); + case IrInstSrcIdAtomicStore: + return ir_analyze_instruction_atomic_store(ira, (IrInstSrcAtomicStore *)instruction); + case IrInstSrcIdSaveErrRetAddr: + return ir_analyze_instruction_save_err_ret_addr(ira, (IrInstSrcSaveErrRetAddr *)instruction); + case IrInstSrcIdAddImplicitReturnType: + return ir_analyze_instruction_add_implicit_return_type(ira, (IrInstSrcAddImplicitReturnType *)instruction); + case IrInstSrcIdFloatOp: + return ir_analyze_instruction_float_op(ira, (IrInstSrcFloatOp *)instruction); + case IrInstSrcIdMulAdd: + return ir_analyze_instruction_mul_add(ira, (IrInstSrcMulAdd *)instruction); + case IrInstSrcIdIntToErr: + return ir_analyze_instruction_int_to_err(ira, (IrInstSrcIntToErr *)instruction); + case IrInstSrcIdErrToInt: + return ir_analyze_instruction_err_to_int(ira, (IrInstSrcErrToInt *)instruction); + case IrInstSrcIdIntToEnum: + return ir_analyze_instruction_int_to_enum(ira, (IrInstSrcIntToEnum *)instruction); + case IrInstSrcIdEnumToInt: + return ir_analyze_instruction_enum_to_int(ira, (IrInstSrcEnumToInt *)instruction); + case IrInstSrcIdCheckRuntimeScope: + return ir_analyze_instruction_check_runtime_scope(ira, (IrInstSrcCheckRuntimeScope *)instruction); + case IrInstSrcIdHasDecl: + return ir_analyze_instruction_has_decl(ira, (IrInstSrcHasDecl *)instruction); + case IrInstSrcIdUndeclaredIdent: + return ir_analyze_instruction_undeclared_ident(ira, (IrInstSrcUndeclaredIdent *)instruction); + case IrInstSrcIdAlloca: + return nullptr; + case IrInstSrcIdEndExpr: + return ir_analyze_instruction_end_expr(ira, (IrInstSrcEndExpr *)instruction); + case IrInstSrcIdBitCast: + return ir_analyze_instruction_bit_cast_src(ira, (IrInstSrcBitCast *)instruction); + case IrInstSrcIdUnionInitNamedField: + return ir_analyze_instruction_union_init_named_field(ira, (IrInstSrcUnionInitNamedField *)instruction); + case IrInstSrcIdSuspendBegin: + return ir_analyze_instruction_suspend_begin(ira, (IrInstSrcSuspendBegin *)instruction); + case IrInstSrcIdSuspendFinish: + return ir_analyze_instruction_suspend_finish(ira, (IrInstSrcSuspendFinish *)instruction); + case IrInstSrcIdResume: + return ir_analyze_instruction_resume(ira, (IrInstSrcResume *)instruction); + case IrInstSrcIdAwait: + return ir_analyze_instruction_await(ira, (IrInstSrcAwait *)instruction); + case IrInstSrcIdSpillBegin: + return ir_analyze_instruction_spill_begin(ira, (IrInstSrcSpillBegin *)instruction); + case IrInstSrcIdSpillEnd: + return ir_analyze_instruction_spill_end(ira, (IrInstSrcSpillEnd *)instruction); + case IrInstSrcIdWasmMemorySize: + return ir_analyze_instruction_wasm_memory_size(ira, (IrInstSrcWasmMemorySize *)instruction); + case IrInstSrcIdWasmMemoryGrow: + return ir_analyze_instruction_wasm_memory_grow(ira, (IrInstSrcWasmMemoryGrow *)instruction); + case IrInstSrcIdSrc: + return ir_analyze_instruction_src(ira, (IrInstSrcSrc *)instruction); + } + zig_unreachable(); +} + +// This function attempts to evaluate IR code while doing type checking and other analysis. +// It emits to a new IrExecutableGen which is partially evaluated IR code. +ZigType *ir_analyze(CodeGen *codegen, IrExecutableSrc *old_exec, IrExecutableGen *new_exec, + ZigType *expected_type, AstNode *expected_type_source_node, ZigValue *result_ptr) +{ + assert(old_exec->first_err_trace_msg == nullptr); + assert(expected_type == nullptr || !type_is_invalid(expected_type)); + + IrAnalyze *ira = heap::c_allocator.create(); + ira->ref_count = 1; + old_exec->analysis = ira; + ira->codegen = codegen; + + ira->explicit_return_type = expected_type; + ira->explicit_return_type_source_node = expected_type_source_node; + + ira->old_irb.codegen = codegen; + ira->old_irb.exec = old_exec; + + ira->new_irb.codegen = codegen; + ira->new_irb.exec = new_exec; + + IrBasicBlockSrc *old_entry_bb = ira->old_irb.exec->basic_block_list.at(0); + IrBasicBlockGen *new_entry_bb = ir_get_new_bb(ira, old_entry_bb, nullptr); + ira->new_irb.current_basic_block = new_entry_bb; + ira->old_bb_index = 0; + + ir_start_bb(ira, old_entry_bb, nullptr); + + if (result_ptr != nullptr) { + assert(result_ptr->type->id == ZigTypeIdPointer); + IrInstGenConst *const_inst = ir_create_inst_noval( + &ira->new_irb, new_exec->begin_scope, new_exec->source_node); + const_inst->base.value = result_ptr; + ira->return_ptr = &const_inst->base; + } else { + assert(new_exec->begin_scope != nullptr); + assert(new_exec->source_node != nullptr); + ira->return_ptr = ir_build_return_ptr(ira, new_exec->begin_scope, new_exec->source_node, + get_pointer_to_type(codegen, expected_type, false)); + } + + while (ira->old_bb_index < ira->old_irb.exec->basic_block_list.length) { + IrInstSrc *old_instruction = ira->old_irb.current_basic_block->instruction_list.at(ira->instruction_index); + + if (old_instruction->base.ref_count == 0 && !ir_inst_src_has_side_effects(old_instruction)) { + ira->instruction_index += 1; + continue; + } + + if (ira->codegen->verbose_ir) { + fprintf(stderr, "~ "); + old_instruction->src(); + fprintf(stderr, "~ "); + ir_print_inst_src(codegen, stderr, old_instruction, 0); + bool want_break = false; + if (ira->break_debug_id == old_instruction->base.debug_id) { + want_break = true; + } else if (old_instruction->base.source_node != nullptr) { + for (size_t i = 0; i < dbg_ir_breakpoints_count; i += 1) { + if (dbg_ir_breakpoints_buf[i].line == old_instruction->base.source_node->line + 1 && + buf_ends_with_str(old_instruction->base.source_node->owner->data.structure.root_struct->path, + dbg_ir_breakpoints_buf[i].src_file)) + { + want_break = true; + } + } + } + if (want_break) BREAKPOINT; + } + IrInstGen *new_instruction = ir_analyze_instruction_base(ira, old_instruction); + if (new_instruction != nullptr) { + ir_assert(new_instruction->value->type != nullptr || new_instruction->value->type != nullptr, &old_instruction->base); + old_instruction->child = new_instruction; + + if (type_is_invalid(new_instruction->value->type)) { + if (ira->codegen->verbose_ir) { + fprintf(stderr, "-> (invalid)"); + } + + if (new_exec->first_err_trace_msg != nullptr) { + ira->codegen->trace_err = new_exec->first_err_trace_msg; + } else { + new_exec->first_err_trace_msg = ira->codegen->trace_err; + } + if (new_exec->first_err_trace_msg != nullptr && + !old_instruction->base.source_node->already_traced_this_node) + { + old_instruction->base.source_node->already_traced_this_node = true; + new_exec->first_err_trace_msg = add_error_note(ira->codegen, new_exec->first_err_trace_msg, + old_instruction->base.source_node, buf_create_from_str("referenced here")); + } + return ira->codegen->builtin_types.entry_invalid; + } else if (ira->codegen->verbose_ir) { + fprintf(stderr, "-> "); + if (new_instruction->value->type->id == ZigTypeIdUnreachable) { + fprintf(stderr, "(noreturn)\n"); + } else { + ir_print_inst_gen(codegen, stderr, new_instruction, 0); + } + } + + // unreachable instructions do their own control flow. + if (new_instruction->value->type->id == ZigTypeIdUnreachable) + continue; + } else { + if (ira->codegen->verbose_ir) { + fprintf(stderr, "-> (null"); + } + } + + ira->instruction_index += 1; + } + + ZigType *res_type; + if (new_exec->first_err_trace_msg != nullptr) { + codegen->trace_err = new_exec->first_err_trace_msg; + if (codegen->trace_err != nullptr && new_exec->source_node != nullptr && + !new_exec->source_node->already_traced_this_node) + { + new_exec->source_node->already_traced_this_node = true; + codegen->trace_err = add_error_note(codegen, codegen->trace_err, + new_exec->source_node, buf_create_from_str("referenced here")); + } + res_type = ira->codegen->builtin_types.entry_invalid; + } else if (ira->src_implicit_return_type_list.length == 0) { + res_type = codegen->builtin_types.entry_unreachable; + } else { + res_type = ir_resolve_peer_types(ira, expected_type_source_node, expected_type, ira->src_implicit_return_type_list.items, + ira->src_implicit_return_type_list.length); + } + + // It is now safe to free Pass 1 IR instructions. + ira_deref(ira); + + return res_type; +} + +bool ir_inst_gen_has_side_effects(IrInstGen *instruction) { + switch (instruction->id) { + case IrInstGenIdInvalid: + zig_unreachable(); + case IrInstGenIdBr: + case IrInstGenIdCondBr: + case IrInstGenIdSwitchBr: + case IrInstGenIdDeclVar: + case IrInstGenIdStorePtr: + case IrInstGenIdVectorStoreElem: + case IrInstGenIdCall: + case IrInstGenIdReturn: + case IrInstGenIdUnreachable: + case IrInstGenIdFence: + case IrInstGenIdMemset: + case IrInstGenIdMemcpy: + case IrInstGenIdBreakpoint: + case IrInstGenIdOverflowOp: // TODO when we support multiple returns this can be side effect free + case IrInstGenIdPanic: + case IrInstGenIdSaveErrRetAddr: + case IrInstGenIdAtomicRmw: + case IrInstGenIdAtomicStore: + case IrInstGenIdCmpxchg: + case IrInstGenIdAssertZero: + case IrInstGenIdAssertNonNull: + case IrInstGenIdPtrOfArrayToSlice: + case IrInstGenIdSlice: + case IrInstGenIdOptionalWrap: + case IrInstGenIdVectorToArray: + case IrInstGenIdSuspendBegin: + case IrInstGenIdSuspendFinish: + case IrInstGenIdResume: + case IrInstGenIdAwait: + case IrInstGenIdSpillBegin: + case IrInstGenIdWasmMemoryGrow: + return true; + + case IrInstGenIdPhi: + case IrInstGenIdBinOp: + case IrInstGenIdConst: + case IrInstGenIdCast: + case IrInstGenIdElemPtr: + case IrInstGenIdVarPtr: + case IrInstGenIdReturnPtr: + case IrInstGenIdStructFieldPtr: + case IrInstGenIdTestNonNull: + case IrInstGenIdClz: + case IrInstGenIdCtz: + case IrInstGenIdPopCount: + case IrInstGenIdBswap: + case IrInstGenIdBitReverse: + case IrInstGenIdUnionTag: + case IrInstGenIdTruncate: + case IrInstGenIdShuffleVector: + case IrInstGenIdSplat: + case IrInstGenIdBoolNot: + case IrInstGenIdReturnAddress: + case IrInstGenIdFrameAddress: + case IrInstGenIdFrameHandle: + case IrInstGenIdFrameSize: + case IrInstGenIdTestErr: + case IrInstGenIdPtrCast: + case IrInstGenIdBitCast: + case IrInstGenIdWidenOrShorten: + case IrInstGenIdPtrToInt: + case IrInstGenIdIntToPtr: + case IrInstGenIdIntToEnum: + case IrInstGenIdIntToErr: + case IrInstGenIdErrToInt: + case IrInstGenIdErrName: + case IrInstGenIdTagName: + case IrInstGenIdFieldParentPtr: + case IrInstGenIdAlignCast: + case IrInstGenIdErrorReturnTrace: + case IrInstGenIdFloatOp: + case IrInstGenIdMulAdd: + case IrInstGenIdAtomicLoad: + case IrInstGenIdArrayToVector: + case IrInstGenIdAlloca: + case IrInstGenIdSpillEnd: + case IrInstGenIdVectorExtractElem: + case IrInstGenIdBinaryNot: + case IrInstGenIdNegation: + case IrInstGenIdNegationWrapping: + case IrInstGenIdWasmMemorySize: + return false; + + case IrInstGenIdAsm: + { + IrInstGenAsm *asm_instruction = (IrInstGenAsm *)instruction; + return asm_instruction->has_side_effects; + } + case IrInstGenIdUnwrapErrPayload: + { + IrInstGenUnwrapErrPayload *unwrap_err_payload_instruction = + (IrInstGenUnwrapErrPayload *)instruction; + return unwrap_err_payload_instruction->safety_check_on || + unwrap_err_payload_instruction->initializing; + } + case IrInstGenIdUnwrapErrCode: + return reinterpret_cast(instruction)->initializing; + case IrInstGenIdUnionFieldPtr: + return reinterpret_cast(instruction)->initializing; + case IrInstGenIdOptionalUnwrapPtr: + return reinterpret_cast(instruction)->initializing; + case IrInstGenIdErrWrapPayload: + return reinterpret_cast(instruction)->result_loc != nullptr; + case IrInstGenIdErrWrapCode: + return reinterpret_cast(instruction)->result_loc != nullptr; + case IrInstGenIdLoadPtr: + return reinterpret_cast(instruction)->result_loc != nullptr; + case IrInstGenIdRef: + return reinterpret_cast(instruction)->result_loc != nullptr; + } + zig_unreachable(); +} + +bool ir_inst_src_has_side_effects(IrInstSrc *instruction) { + switch (instruction->id) { + case IrInstSrcIdInvalid: + zig_unreachable(); + case IrInstSrcIdBr: + case IrInstSrcIdCondBr: + case IrInstSrcIdSwitchBr: + case IrInstSrcIdDeclVar: + case IrInstSrcIdStorePtr: + case IrInstSrcIdCallExtra: + case IrInstSrcIdAsyncCallExtra: + case IrInstSrcIdCall: + case IrInstSrcIdCallArgs: + case IrInstSrcIdReturn: + case IrInstSrcIdUnreachable: + case IrInstSrcIdSetCold: + case IrInstSrcIdSetRuntimeSafety: + case IrInstSrcIdSetFloatMode: + case IrInstSrcIdImport: + case IrInstSrcIdCompileErr: + case IrInstSrcIdCompileLog: + case IrInstSrcIdCImport: + case IrInstSrcIdCInclude: + case IrInstSrcIdCDefine: + case IrInstSrcIdCUndef: + case IrInstSrcIdFence: + case IrInstSrcIdMemset: + case IrInstSrcIdMemcpy: + case IrInstSrcIdBreakpoint: + case IrInstSrcIdOverflowOp: // TODO when we support multiple returns this can be side effect free + case IrInstSrcIdCheckSwitchProngs: + case IrInstSrcIdCheckStatementIsVoid: + case IrInstSrcIdCheckRuntimeScope: + case IrInstSrcIdPanic: + case IrInstSrcIdSetEvalBranchQuota: + case IrInstSrcIdPtrType: + case IrInstSrcIdSetAlignStack: + case IrInstSrcIdExport: + case IrInstSrcIdSaveErrRetAddr: + case IrInstSrcIdAddImplicitReturnType: + case IrInstSrcIdAtomicRmw: + case IrInstSrcIdAtomicStore: + case IrInstSrcIdCmpxchg: + case IrInstSrcIdUndeclaredIdent: + case IrInstSrcIdEndExpr: + case IrInstSrcIdResetResult: + case IrInstSrcIdSuspendBegin: + case IrInstSrcIdSuspendFinish: + case IrInstSrcIdResume: + case IrInstSrcIdAwait: + case IrInstSrcIdSpillBegin: + case IrInstSrcIdWasmMemoryGrow: + return true; + + case IrInstSrcIdPhi: + case IrInstSrcIdUnOp: + case IrInstSrcIdBinOp: + case IrInstSrcIdMergeErrSets: + case IrInstSrcIdLoadPtr: + case IrInstSrcIdConst: + case IrInstSrcIdContainerInitList: + case IrInstSrcIdContainerInitFields: + case IrInstSrcIdUnionInitNamedField: + case IrInstSrcIdFieldPtr: + case IrInstSrcIdElemPtr: + case IrInstSrcIdVarPtr: + case IrInstSrcIdTypeOf: + case IrInstSrcIdArrayType: + case IrInstSrcIdSliceType: + case IrInstSrcIdAnyFrameType: + case IrInstSrcIdSizeOf: + case IrInstSrcIdTestNonNull: + case IrInstSrcIdOptionalUnwrapPtr: + case IrInstSrcIdClz: + case IrInstSrcIdCtz: + case IrInstSrcIdPopCount: + case IrInstSrcIdBswap: + case IrInstSrcIdBitReverse: + case IrInstSrcIdSwitchVar: + case IrInstSrcIdSwitchElseVar: + case IrInstSrcIdSwitchTarget: + case IrInstSrcIdRef: + case IrInstSrcIdEmbedFile: + case IrInstSrcIdTruncate: + case IrInstSrcIdVectorType: + case IrInstSrcIdShuffleVector: + case IrInstSrcIdSplat: + case IrInstSrcIdBoolNot: + case IrInstSrcIdSlice: + case IrInstSrcIdAlignOf: + case IrInstSrcIdReturnAddress: + case IrInstSrcIdFrameAddress: + case IrInstSrcIdFrameHandle: + case IrInstSrcIdFrameType: + case IrInstSrcIdFrameSize: + case IrInstSrcIdTestErr: + case IrInstSrcIdFnProto: + case IrInstSrcIdTestComptime: + case IrInstSrcIdPtrCast: + case IrInstSrcIdBitCast: + case IrInstSrcIdPtrToInt: + case IrInstSrcIdIntToPtr: + case IrInstSrcIdIntToEnum: + case IrInstSrcIdIntToErr: + case IrInstSrcIdErrToInt: + case IrInstSrcIdDeclRef: + case IrInstSrcIdErrName: + case IrInstSrcIdTypeName: + case IrInstSrcIdTagName: + case IrInstSrcIdFieldParentPtr: + case IrInstSrcIdByteOffsetOf: + case IrInstSrcIdBitOffsetOf: + case IrInstSrcIdTypeInfo: + case IrInstSrcIdType: + case IrInstSrcIdHasField: + case IrInstSrcIdAlignCast: + case IrInstSrcIdImplicitCast: + case IrInstSrcIdResolveResult: + case IrInstSrcIdArgType: + case IrInstSrcIdTagType: + case IrInstSrcIdErrorReturnTrace: + case IrInstSrcIdErrorUnion: + case IrInstSrcIdFloatOp: + case IrInstSrcIdMulAdd: + case IrInstSrcIdAtomicLoad: + case IrInstSrcIdIntCast: + case IrInstSrcIdFloatCast: + case IrInstSrcIdErrSetCast: + case IrInstSrcIdIntToFloat: + case IrInstSrcIdFloatToInt: + case IrInstSrcIdBoolToInt: + case IrInstSrcIdEnumToInt: + case IrInstSrcIdHasDecl: + case IrInstSrcIdAlloca: + case IrInstSrcIdSpillEnd: + case IrInstSrcIdWasmMemorySize: + case IrInstSrcIdSrc: + return false; + + case IrInstSrcIdAsm: + { + IrInstSrcAsm *asm_instruction = (IrInstSrcAsm *)instruction; + return asm_instruction->has_side_effects; + } + + case IrInstSrcIdUnwrapErrPayload: + { + IrInstSrcUnwrapErrPayload *unwrap_err_payload_instruction = + (IrInstSrcUnwrapErrPayload *)instruction; + return unwrap_err_payload_instruction->safety_check_on || + unwrap_err_payload_instruction->initializing; + } + case IrInstSrcIdUnwrapErrCode: + return reinterpret_cast(instruction)->initializing; + } + zig_unreachable(); +} + +static ZigType *ir_resolve_lazy_fn_type(IrAnalyze *ira, AstNode *source_node, LazyValueFnType *lazy_fn_type) { + Error err; + AstNode *proto_node = lazy_fn_type->proto_node; + + FnTypeId fn_type_id = {0}; + init_fn_type_id(&fn_type_id, proto_node, lazy_fn_type->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 = proto_node->data.fn_proto.params.at(fn_type_id.next_param_index); + assert(param_node->type == NodeTypeParamDecl); + + bool param_is_var_args = param_node->data.param_decl.is_var_args; + if (param_is_var_args) { + if (fn_type_id.cc == CallingConventionC) { + fn_type_id.param_count = fn_type_id.next_param_index; + break; + } else { + ir_add_error_node(ira, param_node, + buf_sprintf("var args only allowed in functions with C calling convention")); + return nullptr; + } + } + FnTypeParamInfo *param_info = &fn_type_id.param_info[fn_type_id.next_param_index]; + param_info->is_noalias = param_node->data.param_decl.is_noalias; + + if (lazy_fn_type->param_types[fn_type_id.next_param_index] == nullptr) { + param_info->type = nullptr; + return get_generic_fn_type(ira->codegen, &fn_type_id); + } else { + IrInstGen *param_type_inst = lazy_fn_type->param_types[fn_type_id.next_param_index]; + ZigType *param_type = ir_resolve_type(ira, param_type_inst); + if (type_is_invalid(param_type)) + return nullptr; + + if(!is_valid_param_type(param_type)){ + if(param_type->id == ZigTypeIdOpaque){ + ir_add_error(ira, ¶m_type_inst->base, + buf_sprintf("parameter of opaque type '%s' not allowed", buf_ptr(¶m_type->name))); + } else { + ir_add_error(ira, ¶m_type_inst->base, + buf_sprintf("parameter of type '%s' not allowed", buf_ptr(¶m_type->name))); + } + + return nullptr; + } + + switch (type_requires_comptime(ira->codegen, param_type)) { + case ReqCompTimeYes: + if (!calling_convention_allows_zig_types(fn_type_id.cc)) { + ir_add_error(ira, ¶m_type_inst->base, + buf_sprintf("parameter of type '%s' not allowed in function with calling convention '%s'", + buf_ptr(¶m_type->name), calling_convention_name(fn_type_id.cc))); + return nullptr; + } + param_info->type = param_type; + fn_type_id.next_param_index += 1; + return get_generic_fn_type(ira->codegen, &fn_type_id); + case ReqCompTimeInvalid: + return nullptr; + case ReqCompTimeNo: + break; + } + if (!calling_convention_allows_zig_types(fn_type_id.cc)) { + bool has_bits; + if ((err = type_has_bits2(ira->codegen, param_type, &has_bits))) + return nullptr; + if (!has_bits) { + ir_add_error(ira, ¶m_type_inst->base, + buf_sprintf("parameter of type '%s' has 0 bits; not allowed in function with calling convention '%s'", + buf_ptr(¶m_type->name), calling_convention_name(fn_type_id.cc))); + return nullptr; + } + } + param_info->type = param_type; + } + } + + if (lazy_fn_type->align_inst != nullptr) { + if (!ir_resolve_align(ira, lazy_fn_type->align_inst, nullptr, &fn_type_id.alignment)) + return nullptr; + } + + fn_type_id.return_type = ir_resolve_type(ira, lazy_fn_type->return_type); + if (type_is_invalid(fn_type_id.return_type)) + return nullptr; + if (fn_type_id.return_type->id == ZigTypeIdOpaque) { + ir_add_error(ira, &lazy_fn_type->return_type->base, buf_create_from_str("return type cannot be opaque")); + return nullptr; + } + + return get_fn_type(ira->codegen, &fn_type_id); +} + +static Error ir_resolve_lazy_raw(AstNode *source_node, ZigValue *val) { + Error err; + if (val->special != ConstValSpecialLazy) + return ErrorNone; + switch (val->data.x_lazy->id) { + case LazyValueIdInvalid: + zig_unreachable(); + case LazyValueIdTypeInfoDecls: { + LazyValueTypeInfoDecls *type_info_decls = reinterpret_cast(val->data.x_lazy); + IrAnalyze *ira = type_info_decls->ira; + + if ((err = ir_make_type_info_decls(ira, type_info_decls->source_instr, val, type_info_decls->decls_scope, true))) + { + return err; + }; + + // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. + return ErrorNone; + } + case LazyValueIdAlignOf: { + LazyValueAlignOf *lazy_align_of = reinterpret_cast(val->data.x_lazy); + IrAnalyze *ira = lazy_align_of->ira; + + if (lazy_align_of->target_type->value->special == ConstValSpecialStatic) { + switch (lazy_align_of->target_type->value->data.x_type->id) { + case ZigTypeIdInvalid: + zig_unreachable(); + case ZigTypeIdMetaType: + case ZigTypeIdUnreachable: + case ZigTypeIdComptimeFloat: + case ZigTypeIdComptimeInt: + case ZigTypeIdEnumLiteral: + case ZigTypeIdUndefined: + case ZigTypeIdNull: + case ZigTypeIdBoundFn: + case ZigTypeIdVoid: + case ZigTypeIdOpaque: + ir_add_error(ira, &lazy_align_of->target_type->base, + buf_sprintf("no align available for type '%s'", + buf_ptr(&lazy_align_of->target_type->value->data.x_type->name))); + return ErrorSemanticAnalyzeFail; + 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 ZigTypeIdVector: + case ZigTypeIdFnFrame: + case ZigTypeIdAnyFrame: + break; + } + } + + uint32_t align_in_bytes; + if ((err = type_val_resolve_abi_align(ira->codegen, source_node, + lazy_align_of->target_type->value, &align_in_bytes))) + { + return err; + } + + val->special = ConstValSpecialStatic; + assert(val->type->id == ZigTypeIdComptimeInt || val->type->id == ZigTypeIdInt); + bigint_init_unsigned(&val->data.x_bigint, align_in_bytes); + + // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. + return ErrorNone; + } + case LazyValueIdSizeOf: { + LazyValueSizeOf *lazy_size_of = reinterpret_cast(val->data.x_lazy); + IrAnalyze *ira = lazy_size_of->ira; + + if (lazy_size_of->target_type->value->special == ConstValSpecialStatic) { + switch (lazy_size_of->target_type->value->data.x_type->id) { + case ZigTypeIdInvalid: // handled above + zig_unreachable(); + case ZigTypeIdUnreachable: + case ZigTypeIdUndefined: + case ZigTypeIdNull: + case ZigTypeIdBoundFn: + case ZigTypeIdOpaque: + ir_add_error(ira, &lazy_size_of->target_type->base, + buf_sprintf("no size available for type '%s'", + buf_ptr(&lazy_size_of->target_type->value->data.x_type->name))); + return ErrorSemanticAnalyzeFail; + case ZigTypeIdMetaType: + case ZigTypeIdEnumLiteral: + case ZigTypeIdComptimeFloat: + case ZigTypeIdComptimeInt: + 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 ZigTypeIdVector: + case ZigTypeIdFnFrame: + case ZigTypeIdAnyFrame: + break; + } + } + + size_t abi_size; + size_t size_in_bits; + if ((err = type_val_resolve_abi_size(ira->codegen, source_node, lazy_size_of->target_type->value, + &abi_size, &size_in_bits))) + { + return err; + } + + val->special = ConstValSpecialStatic; + assert(val->type->id == ZigTypeIdComptimeInt || val->type->id == ZigTypeIdInt); + if (lazy_size_of->bit_size) + bigint_init_unsigned(&val->data.x_bigint, size_in_bits); + else + bigint_init_unsigned(&val->data.x_bigint, abi_size); + + // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. + return ErrorNone; + } + case LazyValueIdSliceType: { + LazyValueSliceType *lazy_slice_type = reinterpret_cast(val->data.x_lazy); + IrAnalyze *ira = lazy_slice_type->ira; + + ZigType *elem_type = ir_resolve_type(ira, lazy_slice_type->elem_type); + if (type_is_invalid(elem_type)) + return ErrorSemanticAnalyzeFail; + + ZigValue *sentinel_val; + if (lazy_slice_type->sentinel != nullptr) { + if (type_is_invalid(lazy_slice_type->sentinel->value->type)) + return ErrorSemanticAnalyzeFail; + IrInstGen *sentinel = ir_implicit_cast(ira, lazy_slice_type->sentinel, elem_type); + if (type_is_invalid(sentinel->value->type)) + return ErrorSemanticAnalyzeFail; + sentinel_val = ir_resolve_const(ira, sentinel, UndefBad); + if (sentinel_val == nullptr) + return ErrorSemanticAnalyzeFail; + } else { + sentinel_val = nullptr; + } + + uint32_t align_bytes = 0; + if (lazy_slice_type->align_inst != nullptr) { + if (!ir_resolve_align(ira, lazy_slice_type->align_inst, elem_type, &align_bytes)) + return ErrorSemanticAnalyzeFail; + } + + switch (elem_type->id) { + case ZigTypeIdInvalid: // handled above + zig_unreachable(); + case ZigTypeIdUnreachable: + case ZigTypeIdUndefined: + case ZigTypeIdNull: + case ZigTypeIdOpaque: + ir_add_error(ira, &lazy_slice_type->elem_type->base, + buf_sprintf("slice of type '%s' not allowed", buf_ptr(&elem_type->name))); + return ErrorSemanticAnalyzeFail; + case ZigTypeIdMetaType: + case ZigTypeIdVoid: + case ZigTypeIdBool: + case ZigTypeIdInt: + case ZigTypeIdFloat: + case ZigTypeIdPointer: + case ZigTypeIdArray: + case ZigTypeIdStruct: + case ZigTypeIdComptimeFloat: + case ZigTypeIdComptimeInt: + case ZigTypeIdEnumLiteral: + case ZigTypeIdOptional: + case ZigTypeIdErrorUnion: + case ZigTypeIdErrorSet: + case ZigTypeIdEnum: + case ZigTypeIdUnion: + case ZigTypeIdFn: + case ZigTypeIdBoundFn: + case ZigTypeIdVector: + case ZigTypeIdFnFrame: + case ZigTypeIdAnyFrame: + break; + } + + ResolveStatus needed_status = (align_bytes == 0) ? + ResolveStatusZeroBitsKnown : ResolveStatusAlignmentKnown; + if ((err = type_resolve(ira->codegen, elem_type, needed_status))) + return err; + ZigType *slice_ptr_type = get_pointer_to_type_extra2(ira->codegen, elem_type, + lazy_slice_type->is_const, lazy_slice_type->is_volatile, + PtrLenUnknown, + align_bytes, + 0, 0, lazy_slice_type->is_allowzero, + VECTOR_INDEX_NONE, nullptr, sentinel_val); + val->special = ConstValSpecialStatic; + assert(val->type->id == ZigTypeIdMetaType); + val->data.x_type = get_slice_type(ira->codegen, slice_ptr_type); + + // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. + return ErrorNone; + } + case LazyValueIdPtrType: { + LazyValuePtrType *lazy_ptr_type = reinterpret_cast(val->data.x_lazy); + IrAnalyze *ira = lazy_ptr_type->ira; + + ZigType *elem_type = ir_resolve_type(ira, lazy_ptr_type->elem_type); + if (type_is_invalid(elem_type)) + return ErrorSemanticAnalyzeFail; + + ZigValue *sentinel_val; + if (lazy_ptr_type->sentinel != nullptr) { + if (type_is_invalid(lazy_ptr_type->sentinel->value->type)) + return ErrorSemanticAnalyzeFail; + IrInstGen *sentinel = ir_implicit_cast(ira, lazy_ptr_type->sentinel, elem_type); + if (type_is_invalid(sentinel->value->type)) + return ErrorSemanticAnalyzeFail; + sentinel_val = ir_resolve_const(ira, sentinel, UndefBad); + if (sentinel_val == nullptr) + return ErrorSemanticAnalyzeFail; + } else { + sentinel_val = nullptr; + } + + uint32_t align_bytes = 0; + if (lazy_ptr_type->align_inst != nullptr) { + if (!ir_resolve_align(ira, lazy_ptr_type->align_inst, elem_type, &align_bytes)) + return ErrorSemanticAnalyzeFail; + } + + if (elem_type->id == ZigTypeIdUnreachable) { + ir_add_error(ira, &lazy_ptr_type->elem_type->base, + buf_create_from_str("pointer to noreturn not allowed")); + return ErrorSemanticAnalyzeFail; + } else if (elem_type->id == ZigTypeIdOpaque && lazy_ptr_type->ptr_len == PtrLenUnknown) { + ir_add_error(ira, &lazy_ptr_type->elem_type->base, + buf_create_from_str("unknown-length pointer to opaque")); + return ErrorSemanticAnalyzeFail; + } else if (lazy_ptr_type->ptr_len == PtrLenC) { + bool ok_type; + if ((err = type_allowed_in_extern(ira->codegen, elem_type, &ok_type))) + return err; + if (!ok_type) { + ir_add_error(ira, &lazy_ptr_type->elem_type->base, + buf_sprintf("C pointers cannot point to non-C-ABI-compatible type '%s'", + buf_ptr(&elem_type->name))); + return ErrorSemanticAnalyzeFail; + } else if (elem_type->id == ZigTypeIdOpaque) { + ir_add_error(ira, &lazy_ptr_type->elem_type->base, + buf_sprintf("C pointers cannot point to opaque types")); + return ErrorSemanticAnalyzeFail; + } else if (lazy_ptr_type->is_allowzero) { + ir_add_error(ira, &lazy_ptr_type->elem_type->base, + buf_sprintf("C pointers always allow address zero")); + return ErrorSemanticAnalyzeFail; + } + } + + if (align_bytes != 0) { + if ((err = type_resolve(ira->codegen, elem_type, ResolveStatusAlignmentKnown))) + return err; + if (!type_has_bits(ira->codegen, elem_type)) + align_bytes = 0; + } + bool allow_zero = lazy_ptr_type->is_allowzero || lazy_ptr_type->ptr_len == PtrLenC; + assert(val->type->id == ZigTypeIdMetaType); + val->data.x_type = get_pointer_to_type_extra2(ira->codegen, elem_type, + lazy_ptr_type->is_const, lazy_ptr_type->is_volatile, lazy_ptr_type->ptr_len, align_bytes, + lazy_ptr_type->bit_offset_in_host, lazy_ptr_type->host_int_bytes, + allow_zero, VECTOR_INDEX_NONE, nullptr, sentinel_val); + val->special = ConstValSpecialStatic; + + // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. + return ErrorNone; + } + case LazyValueIdArrayType: { + LazyValueArrayType *lazy_array_type = reinterpret_cast(val->data.x_lazy); + IrAnalyze *ira = lazy_array_type->ira; + + ZigType *elem_type = ir_resolve_type(ira, lazy_array_type->elem_type); + if (type_is_invalid(elem_type)) + return ErrorSemanticAnalyzeFail; + + switch (elem_type->id) { + case ZigTypeIdInvalid: // handled above + zig_unreachable(); + case ZigTypeIdUnreachable: + case ZigTypeIdUndefined: + case ZigTypeIdNull: + case ZigTypeIdOpaque: + ir_add_error(ira, &lazy_array_type->elem_type->base, + buf_sprintf("array of type '%s' not allowed", + buf_ptr(&elem_type->name))); + return ErrorSemanticAnalyzeFail; + case ZigTypeIdMetaType: + case ZigTypeIdVoid: + case ZigTypeIdBool: + case ZigTypeIdInt: + case ZigTypeIdFloat: + case ZigTypeIdPointer: + case ZigTypeIdArray: + case ZigTypeIdStruct: + case ZigTypeIdComptimeFloat: + case ZigTypeIdComptimeInt: + case ZigTypeIdEnumLiteral: + case ZigTypeIdOptional: + case ZigTypeIdErrorUnion: + case ZigTypeIdErrorSet: + case ZigTypeIdEnum: + case ZigTypeIdUnion: + case ZigTypeIdFn: + case ZigTypeIdBoundFn: + case ZigTypeIdVector: + case ZigTypeIdFnFrame: + case ZigTypeIdAnyFrame: + break; + } + + if ((err = type_resolve(ira->codegen, elem_type, ResolveStatusSizeKnown))) + return err; + + ZigValue *sentinel_val = nullptr; + if (lazy_array_type->sentinel != nullptr) { + if (type_is_invalid(lazy_array_type->sentinel->value->type)) + return ErrorSemanticAnalyzeFail; + IrInstGen *sentinel = ir_implicit_cast(ira, lazy_array_type->sentinel, elem_type); + if (type_is_invalid(sentinel->value->type)) + return ErrorSemanticAnalyzeFail; + sentinel_val = ir_resolve_const(ira, sentinel, UndefBad); + if (sentinel_val == nullptr) + return ErrorSemanticAnalyzeFail; + } + + assert(val->type->id == ZigTypeIdMetaType); + val->data.x_type = get_array_type(ira->codegen, elem_type, lazy_array_type->length, sentinel_val); + val->special = ConstValSpecialStatic; + + // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. + return ErrorNone; + } + case LazyValueIdOptType: { + LazyValueOptType *lazy_opt_type = reinterpret_cast(val->data.x_lazy); + IrAnalyze *ira = lazy_opt_type->ira; + + ZigType *payload_type = ir_resolve_type(ira, lazy_opt_type->payload_type); + if (type_is_invalid(payload_type)) + return ErrorSemanticAnalyzeFail; + + if (payload_type->id == ZigTypeIdOpaque || payload_type->id == ZigTypeIdUnreachable) { + ir_add_error(ira, &lazy_opt_type->payload_type->base, + buf_sprintf("type '%s' cannot be optional", buf_ptr(&payload_type->name))); + return ErrorSemanticAnalyzeFail; + } + + if ((err = type_resolve(ira->codegen, payload_type, ResolveStatusSizeKnown))) + return err; + + assert(val->type->id == ZigTypeIdMetaType); + val->data.x_type = get_optional_type(ira->codegen, payload_type); + val->special = ConstValSpecialStatic; + + // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. + return ErrorNone; + } + case LazyValueIdFnType: { + LazyValueFnType *lazy_fn_type = reinterpret_cast(val->data.x_lazy); + IrAnalyze *ira = lazy_fn_type->ira; + ZigType *fn_type = ir_resolve_lazy_fn_type(ira, source_node, lazy_fn_type); + if (fn_type == nullptr) + return ErrorSemanticAnalyzeFail; + val->special = ConstValSpecialStatic; + assert(val->type->id == ZigTypeIdMetaType); + val->data.x_type = fn_type; + + // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. + return ErrorNone; + } + case LazyValueIdErrUnionType: { + LazyValueErrUnionType *lazy_err_union_type = + reinterpret_cast(val->data.x_lazy); + IrAnalyze *ira = lazy_err_union_type->ira; + + ZigType *err_set_type = ir_resolve_type(ira, lazy_err_union_type->err_set_type); + if (type_is_invalid(err_set_type)) + return ErrorSemanticAnalyzeFail; + + ZigType *payload_type = ir_resolve_type(ira, lazy_err_union_type->payload_type); + if (type_is_invalid(payload_type)) + return ErrorSemanticAnalyzeFail; + + if (err_set_type->id != ZigTypeIdErrorSet) { + ir_add_error(ira, &lazy_err_union_type->err_set_type->base, + buf_sprintf("expected error set type, found type '%s'", + buf_ptr(&err_set_type->name))); + return ErrorSemanticAnalyzeFail; + } + + if ((err = type_resolve(ira->codegen, payload_type, ResolveStatusSizeKnown))) + return ErrorSemanticAnalyzeFail; + + assert(val->type->id == ZigTypeIdMetaType); + val->data.x_type = get_error_union_type(ira->codegen, err_set_type, payload_type); + val->special = ConstValSpecialStatic; + + // We can't free the lazy value here, because multiple other ZigValues might be pointing to it. + return ErrorNone; + } + } + zig_unreachable(); +} + +Error ir_resolve_lazy(CodeGen *codegen, AstNode *source_node, ZigValue *val) { + Error err; + if ((err = ir_resolve_lazy_raw(source_node, val))) { + if (codegen->trace_err != nullptr && source_node != nullptr && !source_node->already_traced_this_node) { + source_node->already_traced_this_node = true; + codegen->trace_err = add_error_note(codegen, codegen->trace_err, source_node, + buf_create_from_str("referenced here")); + } + return err; + } + if (type_is_invalid(val->type)) { + return ErrorSemanticAnalyzeFail; + } + return ErrorNone; +} + +void IrInst::src() { + IrInst *inst = this; + if (inst->source_node != nullptr) { + inst->source_node->src(); + } else { + fprintf(stderr, "(null source node)\n"); + } +} + +void IrInst::dump() { + this->src(); + fprintf(stderr, "IrInst(#%" PRIu32 ")\n", this->debug_id); +} + +void IrInstSrc::src() { + this->base.src(); +} + +void IrInstGen::src() { + this->base.src(); +} + +void IrInstSrc::dump() { + IrInstSrc *inst = this; + inst->src(); + if (inst->base.scope == nullptr) { + fprintf(stderr, "(null scope)\n"); + } else { + ir_print_inst_src(inst->base.scope->codegen, stderr, inst, 0); + fprintf(stderr, "-> "); + ir_print_inst_gen(inst->base.scope->codegen, stderr, inst->child, 0); + } +} +void IrInstGen::dump() { + IrInstGen *inst = this; + inst->src(); + if (inst->base.scope == nullptr) { + fprintf(stderr, "(null scope)\n"); + } else { + ir_print_inst_gen(inst->base.scope->codegen, stderr, inst, 0); + } +} + +void IrAnalyze::dump() { + ir_print_gen(this->codegen, stderr, this->new_irb.exec, 0); + if (this->new_irb.current_basic_block != nullptr) { + fprintf(stderr, "Current basic block:\n"); + ir_print_basic_block_gen(this->codegen, stderr, this->new_irb.current_basic_block, 1); + } +} + +void dbg_ir_break(const char *src_file, uint32_t line) { + dbg_ir_breakpoints_buf[dbg_ir_breakpoints_count] = {src_file, line}; + dbg_ir_breakpoints_count += 1; +} +void dbg_ir_clear(void) { + dbg_ir_breakpoints_count = 0; +} diff --git a/src/stage1/ir.hpp b/src/stage1/ir.hpp new file mode 100644 index 0000000000000000000000000000000000000000..368677128754a5a4ce2ab91b40645410944b7610 --- /dev/null +++ b/src/stage1/ir.hpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2016 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef ZIG_IR_HPP +#define ZIG_IR_HPP + +#include "all_types.hpp" + +bool ir_gen(CodeGen *g, AstNode *node, Scope *scope, IrExecutableSrc *ir_executable); +bool ir_gen_fn(CodeGen *g, ZigFn *fn_entry); + +IrInstGen *ir_create_alloca(CodeGen *g, Scope *scope, AstNode *source_node, ZigFn *fn, + ZigType *var_type, const char *name_hint); + +Error ir_eval_const_value(CodeGen *codegen, Scope *scope, AstNode *node, + ZigValue *return_ptr, size_t *backward_branch_count, size_t *backward_branch_quota, + ZigFn *fn_entry, Buf *c_import_buf, AstNode *source_node, Buf *exec_name, + IrExecutableGen *parent_exec, AstNode *expected_type_source_node, UndefAllowed undef); + +Error ir_resolve_lazy(CodeGen *codegen, AstNode *source_node, ZigValue *val); + +ZigType *ir_analyze(CodeGen *g, IrExecutableSrc *old_executable, IrExecutableGen *new_executable, + ZigType *expected_type, AstNode *expected_type_source_node, ZigValue *return_ptr); + +bool ir_inst_gen_has_side_effects(IrInstGen *inst); +bool ir_inst_src_has_side_effects(IrInstSrc *inst); + +struct IrAnalyze; +ZigValue *const_ptr_pointee(IrAnalyze *ira, CodeGen *codegen, ZigValue *const_val, + AstNode *source_node); + +// for debugging purposes +void dbg_ir_break(const char *src_file, uint32_t line); +void dbg_ir_clear(void); + +#endif diff --git a/src/stage1/ir_print.cpp b/src/stage1/ir_print.cpp new file mode 100644 index 0000000000000000000000000000000000000000..18c2ca99f76dd40a77a7e5e994f51120a9a7e4ff --- /dev/null +++ b/src/stage1/ir_print.cpp @@ -0,0 +1,3376 @@ +/* + * Copyright (c) 2016 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#include "all_types.hpp" +#include "analyze.hpp" +#include "ir.hpp" +#include "ir_print.hpp" +#include "os.hpp" + +static uint32_t hash_inst_src_ptr(IrInstSrc* instruction) { + return (uint32_t)(uintptr_t)instruction; +} + +static uint32_t hash_inst_gen_ptr(IrInstGen* instruction) { + return (uint32_t)(uintptr_t)instruction; +} + +static bool inst_src_ptr_eql(IrInstSrc* a, IrInstSrc* b) { + return a == b; +} + +static bool inst_gen_ptr_eql(IrInstGen* a, IrInstGen* b) { + return a == b; +} + +using InstSetSrc = HashMap; +using InstSetGen = HashMap; +using InstListSrc = ZigList; +using InstListGen = ZigList; + +struct IrPrintSrc { + CodeGen *codegen; + FILE *f; + int indent; + int indent_size; +}; + +struct IrPrintGen { + CodeGen *codegen; + FILE *f; + int indent; + int indent_size; + + // When printing pass 2 instructions referenced var instructions are not + // present in the instruction list. Thus we track which instructions + // are printed (per executable) and after each pass 2 instruction those + // var instructions are rendered in a trailing fashion. + InstSetGen printed; + InstListGen pending; +}; + +static void ir_print_other_inst_src(IrPrintSrc *irp, IrInstSrc *inst); +static void ir_print_other_inst_gen(IrPrintGen *irp, IrInstGen *inst); + +static void ir_print_call_modifier(FILE *f, CallModifier modifier) { + switch (modifier) { + case CallModifierNone: + break; + case CallModifierNoSuspend: + fprintf(f, "nosuspend "); + break; + case CallModifierAsync: + fprintf(f, "async "); + break; + case CallModifierNeverTail: + fprintf(f, "notail "); + break; + case CallModifierNeverInline: + fprintf(f, "noinline "); + break; + case CallModifierAlwaysTail: + fprintf(f, "tail "); + break; + case CallModifierAlwaysInline: + fprintf(f, "inline "); + break; + case CallModifierCompileTime: + fprintf(f, "comptime "); + break; + case CallModifierBuiltin: + zig_unreachable(); + } +} + +const char* ir_inst_src_type_str(IrInstSrcId id) { + switch (id) { + case IrInstSrcIdInvalid: + return "SrcInvalid"; + case IrInstSrcIdShuffleVector: + return "SrcShuffle"; + case IrInstSrcIdSplat: + return "SrcSplat"; + case IrInstSrcIdDeclVar: + return "SrcDeclVar"; + case IrInstSrcIdBr: + return "SrcBr"; + case IrInstSrcIdCondBr: + return "SrcCondBr"; + case IrInstSrcIdSwitchBr: + return "SrcSwitchBr"; + case IrInstSrcIdSwitchVar: + return "SrcSwitchVar"; + case IrInstSrcIdSwitchElseVar: + return "SrcSwitchElseVar"; + case IrInstSrcIdSwitchTarget: + return "SrcSwitchTarget"; + case IrInstSrcIdPhi: + return "SrcPhi"; + case IrInstSrcIdUnOp: + return "SrcUnOp"; + case IrInstSrcIdBinOp: + return "SrcBinOp"; + case IrInstSrcIdMergeErrSets: + return "SrcMergeErrSets"; + case IrInstSrcIdLoadPtr: + return "SrcLoadPtr"; + case IrInstSrcIdStorePtr: + return "SrcStorePtr"; + case IrInstSrcIdFieldPtr: + return "SrcFieldPtr"; + case IrInstSrcIdElemPtr: + return "SrcElemPtr"; + case IrInstSrcIdVarPtr: + return "SrcVarPtr"; + case IrInstSrcIdCallExtra: + return "SrcCallExtra"; + case IrInstSrcIdAsyncCallExtra: + return "SrcAsyncCallExtra"; + case IrInstSrcIdCall: + return "SrcCall"; + case IrInstSrcIdCallArgs: + return "SrcCallArgs"; + case IrInstSrcIdConst: + return "SrcConst"; + case IrInstSrcIdReturn: + return "SrcReturn"; + case IrInstSrcIdContainerInitList: + return "SrcContainerInitList"; + case IrInstSrcIdContainerInitFields: + return "SrcContainerInitFields"; + case IrInstSrcIdUnreachable: + return "SrcUnreachable"; + case IrInstSrcIdTypeOf: + return "SrcTypeOf"; + case IrInstSrcIdSetCold: + return "SrcSetCold"; + case IrInstSrcIdSetRuntimeSafety: + return "SrcSetRuntimeSafety"; + case IrInstSrcIdSetFloatMode: + return "SrcSetFloatMode"; + case IrInstSrcIdArrayType: + return "SrcArrayType"; + case IrInstSrcIdAnyFrameType: + return "SrcAnyFrameType"; + case IrInstSrcIdSliceType: + return "SrcSliceType"; + case IrInstSrcIdAsm: + return "SrcAsm"; + case IrInstSrcIdSizeOf: + return "SrcSizeOf"; + case IrInstSrcIdTestNonNull: + return "SrcTestNonNull"; + case IrInstSrcIdOptionalUnwrapPtr: + return "SrcOptionalUnwrapPtr"; + case IrInstSrcIdClz: + return "SrcClz"; + case IrInstSrcIdCtz: + return "SrcCtz"; + case IrInstSrcIdPopCount: + return "SrcPopCount"; + case IrInstSrcIdBswap: + return "SrcBswap"; + case IrInstSrcIdBitReverse: + return "SrcBitReverse"; + case IrInstSrcIdImport: + return "SrcImport"; + case IrInstSrcIdCImport: + return "SrcCImport"; + case IrInstSrcIdCInclude: + return "SrcCInclude"; + case IrInstSrcIdCDefine: + return "SrcCDefine"; + case IrInstSrcIdCUndef: + return "SrcCUndef"; + case IrInstSrcIdRef: + return "SrcRef"; + case IrInstSrcIdCompileErr: + return "SrcCompileErr"; + case IrInstSrcIdCompileLog: + return "SrcCompileLog"; + case IrInstSrcIdErrName: + return "SrcErrName"; + case IrInstSrcIdEmbedFile: + return "SrcEmbedFile"; + case IrInstSrcIdCmpxchg: + return "SrcCmpxchg"; + case IrInstSrcIdFence: + return "SrcFence"; + case IrInstSrcIdTruncate: + return "SrcTruncate"; + case IrInstSrcIdIntCast: + return "SrcIntCast"; + case IrInstSrcIdFloatCast: + return "SrcFloatCast"; + case IrInstSrcIdIntToFloat: + return "SrcIntToFloat"; + case IrInstSrcIdFloatToInt: + return "SrcFloatToInt"; + case IrInstSrcIdBoolToInt: + return "SrcBoolToInt"; + case IrInstSrcIdVectorType: + return "SrcVectorType"; + case IrInstSrcIdBoolNot: + return "SrcBoolNot"; + case IrInstSrcIdMemset: + return "SrcMemset"; + case IrInstSrcIdMemcpy: + return "SrcMemcpy"; + case IrInstSrcIdSlice: + return "SrcSlice"; + case IrInstSrcIdBreakpoint: + return "SrcBreakpoint"; + case IrInstSrcIdReturnAddress: + return "SrcReturnAddress"; + case IrInstSrcIdFrameAddress: + return "SrcFrameAddress"; + case IrInstSrcIdFrameHandle: + return "SrcFrameHandle"; + case IrInstSrcIdFrameType: + return "SrcFrameType"; + case IrInstSrcIdFrameSize: + return "SrcFrameSize"; + case IrInstSrcIdAlignOf: + return "SrcAlignOf"; + case IrInstSrcIdOverflowOp: + return "SrcOverflowOp"; + case IrInstSrcIdTestErr: + return "SrcTestErr"; + case IrInstSrcIdMulAdd: + return "SrcMulAdd"; + case IrInstSrcIdFloatOp: + return "SrcFloatOp"; + case IrInstSrcIdUnwrapErrCode: + return "SrcUnwrapErrCode"; + case IrInstSrcIdUnwrapErrPayload: + return "SrcUnwrapErrPayload"; + case IrInstSrcIdFnProto: + return "SrcFnProto"; + case IrInstSrcIdTestComptime: + return "SrcTestComptime"; + case IrInstSrcIdPtrCast: + return "SrcPtrCast"; + case IrInstSrcIdBitCast: + return "SrcBitCast"; + case IrInstSrcIdIntToPtr: + return "SrcIntToPtr"; + case IrInstSrcIdPtrToInt: + return "SrcPtrToInt"; + case IrInstSrcIdIntToEnum: + return "SrcIntToEnum"; + case IrInstSrcIdEnumToInt: + return "SrcEnumToInt"; + case IrInstSrcIdIntToErr: + return "SrcIntToErr"; + case IrInstSrcIdErrToInt: + return "SrcErrToInt"; + case IrInstSrcIdCheckSwitchProngs: + return "SrcCheckSwitchProngs"; + case IrInstSrcIdCheckStatementIsVoid: + return "SrcCheckStatementIsVoid"; + case IrInstSrcIdTypeName: + return "SrcTypeName"; + case IrInstSrcIdDeclRef: + return "SrcDeclRef"; + case IrInstSrcIdPanic: + return "SrcPanic"; + case IrInstSrcIdTagName: + return "SrcTagName"; + case IrInstSrcIdTagType: + return "SrcTagType"; + case IrInstSrcIdFieldParentPtr: + return "SrcFieldParentPtr"; + case IrInstSrcIdByteOffsetOf: + return "SrcByteOffsetOf"; + case IrInstSrcIdBitOffsetOf: + return "SrcBitOffsetOf"; + case IrInstSrcIdTypeInfo: + return "SrcTypeInfo"; + case IrInstSrcIdType: + return "SrcType"; + case IrInstSrcIdHasField: + return "SrcHasField"; + case IrInstSrcIdSetEvalBranchQuota: + return "SrcSetEvalBranchQuota"; + case IrInstSrcIdPtrType: + return "SrcPtrType"; + case IrInstSrcIdAlignCast: + return "SrcAlignCast"; + case IrInstSrcIdImplicitCast: + return "SrcImplicitCast"; + case IrInstSrcIdResolveResult: + return "SrcResolveResult"; + case IrInstSrcIdResetResult: + return "SrcResetResult"; + case IrInstSrcIdSetAlignStack: + return "SrcSetAlignStack"; + case IrInstSrcIdArgType: + return "SrcArgType"; + case IrInstSrcIdExport: + return "SrcExport"; + case IrInstSrcIdErrorReturnTrace: + return "SrcErrorReturnTrace"; + case IrInstSrcIdErrorUnion: + return "SrcErrorUnion"; + case IrInstSrcIdAtomicRmw: + return "SrcAtomicRmw"; + case IrInstSrcIdAtomicLoad: + return "SrcAtomicLoad"; + case IrInstSrcIdAtomicStore: + return "SrcAtomicStore"; + case IrInstSrcIdSaveErrRetAddr: + return "SrcSaveErrRetAddr"; + case IrInstSrcIdAddImplicitReturnType: + return "SrcAddImplicitReturnType"; + case IrInstSrcIdErrSetCast: + return "SrcErrSetCast"; + case IrInstSrcIdCheckRuntimeScope: + return "SrcCheckRuntimeScope"; + case IrInstSrcIdHasDecl: + return "SrcHasDecl"; + case IrInstSrcIdUndeclaredIdent: + return "SrcUndeclaredIdent"; + case IrInstSrcIdAlloca: + return "SrcAlloca"; + case IrInstSrcIdEndExpr: + return "SrcEndExpr"; + case IrInstSrcIdUnionInitNamedField: + return "SrcUnionInitNamedField"; + case IrInstSrcIdSuspendBegin: + return "SrcSuspendBegin"; + case IrInstSrcIdSuspendFinish: + return "SrcSuspendFinish"; + case IrInstSrcIdAwait: + return "SrcAwaitSr"; + case IrInstSrcIdResume: + return "SrcResume"; + case IrInstSrcIdSpillBegin: + return "SrcSpillBegin"; + case IrInstSrcIdSpillEnd: + return "SrcSpillEnd"; + case IrInstSrcIdWasmMemorySize: + return "SrcWasmMemorySize"; + case IrInstSrcIdWasmMemoryGrow: + return "SrcWasmMemoryGrow"; + case IrInstSrcIdSrc: + return "SrcSrc"; + } + zig_unreachable(); +} + +const char* ir_inst_gen_type_str(IrInstGenId id) { + switch (id) { + case IrInstGenIdInvalid: + return "GenInvalid"; + case IrInstGenIdShuffleVector: + return "GenShuffle"; + case IrInstGenIdSplat: + return "GenSplat"; + case IrInstGenIdDeclVar: + return "GenDeclVar"; + case IrInstGenIdBr: + return "GenBr"; + case IrInstGenIdCondBr: + return "GenCondBr"; + case IrInstGenIdSwitchBr: + return "GenSwitchBr"; + case IrInstGenIdPhi: + return "GenPhi"; + case IrInstGenIdBinOp: + return "GenBinOp"; + case IrInstGenIdLoadPtr: + return "GenLoadPtr"; + case IrInstGenIdStorePtr: + return "GenStorePtr"; + case IrInstGenIdVectorStoreElem: + return "GenVectorStoreElem"; + case IrInstGenIdStructFieldPtr: + return "GenStructFieldPtr"; + case IrInstGenIdUnionFieldPtr: + return "GenUnionFieldPtr"; + case IrInstGenIdElemPtr: + return "GenElemPtr"; + case IrInstGenIdVarPtr: + return "GenVarPtr"; + case IrInstGenIdReturnPtr: + return "GenReturnPtr"; + case IrInstGenIdCall: + return "GenCall"; + case IrInstGenIdConst: + return "GenConst"; + case IrInstGenIdReturn: + return "GenReturn"; + case IrInstGenIdCast: + return "GenCast"; + case IrInstGenIdUnreachable: + return "GenUnreachable"; + case IrInstGenIdAsm: + return "GenAsm"; + case IrInstGenIdTestNonNull: + return "GenTestNonNull"; + case IrInstGenIdOptionalUnwrapPtr: + return "GenOptionalUnwrapPtr"; + case IrInstGenIdOptionalWrap: + return "GenOptionalWrap"; + case IrInstGenIdUnionTag: + return "GenUnionTag"; + case IrInstGenIdClz: + return "GenClz"; + case IrInstGenIdCtz: + return "GenCtz"; + case IrInstGenIdPopCount: + return "GenPopCount"; + case IrInstGenIdBswap: + return "GenBswap"; + case IrInstGenIdBitReverse: + return "GenBitReverse"; + case IrInstGenIdRef: + return "GenRef"; + case IrInstGenIdErrName: + return "GenErrName"; + case IrInstGenIdCmpxchg: + return "GenCmpxchg"; + case IrInstGenIdFence: + return "GenFence"; + case IrInstGenIdTruncate: + return "GenTruncate"; + case IrInstGenIdBoolNot: + return "GenBoolNot"; + case IrInstGenIdMemset: + return "GenMemset"; + case IrInstGenIdMemcpy: + return "GenMemcpy"; + case IrInstGenIdSlice: + return "GenSlice"; + case IrInstGenIdBreakpoint: + return "GenBreakpoint"; + case IrInstGenIdReturnAddress: + return "GenReturnAddress"; + case IrInstGenIdFrameAddress: + return "GenFrameAddress"; + case IrInstGenIdFrameHandle: + return "GenFrameHandle"; + case IrInstGenIdFrameSize: + return "GenFrameSize"; + case IrInstGenIdOverflowOp: + return "GenOverflowOp"; + case IrInstGenIdTestErr: + return "GenTestErr"; + case IrInstGenIdMulAdd: + return "GenMulAdd"; + case IrInstGenIdFloatOp: + return "GenFloatOp"; + case IrInstGenIdUnwrapErrCode: + return "GenUnwrapErrCode"; + case IrInstGenIdUnwrapErrPayload: + return "GenUnwrapErrPayload"; + case IrInstGenIdErrWrapCode: + return "GenErrWrapCode"; + case IrInstGenIdErrWrapPayload: + return "GenErrWrapPayload"; + case IrInstGenIdPtrCast: + return "GenPtrCast"; + case IrInstGenIdBitCast: + return "GenBitCast"; + case IrInstGenIdWidenOrShorten: + return "GenWidenOrShorten"; + case IrInstGenIdIntToPtr: + return "GenIntToPtr"; + case IrInstGenIdPtrToInt: + return "GenPtrToInt"; + case IrInstGenIdIntToEnum: + return "GenIntToEnum"; + case IrInstGenIdIntToErr: + return "GenIntToErr"; + case IrInstGenIdErrToInt: + return "GenErrToInt"; + case IrInstGenIdPanic: + return "GenPanic"; + case IrInstGenIdTagName: + return "GenTagName"; + case IrInstGenIdFieldParentPtr: + return "GenFieldParentPtr"; + case IrInstGenIdAlignCast: + return "GenAlignCast"; + case IrInstGenIdErrorReturnTrace: + return "GenErrorReturnTrace"; + case IrInstGenIdAtomicRmw: + return "GenAtomicRmw"; + case IrInstGenIdAtomicLoad: + return "GenAtomicLoad"; + case IrInstGenIdAtomicStore: + return "GenAtomicStore"; + case IrInstGenIdSaveErrRetAddr: + return "GenSaveErrRetAddr"; + case IrInstGenIdVectorToArray: + return "GenVectorToArray"; + case IrInstGenIdArrayToVector: + return "GenArrayToVector"; + case IrInstGenIdAssertZero: + return "GenAssertZero"; + case IrInstGenIdAssertNonNull: + return "GenAssertNonNull"; + case IrInstGenIdAlloca: + return "GenAlloca"; + case IrInstGenIdPtrOfArrayToSlice: + return "GenPtrOfArrayToSlice"; + case IrInstGenIdSuspendBegin: + return "GenSuspendBegin"; + case IrInstGenIdSuspendFinish: + return "GenSuspendFinish"; + case IrInstGenIdAwait: + return "GenAwait"; + case IrInstGenIdResume: + return "GenResume"; + case IrInstGenIdSpillBegin: + return "GenSpillBegin"; + case IrInstGenIdSpillEnd: + return "GenSpillEnd"; + case IrInstGenIdVectorExtractElem: + return "GenVectorExtractElem"; + case IrInstGenIdBinaryNot: + return "GenBinaryNot"; + case IrInstGenIdNegation: + return "GenNegation"; + case IrInstGenIdNegationWrapping: + return "GenNegationWrapping"; + case IrInstGenIdWasmMemorySize: + return "GenWasmMemorySize"; + case IrInstGenIdWasmMemoryGrow: + return "GenWasmMemoryGrow"; + } + zig_unreachable(); +} + +static void ir_print_indent_src(IrPrintSrc *irp) { + for (int i = 0; i < irp->indent; i += 1) { + fprintf(irp->f, " "); + } +} + +static void ir_print_indent_gen(IrPrintGen *irp) { + for (int i = 0; i < irp->indent; i += 1) { + fprintf(irp->f, " "); + } +} + +static void ir_print_prefix_src(IrPrintSrc *irp, IrInstSrc *instruction, bool trailing) { + ir_print_indent_src(irp); + const char mark = trailing ? ':' : '#'; + const char *type_name; + if (instruction->id == IrInstSrcIdConst) { + type_name = buf_ptr(&reinterpret_cast(instruction)->value->type->name); + } else if (instruction->is_noreturn) { + type_name = "noreturn"; + } else { + type_name = "(unknown)"; + } + const char *ref_count = ir_inst_src_has_side_effects(instruction) ? + "-" : buf_ptr(buf_sprintf("%" PRIu32 "", instruction->base.ref_count)); + fprintf(irp->f, "%c%-3" PRIu32 "| %-22s| %-12s| %-2s| ", mark, instruction->base.debug_id, + ir_inst_src_type_str(instruction->id), type_name, ref_count); +} + +static void ir_print_prefix_gen(IrPrintGen *irp, IrInstGen *instruction, bool trailing) { + ir_print_indent_gen(irp); + const char mark = trailing ? ':' : '#'; + const char *type_name = instruction->value->type ? buf_ptr(&instruction->value->type->name) : "(unknown)"; + const char *ref_count = ir_inst_gen_has_side_effects(instruction) ? + "-" : buf_ptr(buf_sprintf("%" PRIu32 "", instruction->base.ref_count)); + fprintf(irp->f, "%c%-3" PRIu32 "| %-22s| %-12s| %-2s| ", mark, instruction->base.debug_id, + ir_inst_gen_type_str(instruction->id), type_name, ref_count); +} + +static void ir_print_var_src(IrPrintSrc *irp, IrInstSrc *inst) { + fprintf(irp->f, "#%" PRIu32 "", inst->base.debug_id); +} + +static void ir_print_var_gen(IrPrintGen *irp, IrInstGen *inst) { + fprintf(irp->f, "#%" PRIu32 "", inst->base.debug_id); + if (irp->printed.maybe_get(inst) == nullptr) { + irp->printed.put(inst, 0); + irp->pending.append(inst); + } +} + +static void ir_print_other_inst_src(IrPrintSrc *irp, IrInstSrc *inst) { + if (inst == nullptr) { + fprintf(irp->f, "(null)"); + return; + } + ir_print_var_src(irp, inst); +} + +static void ir_print_const_value(CodeGen *g, FILE *f, ZigValue *const_val) { + Buf buf = BUF_INIT; + buf_resize(&buf, 0); + render_const_value(g, &buf, const_val); + fprintf(f, "%s", buf_ptr(&buf)); +} + +static void ir_print_other_inst_gen(IrPrintGen *irp, IrInstGen *inst) { + if (inst == nullptr) { + fprintf(irp->f, "(null)"); + } else { + ir_print_var_gen(irp, inst); + } +} + +static void ir_print_other_block(IrPrintSrc *irp, IrBasicBlockSrc *bb) { + if (bb == nullptr) { + fprintf(irp->f, "(null block)"); + } else { + fprintf(irp->f, "$%s_%" PRIu32 "", bb->name_hint, bb->debug_id); + } +} + +static void ir_print_other_block_gen(IrPrintGen *irp, IrBasicBlockGen *bb) { + if (bb == nullptr) { + fprintf(irp->f, "(null block)"); + } else { + fprintf(irp->f, "$%s_%" PRIu32 "", bb->name_hint, bb->debug_id); + } +} + +static void ir_print_return_src(IrPrintSrc *irp, IrInstSrcReturn *inst) { + fprintf(irp->f, "return "); + ir_print_other_inst_src(irp, inst->operand); +} + +static void ir_print_return_gen(IrPrintGen *irp, IrInstGenReturn *inst) { + fprintf(irp->f, "return "); + ir_print_other_inst_gen(irp, inst->operand); +} + +static void ir_print_const(IrPrintSrc *irp, IrInstSrcConst *const_instruction) { + ir_print_const_value(irp->codegen, irp->f, const_instruction->value); +} + +static void ir_print_const(IrPrintGen *irp, IrInstGenConst *const_instruction) { + ir_print_const_value(irp->codegen, irp->f, const_instruction->base.value); +} + +static const char *ir_bin_op_id_str(IrBinOp op_id) { + switch (op_id) { + case IrBinOpInvalid: + zig_unreachable(); + case IrBinOpBoolOr: + return "BoolOr"; + case IrBinOpBoolAnd: + return "BoolAnd"; + case IrBinOpCmpEq: + return "=="; + case IrBinOpCmpNotEq: + return "!="; + case IrBinOpCmpLessThan: + return "<"; + case IrBinOpCmpGreaterThan: + return ">"; + case IrBinOpCmpLessOrEq: + return "<="; + case IrBinOpCmpGreaterOrEq: + return ">="; + case IrBinOpBinOr: + return "|"; + case IrBinOpBinXor: + return "^"; + case IrBinOpBinAnd: + return "&"; + case IrBinOpBitShiftLeftLossy: + return "<<"; + case IrBinOpBitShiftLeftExact: + return "@shlExact"; + case IrBinOpBitShiftRightLossy: + return ">>"; + case IrBinOpBitShiftRightExact: + return "@shrExact"; + case IrBinOpAdd: + return "+"; + case IrBinOpAddWrap: + return "+%"; + case IrBinOpSub: + return "-"; + case IrBinOpSubWrap: + return "-%"; + case IrBinOpMult: + return "*"; + case IrBinOpMultWrap: + return "*%"; + case IrBinOpDivUnspecified: + return "/"; + case IrBinOpDivTrunc: + return "@divTrunc"; + case IrBinOpDivFloor: + return "@divFloor"; + case IrBinOpDivExact: + return "@divExact"; + case IrBinOpRemUnspecified: + return "%"; + case IrBinOpRemRem: + return "@rem"; + case IrBinOpRemMod: + return "@mod"; + case IrBinOpArrayCat: + return "++"; + case IrBinOpArrayMult: + return "**"; + } + zig_unreachable(); +} + +static const char *ir_un_op_id_str(IrUnOp op_id) { + switch (op_id) { + case IrUnOpInvalid: + zig_unreachable(); + case IrUnOpBinNot: + return "~"; + case IrUnOpNegation: + return "-"; + case IrUnOpNegationWrap: + return "-%"; + case IrUnOpDereference: + return "*"; + case IrUnOpOptional: + return "?"; + } + zig_unreachable(); +} + +static void ir_print_un_op(IrPrintSrc *irp, IrInstSrcUnOp *inst) { + fprintf(irp->f, "%s ", ir_un_op_id_str(inst->op_id)); + ir_print_other_inst_src(irp, inst->value); +} + +static void ir_print_bin_op(IrPrintSrc *irp, IrInstSrcBinOp *bin_op_instruction) { + ir_print_other_inst_src(irp, bin_op_instruction->op1); + fprintf(irp->f, " %s ", ir_bin_op_id_str(bin_op_instruction->op_id)); + ir_print_other_inst_src(irp, bin_op_instruction->op2); + if (!bin_op_instruction->safety_check_on) { + fprintf(irp->f, " // no safety"); + } +} + +static void ir_print_bin_op(IrPrintGen *irp, IrInstGenBinOp *bin_op_instruction) { + ir_print_other_inst_gen(irp, bin_op_instruction->op1); + fprintf(irp->f, " %s ", ir_bin_op_id_str(bin_op_instruction->op_id)); + ir_print_other_inst_gen(irp, bin_op_instruction->op2); + if (!bin_op_instruction->safety_check_on) { + fprintf(irp->f, " // no safety"); + } +} + +static void ir_print_merge_err_sets(IrPrintSrc *irp, IrInstSrcMergeErrSets *instruction) { + ir_print_other_inst_src(irp, instruction->op1); + fprintf(irp->f, " || "); + ir_print_other_inst_src(irp, instruction->op2); + if (instruction->type_name != nullptr) { + fprintf(irp->f, " // name=%s", buf_ptr(instruction->type_name)); + } +} + +static void ir_print_decl_var_src(IrPrintSrc *irp, IrInstSrcDeclVar *decl_var_instruction) { + const char *var_or_const = decl_var_instruction->var->gen_is_const ? "const" : "var"; + const char *name = decl_var_instruction->var->name; + if (decl_var_instruction->var_type) { + fprintf(irp->f, "%s %s: ", var_or_const, name); + ir_print_other_inst_src(irp, decl_var_instruction->var_type); + fprintf(irp->f, " "); + } else { + fprintf(irp->f, "%s %s ", var_or_const, name); + } + if (decl_var_instruction->align_value) { + fprintf(irp->f, "align "); + ir_print_other_inst_src(irp, decl_var_instruction->align_value); + fprintf(irp->f, " "); + } + fprintf(irp->f, "= "); + ir_print_other_inst_src(irp, decl_var_instruction->ptr); + if (decl_var_instruction->var->is_comptime != nullptr) { + fprintf(irp->f, " // comptime = "); + ir_print_other_inst_src(irp, decl_var_instruction->var->is_comptime); + } +} + +static const char *cast_op_str(CastOp op) { + switch (op) { + case CastOpNoCast: return "NoCast"; + case CastOpNoop: return "NoOp"; + case CastOpIntToFloat: return "IntToFloat"; + case CastOpFloatToInt: return "FloatToInt"; + case CastOpBoolToInt: return "BoolToInt"; + case CastOpNumLitToConcrete: return "NumLitToConcrate"; + case CastOpErrSet: return "ErrSet"; + case CastOpBitCast: return "BitCast"; + } + zig_unreachable(); +} + +static void ir_print_cast(IrPrintGen *irp, IrInstGenCast *cast_instruction) { + fprintf(irp->f, "%s cast ", cast_op_str(cast_instruction->cast_op)); + ir_print_other_inst_gen(irp, cast_instruction->value); +} + +static void ir_print_result_loc_var(IrPrintSrc *irp, ResultLocVar *result_loc_var) { + fprintf(irp->f, "var("); + ir_print_other_inst_src(irp, result_loc_var->base.source_instruction); + fprintf(irp->f, ")"); +} + +static void ir_print_result_loc_instruction(IrPrintSrc *irp, ResultLocInstruction *result_loc_inst) { + fprintf(irp->f, "inst("); + ir_print_other_inst_src(irp, result_loc_inst->base.source_instruction); + fprintf(irp->f, ")"); +} + +static void ir_print_result_loc_peer(IrPrintSrc *irp, ResultLocPeer *result_loc_peer) { + fprintf(irp->f, "peer(next="); + ir_print_other_block(irp, result_loc_peer->next_bb); + fprintf(irp->f, ")"); +} + +static void ir_print_result_loc_bit_cast(IrPrintSrc *irp, ResultLocBitCast *result_loc_bit_cast) { + fprintf(irp->f, "bitcast(ty="); + ir_print_other_inst_src(irp, result_loc_bit_cast->base.source_instruction); + fprintf(irp->f, ")"); +} + +static void ir_print_result_loc_cast(IrPrintSrc *irp, ResultLocCast *result_loc_cast) { + fprintf(irp->f, "cast(ty="); + ir_print_other_inst_src(irp, result_loc_cast->base.source_instruction); + fprintf(irp->f, ")"); +} + +static void ir_print_result_loc(IrPrintSrc *irp, ResultLoc *result_loc) { + switch (result_loc->id) { + case ResultLocIdInvalid: + zig_unreachable(); + case ResultLocIdNone: + fprintf(irp->f, "none"); + return; + case ResultLocIdReturn: + fprintf(irp->f, "return"); + return; + case ResultLocIdVar: + return ir_print_result_loc_var(irp, (ResultLocVar *)result_loc); + case ResultLocIdInstruction: + return ir_print_result_loc_instruction(irp, (ResultLocInstruction *)result_loc); + case ResultLocIdPeer: + return ir_print_result_loc_peer(irp, (ResultLocPeer *)result_loc); + case ResultLocIdBitCast: + return ir_print_result_loc_bit_cast(irp, (ResultLocBitCast *)result_loc); + case ResultLocIdCast: + return ir_print_result_loc_cast(irp, (ResultLocCast *)result_loc); + case ResultLocIdPeerParent: + fprintf(irp->f, "peer_parent"); + return; + } + zig_unreachable(); +} + +static void ir_print_call_extra(IrPrintSrc *irp, IrInstSrcCallExtra *instruction) { + fprintf(irp->f, "opts="); + ir_print_other_inst_src(irp, instruction->options); + fprintf(irp->f, ", fn="); + ir_print_other_inst_src(irp, instruction->fn_ref); + fprintf(irp->f, ", args="); + ir_print_other_inst_src(irp, instruction->args); + fprintf(irp->f, ", result="); + ir_print_result_loc(irp, instruction->result_loc); +} + +static void ir_print_async_call_extra(IrPrintSrc *irp, IrInstSrcAsyncCallExtra *instruction) { + fprintf(irp->f, "modifier="); + ir_print_call_modifier(irp->f, instruction->modifier); + fprintf(irp->f, ", fn="); + ir_print_other_inst_src(irp, instruction->fn_ref); + if (instruction->ret_ptr != nullptr) { + fprintf(irp->f, ", ret_ptr="); + ir_print_other_inst_src(irp, instruction->ret_ptr); + } + fprintf(irp->f, ", new_stack="); + ir_print_other_inst_src(irp, instruction->new_stack); + fprintf(irp->f, ", args="); + ir_print_other_inst_src(irp, instruction->args); + fprintf(irp->f, ", result="); + ir_print_result_loc(irp, instruction->result_loc); +} + +static void ir_print_call_args(IrPrintSrc *irp, IrInstSrcCallArgs *instruction) { + fprintf(irp->f, "opts="); + ir_print_other_inst_src(irp, instruction->options); + fprintf(irp->f, ", fn="); + ir_print_other_inst_src(irp, instruction->fn_ref); + fprintf(irp->f, ", args=("); + for (size_t i = 0; i < instruction->args_len; i += 1) { + IrInstSrc *arg = instruction->args_ptr[i]; + if (i != 0) + fprintf(irp->f, ", "); + ir_print_other_inst_src(irp, arg); + } + fprintf(irp->f, "), result="); + ir_print_result_loc(irp, instruction->result_loc); +} + +static void ir_print_call_src(IrPrintSrc *irp, IrInstSrcCall *call_instruction) { + ir_print_call_modifier(irp->f, call_instruction->modifier); + if (call_instruction->fn_entry) { + fprintf(irp->f, "%s", buf_ptr(&call_instruction->fn_entry->symbol_name)); + } else { + assert(call_instruction->fn_ref); + ir_print_other_inst_src(irp, call_instruction->fn_ref); + } + fprintf(irp->f, "("); + for (size_t i = 0; i < call_instruction->arg_count; i += 1) { + IrInstSrc *arg = call_instruction->args[i]; + if (i != 0) + fprintf(irp->f, ", "); + ir_print_other_inst_src(irp, arg); + } + fprintf(irp->f, ")result="); + ir_print_result_loc(irp, call_instruction->result_loc); +} + +static void ir_print_call_gen(IrPrintGen *irp, IrInstGenCall *call_instruction) { + ir_print_call_modifier(irp->f, call_instruction->modifier); + if (call_instruction->fn_entry) { + fprintf(irp->f, "%s", buf_ptr(&call_instruction->fn_entry->symbol_name)); + } else { + assert(call_instruction->fn_ref); + ir_print_other_inst_gen(irp, call_instruction->fn_ref); + } + fprintf(irp->f, "("); + for (size_t i = 0; i < call_instruction->arg_count; i += 1) { + IrInstGen *arg = call_instruction->args[i]; + if (i != 0) + fprintf(irp->f, ", "); + ir_print_other_inst_gen(irp, arg); + } + fprintf(irp->f, ")result="); + ir_print_other_inst_gen(irp, call_instruction->result_loc); +} + +static void ir_print_cond_br(IrPrintSrc *irp, IrInstSrcCondBr *inst) { + fprintf(irp->f, "if ("); + ir_print_other_inst_src(irp, inst->condition); + fprintf(irp->f, ") "); + ir_print_other_block(irp, inst->then_block); + fprintf(irp->f, " else "); + ir_print_other_block(irp, inst->else_block); + if (inst->is_comptime != nullptr) { + fprintf(irp->f, " // comptime = "); + ir_print_other_inst_src(irp, inst->is_comptime); + } +} + +static void ir_print_cond_br(IrPrintGen *irp, IrInstGenCondBr *inst) { + fprintf(irp->f, "if ("); + ir_print_other_inst_gen(irp, inst->condition); + fprintf(irp->f, ") "); + ir_print_other_block_gen(irp, inst->then_block); + fprintf(irp->f, " else "); + ir_print_other_block_gen(irp, inst->else_block); +} + +static void ir_print_br(IrPrintSrc *irp, IrInstSrcBr *br_instruction) { + fprintf(irp->f, "goto "); + ir_print_other_block(irp, br_instruction->dest_block); + if (br_instruction->is_comptime != nullptr) { + fprintf(irp->f, " // comptime = "); + ir_print_other_inst_src(irp, br_instruction->is_comptime); + } +} + +static void ir_print_br(IrPrintGen *irp, IrInstGenBr *inst) { + fprintf(irp->f, "goto "); + ir_print_other_block_gen(irp, inst->dest_block); +} + +static void ir_print_phi(IrPrintSrc *irp, IrInstSrcPhi *phi_instruction) { + assert(phi_instruction->incoming_count != 0); + assert(phi_instruction->incoming_count != SIZE_MAX); + for (size_t i = 0; i < phi_instruction->incoming_count; i += 1) { + IrBasicBlockSrc *incoming_block = phi_instruction->incoming_blocks[i]; + IrInstSrc *incoming_value = phi_instruction->incoming_values[i]; + if (i != 0) + fprintf(irp->f, " "); + ir_print_other_block(irp, incoming_block); + fprintf(irp->f, ":"); + ir_print_other_inst_src(irp, incoming_value); + } +} + +static void ir_print_phi(IrPrintGen *irp, IrInstGenPhi *phi_instruction) { + assert(phi_instruction->incoming_count != 0); + assert(phi_instruction->incoming_count != SIZE_MAX); + for (size_t i = 0; i < phi_instruction->incoming_count; i += 1) { + IrBasicBlockGen *incoming_block = phi_instruction->incoming_blocks[i]; + IrInstGen *incoming_value = phi_instruction->incoming_values[i]; + if (i != 0) + fprintf(irp->f, " "); + ir_print_other_block_gen(irp, incoming_block); + fprintf(irp->f, ":"); + ir_print_other_inst_gen(irp, incoming_value); + } +} + +static void ir_print_container_init_list(IrPrintSrc *irp, IrInstSrcContainerInitList *instruction) { + fprintf(irp->f, "{"); + if (instruction->item_count > 50) { + fprintf(irp->f, "...(%" ZIG_PRI_usize " items)...", instruction->item_count); + } else { + for (size_t i = 0; i < instruction->item_count; i += 1) { + IrInstSrc *result_loc = instruction->elem_result_loc_list[i]; + if (i != 0) + fprintf(irp->f, ", "); + ir_print_other_inst_src(irp, result_loc); + } + } + fprintf(irp->f, "}result="); + ir_print_other_inst_src(irp, instruction->result_loc); +} + +static void ir_print_container_init_fields(IrPrintSrc *irp, IrInstSrcContainerInitFields *instruction) { + fprintf(irp->f, "{"); + for (size_t i = 0; i < instruction->field_count; i += 1) { + IrInstSrcContainerInitFieldsField *field = &instruction->fields[i]; + const char *comma = (i == 0) ? "" : ", "; + fprintf(irp->f, "%s.%s = ", comma, buf_ptr(field->name)); + ir_print_other_inst_src(irp, field->result_loc); + } + fprintf(irp->f, "}result="); + ir_print_other_inst_src(irp, instruction->result_loc); +} + +static void ir_print_unreachable(IrPrintSrc *irp, IrInstSrcUnreachable *instruction) { + fprintf(irp->f, "unreachable"); +} + +static void ir_print_unreachable(IrPrintGen *irp, IrInstGenUnreachable *instruction) { + fprintf(irp->f, "unreachable"); +} + +static void ir_print_elem_ptr(IrPrintSrc *irp, IrInstSrcElemPtr *instruction) { + fprintf(irp->f, "&"); + ir_print_other_inst_src(irp, instruction->array_ptr); + fprintf(irp->f, "["); + ir_print_other_inst_src(irp, instruction->elem_index); + fprintf(irp->f, "]"); + if (!instruction->safety_check_on) { + fprintf(irp->f, " // no safety"); + } +} + +static void ir_print_elem_ptr(IrPrintGen *irp, IrInstGenElemPtr *instruction) { + fprintf(irp->f, "&"); + ir_print_other_inst_gen(irp, instruction->array_ptr); + fprintf(irp->f, "["); + ir_print_other_inst_gen(irp, instruction->elem_index); + fprintf(irp->f, "]"); + if (!instruction->safety_check_on) { + fprintf(irp->f, " // no safety"); + } +} + +static void ir_print_var_ptr(IrPrintSrc *irp, IrInstSrcVarPtr *instruction) { + fprintf(irp->f, "&%s", instruction->var->name); +} + +static void ir_print_var_ptr(IrPrintGen *irp, IrInstGenVarPtr *instruction) { + fprintf(irp->f, "&%s", instruction->var->name); +} + +static void ir_print_return_ptr(IrPrintGen *irp, IrInstGenReturnPtr *instruction) { + fprintf(irp->f, "@ReturnPtr"); +} + +static void ir_print_load_ptr(IrPrintSrc *irp, IrInstSrcLoadPtr *instruction) { + ir_print_other_inst_src(irp, instruction->ptr); + fprintf(irp->f, ".*"); +} + +static void ir_print_load_ptr_gen(IrPrintGen *irp, IrInstGenLoadPtr *instruction) { + fprintf(irp->f, "loadptr("); + ir_print_other_inst_gen(irp, instruction->ptr); + fprintf(irp->f, ")result="); + ir_print_other_inst_gen(irp, instruction->result_loc); +} + +static void ir_print_store_ptr(IrPrintSrc *irp, IrInstSrcStorePtr *instruction) { + fprintf(irp->f, "*"); + ir_print_var_src(irp, instruction->ptr); + fprintf(irp->f, " = "); + ir_print_other_inst_src(irp, instruction->value); +} + +static void ir_print_store_ptr(IrPrintGen *irp, IrInstGenStorePtr *instruction) { + fprintf(irp->f, "*"); + ir_print_var_gen(irp, instruction->ptr); + fprintf(irp->f, " = "); + ir_print_other_inst_gen(irp, instruction->value); +} + +static void ir_print_vector_store_elem(IrPrintGen *irp, IrInstGenVectorStoreElem *instruction) { + fprintf(irp->f, "vector_ptr="); + ir_print_var_gen(irp, instruction->vector_ptr); + fprintf(irp->f, ",index="); + ir_print_var_gen(irp, instruction->index); + fprintf(irp->f, ",value="); + ir_print_other_inst_gen(irp, instruction->value); +} + +static void ir_print_typeof(IrPrintSrc *irp, IrInstSrcTypeOf *instruction) { + fprintf(irp->f, "@TypeOf("); + if (instruction->value_count == 1) { + ir_print_other_inst_src(irp, instruction->value.scalar); + } else { + for (size_t i = 0; i < instruction->value_count; i += 1) { + ir_print_other_inst_src(irp, instruction->value.list[i]); + } + } + fprintf(irp->f, ")"); +} + +static void ir_print_binary_not(IrPrintGen *irp, IrInstGenBinaryNot *instruction) { + fprintf(irp->f, "~"); + ir_print_other_inst_gen(irp, instruction->operand); +} + +static void ir_print_negation(IrPrintGen *irp, IrInstGenNegation *instruction) { + fprintf(irp->f, "-"); + ir_print_other_inst_gen(irp, instruction->operand); +} + +static void ir_print_negation_wrapping(IrPrintGen *irp, IrInstGenNegationWrapping *instruction) { + fprintf(irp->f, "-%%"); + ir_print_other_inst_gen(irp, instruction->operand); +} + + +static void ir_print_field_ptr(IrPrintSrc *irp, IrInstSrcFieldPtr *instruction) { + if (instruction->field_name_buffer) { + fprintf(irp->f, "fieldptr "); + ir_print_other_inst_src(irp, instruction->container_ptr); + fprintf(irp->f, ".%s", buf_ptr(instruction->field_name_buffer)); + } else { + assert(instruction->field_name_expr); + fprintf(irp->f, "@field("); + ir_print_other_inst_src(irp, instruction->container_ptr); + fprintf(irp->f, ", "); + ir_print_other_inst_src(irp, instruction->field_name_expr); + fprintf(irp->f, ")"); + } +} + +static void ir_print_struct_field_ptr(IrPrintGen *irp, IrInstGenStructFieldPtr *instruction) { + fprintf(irp->f, "@StructFieldPtr(&"); + ir_print_other_inst_gen(irp, instruction->struct_ptr); + fprintf(irp->f, ".%s", buf_ptr(instruction->field->name)); + fprintf(irp->f, ")"); +} + +static void ir_print_union_field_ptr(IrPrintGen *irp, IrInstGenUnionFieldPtr *instruction) { + fprintf(irp->f, "@UnionFieldPtr(&"); + ir_print_other_inst_gen(irp, instruction->union_ptr); + fprintf(irp->f, ".%s", buf_ptr(instruction->field->enum_field->name)); + fprintf(irp->f, ")"); +} + +static void ir_print_set_cold(IrPrintSrc *irp, IrInstSrcSetCold *instruction) { + fprintf(irp->f, "@setCold("); + ir_print_other_inst_src(irp, instruction->is_cold); + fprintf(irp->f, ")"); +} + +static void ir_print_set_runtime_safety(IrPrintSrc *irp, IrInstSrcSetRuntimeSafety *instruction) { + fprintf(irp->f, "@setRuntimeSafety("); + ir_print_other_inst_src(irp, instruction->safety_on); + fprintf(irp->f, ")"); +} + +static void ir_print_set_float_mode(IrPrintSrc *irp, IrInstSrcSetFloatMode *instruction) { + fprintf(irp->f, "@setFloatMode("); + ir_print_other_inst_src(irp, instruction->scope_value); + fprintf(irp->f, ", "); + ir_print_other_inst_src(irp, instruction->mode_value); + fprintf(irp->f, ")"); +} + +static void ir_print_array_type(IrPrintSrc *irp, IrInstSrcArrayType *instruction) { + fprintf(irp->f, "["); + ir_print_other_inst_src(irp, instruction->size); + if (instruction->sentinel != nullptr) { + fprintf(irp->f, ":"); + ir_print_other_inst_src(irp, instruction->sentinel); + } + fprintf(irp->f, "]"); + ir_print_other_inst_src(irp, instruction->child_type); +} + +static void ir_print_slice_type(IrPrintSrc *irp, IrInstSrcSliceType *instruction) { + const char *const_kw = instruction->is_const ? "const " : ""; + fprintf(irp->f, "[]%s", const_kw); + ir_print_other_inst_src(irp, instruction->child_type); +} + +static void ir_print_any_frame_type(IrPrintSrc *irp, IrInstSrcAnyFrameType *instruction) { + if (instruction->payload_type == nullptr) { + fprintf(irp->f, "anyframe"); + } else { + fprintf(irp->f, "anyframe->"); + ir_print_other_inst_src(irp, instruction->payload_type); + } +} + +static void ir_print_asm_src(IrPrintSrc *irp, IrInstSrcAsm *instruction) { + assert(instruction->base.base.source_node->type == NodeTypeAsmExpr); + AstNodeAsmExpr *asm_expr = &instruction->base.base.source_node->data.asm_expr; + const char *volatile_kw = instruction->has_side_effects ? " volatile" : ""; + fprintf(irp->f, "asm%s (", volatile_kw); + ir_print_other_inst_src(irp, instruction->asm_template); + + 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(irp->f, ", "); + + fprintf(irp->f, "[%s] \"%s\" (", + buf_ptr(asm_output->asm_symbolic_name), + buf_ptr(asm_output->constraint)); + if (asm_output->return_type) { + fprintf(irp->f, "-> "); + ir_print_other_inst_src(irp, instruction->output_types[i]); + } else { + fprintf(irp->f, "%s", buf_ptr(asm_output->variable_name)); + } + fprintf(irp->f, ")"); + } + + fprintf(irp->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(irp->f, ", "); + fprintf(irp->f, "[%s] \"%s\" (", + buf_ptr(asm_input->asm_symbolic_name), + buf_ptr(asm_input->constraint)); + ir_print_other_inst_src(irp, instruction->input_list[i]); + fprintf(irp->f, ")"); + } + fprintf(irp->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(irp->f, ", "); + fprintf(irp->f, "\"%s\"", buf_ptr(reg_name)); + } + fprintf(irp->f, ")"); +} + +static void ir_print_asm_gen(IrPrintGen *irp, IrInstGenAsm *instruction) { + assert(instruction->base.base.source_node->type == NodeTypeAsmExpr); + AstNodeAsmExpr *asm_expr = &instruction->base.base.source_node->data.asm_expr; + const char *volatile_kw = instruction->has_side_effects ? " volatile" : ""; + fprintf(irp->f, "asm%s (\"%s\") : ", volatile_kw, buf_ptr(instruction->asm_template)); + + 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(irp->f, ", "); + + fprintf(irp->f, "[%s] \"%s\" (", + buf_ptr(asm_output->asm_symbolic_name), + buf_ptr(asm_output->constraint)); + if (asm_output->return_type) { + fprintf(irp->f, "-> "); + ir_print_other_inst_gen(irp, instruction->output_types[i]); + } else { + fprintf(irp->f, "%s", buf_ptr(asm_output->variable_name)); + } + fprintf(irp->f, ")"); + } + + fprintf(irp->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(irp->f, ", "); + fprintf(irp->f, "[%s] \"%s\" (", + buf_ptr(asm_input->asm_symbolic_name), + buf_ptr(asm_input->constraint)); + ir_print_other_inst_gen(irp, instruction->input_list[i]); + fprintf(irp->f, ")"); + } + fprintf(irp->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(irp->f, ", "); + fprintf(irp->f, "\"%s\"", buf_ptr(reg_name)); + } + fprintf(irp->f, ")"); +} + +static void ir_print_size_of(IrPrintSrc *irp, IrInstSrcSizeOf *instruction) { + if (instruction->bit_size) + fprintf(irp->f, "@bitSizeOf("); + else + fprintf(irp->f, "@sizeOf("); + ir_print_other_inst_src(irp, instruction->type_value); + fprintf(irp->f, ")"); +} + +static void ir_print_test_non_null(IrPrintSrc *irp, IrInstSrcTestNonNull *instruction) { + ir_print_other_inst_src(irp, instruction->value); + fprintf(irp->f, " != null"); +} + +static void ir_print_test_non_null(IrPrintGen *irp, IrInstGenTestNonNull *instruction) { + ir_print_other_inst_gen(irp, instruction->value); + fprintf(irp->f, " != null"); +} + +static void ir_print_optional_unwrap_ptr(IrPrintSrc *irp, IrInstSrcOptionalUnwrapPtr *instruction) { + fprintf(irp->f, "&"); + ir_print_other_inst_src(irp, instruction->base_ptr); + fprintf(irp->f, ".*.?"); + if (!instruction->safety_check_on) { + fprintf(irp->f, " // no safety"); + } +} + +static void ir_print_optional_unwrap_ptr(IrPrintGen *irp, IrInstGenOptionalUnwrapPtr *instruction) { + fprintf(irp->f, "&"); + ir_print_other_inst_gen(irp, instruction->base_ptr); + fprintf(irp->f, ".*.?"); + if (!instruction->safety_check_on) { + fprintf(irp->f, " // no safety"); + } +} + +static void ir_print_clz(IrPrintSrc *irp, IrInstSrcClz *instruction) { + fprintf(irp->f, "@clz("); + ir_print_other_inst_src(irp, instruction->type); + fprintf(irp->f, ","); + ir_print_other_inst_src(irp, instruction->op); + fprintf(irp->f, ")"); +} + +static void ir_print_clz(IrPrintGen *irp, IrInstGenClz *instruction) { + fprintf(irp->f, "@clz("); + ir_print_other_inst_gen(irp, instruction->op); + fprintf(irp->f, ")"); +} + +static void ir_print_ctz(IrPrintSrc *irp, IrInstSrcCtz *instruction) { + fprintf(irp->f, "@ctz("); + ir_print_other_inst_src(irp, instruction->type); + fprintf(irp->f, ","); + ir_print_other_inst_src(irp, instruction->op); + fprintf(irp->f, ")"); +} + +static void ir_print_ctz(IrPrintGen *irp, IrInstGenCtz *instruction) { + fprintf(irp->f, "@ctz("); + ir_print_other_inst_gen(irp, instruction->op); + fprintf(irp->f, ")"); +} + +static void ir_print_pop_count(IrPrintSrc *irp, IrInstSrcPopCount *instruction) { + fprintf(irp->f, "@popCount("); + ir_print_other_inst_src(irp, instruction->type); + fprintf(irp->f, ","); + ir_print_other_inst_src(irp, instruction->op); + fprintf(irp->f, ")"); +} + +static void ir_print_pop_count(IrPrintGen *irp, IrInstGenPopCount *instruction) { + fprintf(irp->f, "@popCount("); + ir_print_other_inst_gen(irp, instruction->op); + fprintf(irp->f, ")"); +} + +static void ir_print_bswap(IrPrintSrc *irp, IrInstSrcBswap *instruction) { + fprintf(irp->f, "@byteSwap("); + ir_print_other_inst_src(irp, instruction->type); + fprintf(irp->f, ","); + ir_print_other_inst_src(irp, instruction->op); + fprintf(irp->f, ")"); +} + +static void ir_print_bswap(IrPrintGen *irp, IrInstGenBswap *instruction) { + fprintf(irp->f, "@byteSwap("); + ir_print_other_inst_gen(irp, instruction->op); + fprintf(irp->f, ")"); +} + +static void ir_print_bit_reverse(IrPrintSrc *irp, IrInstSrcBitReverse *instruction) { + fprintf(irp->f, "@bitReverse("); + ir_print_other_inst_src(irp, instruction->type); + fprintf(irp->f, ","); + ir_print_other_inst_src(irp, instruction->op); + fprintf(irp->f, ")"); +} + +static void ir_print_bit_reverse(IrPrintGen *irp, IrInstGenBitReverse *instruction) { + fprintf(irp->f, "@bitReverse("); + ir_print_other_inst_gen(irp, instruction->op); + fprintf(irp->f, ")"); +} + +static void ir_print_switch_br(IrPrintSrc *irp, IrInstSrcSwitchBr *instruction) { + fprintf(irp->f, "switch ("); + ir_print_other_inst_src(irp, instruction->target_value); + fprintf(irp->f, ") "); + for (size_t i = 0; i < instruction->case_count; i += 1) { + IrInstSrcSwitchBrCase *this_case = &instruction->cases[i]; + ir_print_other_inst_src(irp, this_case->value); + fprintf(irp->f, " => "); + ir_print_other_block(irp, this_case->block); + fprintf(irp->f, ", "); + } + fprintf(irp->f, "else => "); + ir_print_other_block(irp, instruction->else_block); + if (instruction->is_comptime != nullptr) { + fprintf(irp->f, " // comptime = "); + ir_print_other_inst_src(irp, instruction->is_comptime); + } +} + +static void ir_print_switch_br(IrPrintGen *irp, IrInstGenSwitchBr *instruction) { + fprintf(irp->f, "switch ("); + ir_print_other_inst_gen(irp, instruction->target_value); + fprintf(irp->f, ") "); + for (size_t i = 0; i < instruction->case_count; i += 1) { + IrInstGenSwitchBrCase *this_case = &instruction->cases[i]; + ir_print_other_inst_gen(irp, this_case->value); + fprintf(irp->f, " => "); + ir_print_other_block_gen(irp, this_case->block); + fprintf(irp->f, ", "); + } + fprintf(irp->f, "else => "); + ir_print_other_block_gen(irp, instruction->else_block); +} + +static void ir_print_switch_var(IrPrintSrc *irp, IrInstSrcSwitchVar *instruction) { + fprintf(irp->f, "switchvar "); + ir_print_other_inst_src(irp, instruction->target_value_ptr); + for (size_t i = 0; i < instruction->prongs_len; i += 1) { + fprintf(irp->f, ", "); + ir_print_other_inst_src(irp, instruction->prongs_ptr[i]); + } +} + +static void ir_print_switch_else_var(IrPrintSrc *irp, IrInstSrcSwitchElseVar *instruction) { + fprintf(irp->f, "switchelsevar "); + ir_print_other_inst_src(irp, &instruction->switch_br->base); +} + +static void ir_print_switch_target(IrPrintSrc *irp, IrInstSrcSwitchTarget *instruction) { + fprintf(irp->f, "switchtarget "); + ir_print_other_inst_src(irp, instruction->target_value_ptr); +} + +static void ir_print_union_tag(IrPrintGen *irp, IrInstGenUnionTag *instruction) { + fprintf(irp->f, "uniontag "); + ir_print_other_inst_gen(irp, instruction->value); +} + +static void ir_print_import(IrPrintSrc *irp, IrInstSrcImport *instruction) { + fprintf(irp->f, "@import("); + ir_print_other_inst_src(irp, instruction->name); + fprintf(irp->f, ")"); +} + +static void ir_print_ref(IrPrintSrc *irp, IrInstSrcRef *instruction) { + fprintf(irp->f, "ref "); + ir_print_other_inst_src(irp, instruction->value); +} + +static void ir_print_ref_gen(IrPrintGen *irp, IrInstGenRef *instruction) { + fprintf(irp->f, "@ref("); + ir_print_other_inst_gen(irp, instruction->operand); + fprintf(irp->f, ")result="); + ir_print_other_inst_gen(irp, instruction->result_loc); +} + +static void ir_print_compile_err(IrPrintSrc *irp, IrInstSrcCompileErr *instruction) { + fprintf(irp->f, "@compileError("); + ir_print_other_inst_src(irp, instruction->msg); + fprintf(irp->f, ")"); +} + +static void ir_print_compile_log(IrPrintSrc *irp, IrInstSrcCompileLog *instruction) { + fprintf(irp->f, "@compileLog("); + for (size_t i = 0; i < instruction->msg_count; i += 1) { + if (i != 0) + fprintf(irp->f, ","); + IrInstSrc *msg = instruction->msg_list[i]; + ir_print_other_inst_src(irp, msg); + } + fprintf(irp->f, ")"); +} + +static void ir_print_err_name(IrPrintSrc *irp, IrInstSrcErrName *instruction) { + fprintf(irp->f, "@errorName("); + ir_print_other_inst_src(irp, instruction->value); + fprintf(irp->f, ")"); +} + +static void ir_print_err_name(IrPrintGen *irp, IrInstGenErrName *instruction) { + fprintf(irp->f, "@errorName("); + ir_print_other_inst_gen(irp, instruction->value); + fprintf(irp->f, ")"); +} + +static void ir_print_c_import(IrPrintSrc *irp, IrInstSrcCImport *instruction) { + fprintf(irp->f, "@cImport(...)"); +} + +static void ir_print_c_include(IrPrintSrc *irp, IrInstSrcCInclude *instruction) { + fprintf(irp->f, "@cInclude("); + ir_print_other_inst_src(irp, instruction->name); + fprintf(irp->f, ")"); +} + +static void ir_print_c_define(IrPrintSrc *irp, IrInstSrcCDefine *instruction) { + fprintf(irp->f, "@cDefine("); + ir_print_other_inst_src(irp, instruction->name); + fprintf(irp->f, ", "); + ir_print_other_inst_src(irp, instruction->value); + fprintf(irp->f, ")"); +} + +static void ir_print_c_undef(IrPrintSrc *irp, IrInstSrcCUndef *instruction) { + fprintf(irp->f, "@cUndef("); + ir_print_other_inst_src(irp, instruction->name); + fprintf(irp->f, ")"); +} + +static void ir_print_embed_file(IrPrintSrc *irp, IrInstSrcEmbedFile *instruction) { + fprintf(irp->f, "@embedFile("); + ir_print_other_inst_src(irp, instruction->name); + fprintf(irp->f, ")"); +} + +static void ir_print_cmpxchg_src(IrPrintSrc *irp, IrInstSrcCmpxchg *instruction) { + fprintf(irp->f, "@cmpxchg("); + ir_print_other_inst_src(irp, instruction->ptr); + fprintf(irp->f, ", "); + ir_print_other_inst_src(irp, instruction->cmp_value); + fprintf(irp->f, ", "); + ir_print_other_inst_src(irp, instruction->new_value); + fprintf(irp->f, ", "); + ir_print_other_inst_src(irp, instruction->success_order_value); + fprintf(irp->f, ", "); + ir_print_other_inst_src(irp, instruction->failure_order_value); + fprintf(irp->f, ")result="); + ir_print_result_loc(irp, instruction->result_loc); +} + +static void ir_print_cmpxchg_gen(IrPrintGen *irp, IrInstGenCmpxchg *instruction) { + fprintf(irp->f, "@cmpxchg("); + ir_print_other_inst_gen(irp, instruction->ptr); + fprintf(irp->f, ", "); + ir_print_other_inst_gen(irp, instruction->cmp_value); + fprintf(irp->f, ", "); + ir_print_other_inst_gen(irp, instruction->new_value); + fprintf(irp->f, ", TODO print atomic orders)result="); + ir_print_other_inst_gen(irp, instruction->result_loc); +} + +static void ir_print_fence(IrPrintSrc *irp, IrInstSrcFence *instruction) { + fprintf(irp->f, "@fence("); + ir_print_other_inst_src(irp, instruction->order); + fprintf(irp->f, ")"); +} + +static const char *atomic_order_str(AtomicOrder order) { + switch (order) { + case AtomicOrderUnordered: return "Unordered"; + case AtomicOrderMonotonic: return "Monotonic"; + case AtomicOrderAcquire: return "Acquire"; + case AtomicOrderRelease: return "Release"; + case AtomicOrderAcqRel: return "AcqRel"; + case AtomicOrderSeqCst: return "SeqCst"; + } + zig_unreachable(); +} + +static void ir_print_fence(IrPrintGen *irp, IrInstGenFence *instruction) { + fprintf(irp->f, "fence %s", atomic_order_str(instruction->order)); +} + +static void ir_print_truncate(IrPrintSrc *irp, IrInstSrcTruncate *instruction) { + fprintf(irp->f, "@truncate("); + ir_print_other_inst_src(irp, instruction->dest_type); + fprintf(irp->f, ", "); + ir_print_other_inst_src(irp, instruction->target); + fprintf(irp->f, ")"); +} + +static void ir_print_truncate(IrPrintGen *irp, IrInstGenTruncate *instruction) { + fprintf(irp->f, "@truncate("); + ir_print_other_inst_gen(irp, instruction->target); + fprintf(irp->f, ")"); +} + +static void ir_print_int_cast(IrPrintSrc *irp, IrInstSrcIntCast *instruction) { + fprintf(irp->f, "@intCast("); + ir_print_other_inst_src(irp, instruction->dest_type); + fprintf(irp->f, ", "); + ir_print_other_inst_src(irp, instruction->target); + fprintf(irp->f, ")"); +} + +static void ir_print_float_cast(IrPrintSrc *irp, IrInstSrcFloatCast *instruction) { + fprintf(irp->f, "@floatCast("); + ir_print_other_inst_src(irp, instruction->dest_type); + fprintf(irp->f, ", "); + ir_print_other_inst_src(irp, instruction->target); + fprintf(irp->f, ")"); +} + +static void ir_print_err_set_cast(IrPrintSrc *irp, IrInstSrcErrSetCast *instruction) { + fprintf(irp->f, "@errSetCast("); + ir_print_other_inst_src(irp, instruction->dest_type); + fprintf(irp->f, ", "); + ir_print_other_inst_src(irp, instruction->target); + fprintf(irp->f, ")"); +} + +static void ir_print_int_to_float(IrPrintSrc *irp, IrInstSrcIntToFloat *instruction) { + fprintf(irp->f, "@intToFloat("); + ir_print_other_inst_src(irp, instruction->dest_type); + fprintf(irp->f, ", "); + ir_print_other_inst_src(irp, instruction->target); + fprintf(irp->f, ")"); +} + +static void ir_print_float_to_int(IrPrintSrc *irp, IrInstSrcFloatToInt *instruction) { + fprintf(irp->f, "@floatToInt("); + ir_print_other_inst_src(irp, instruction->dest_type); + fprintf(irp->f, ", "); + ir_print_other_inst_src(irp, instruction->target); + fprintf(irp->f, ")"); +} + +static void ir_print_bool_to_int(IrPrintSrc *irp, IrInstSrcBoolToInt *instruction) { + fprintf(irp->f, "@boolToInt("); + ir_print_other_inst_src(irp, instruction->target); + fprintf(irp->f, ")"); +} + +static void ir_print_vector_type(IrPrintSrc *irp, IrInstSrcVectorType *instruction) { + fprintf(irp->f, "@Vector("); + ir_print_other_inst_src(irp, instruction->len); + fprintf(irp->f, ", "); + ir_print_other_inst_src(irp, instruction->elem_type); + fprintf(irp->f, ")"); +} + +static void ir_print_shuffle_vector(IrPrintSrc *irp, IrInstSrcShuffleVector *instruction) { + fprintf(irp->f, "@shuffle("); + ir_print_other_inst_src(irp, instruction->scalar_type); + fprintf(irp->f, ", "); + ir_print_other_inst_src(irp, instruction->a); + fprintf(irp->f, ", "); + ir_print_other_inst_src(irp, instruction->b); + fprintf(irp->f, ", "); + ir_print_other_inst_src(irp, instruction->mask); + fprintf(irp->f, ")"); +} + +static void ir_print_shuffle_vector(IrPrintGen *irp, IrInstGenShuffleVector *instruction) { + fprintf(irp->f, "@shuffle("); + ir_print_other_inst_gen(irp, instruction->a); + fprintf(irp->f, ", "); + ir_print_other_inst_gen(irp, instruction->b); + fprintf(irp->f, ", "); + ir_print_other_inst_gen(irp, instruction->mask); + fprintf(irp->f, ")"); +} + +static void ir_print_splat_src(IrPrintSrc *irp, IrInstSrcSplat *instruction) { + fprintf(irp->f, "@splat("); + ir_print_other_inst_src(irp, instruction->len); + fprintf(irp->f, ", "); + ir_print_other_inst_src(irp, instruction->scalar); + fprintf(irp->f, ")"); +} + +static void ir_print_splat_gen(IrPrintGen *irp, IrInstGenSplat *instruction) { + fprintf(irp->f, "@splat("); + ir_print_other_inst_gen(irp, instruction->scalar); + fprintf(irp->f, ")"); +} + +static void ir_print_bool_not(IrPrintSrc *irp, IrInstSrcBoolNot *instruction) { + fprintf(irp->f, "! "); + ir_print_other_inst_src(irp, instruction->value); +} + +static void ir_print_bool_not(IrPrintGen *irp, IrInstGenBoolNot *instruction) { + fprintf(irp->f, "! "); + ir_print_other_inst_gen(irp, instruction->value); +} + +static void ir_print_wasm_memory_size(IrPrintSrc *irp, IrInstSrcWasmMemorySize *instruction) { + fprintf(irp->f, "@wasmMemorySize("); + ir_print_other_inst_src(irp, instruction->index); + fprintf(irp->f, ")"); +} + +static void ir_print_wasm_memory_size(IrPrintGen *irp, IrInstGenWasmMemorySize *instruction) { + fprintf(irp->f, "@wasmMemorySize("); + ir_print_other_inst_gen(irp, instruction->index); + fprintf(irp->f, ")"); +} + +static void ir_print_wasm_memory_grow(IrPrintSrc *irp, IrInstSrcWasmMemoryGrow *instruction) { + fprintf(irp->f, "@wasmMemoryGrow("); + ir_print_other_inst_src(irp, instruction->index); + fprintf(irp->f, ", "); + ir_print_other_inst_src(irp, instruction->delta); + fprintf(irp->f, ")"); +} + +static void ir_print_wasm_memory_grow(IrPrintGen *irp, IrInstGenWasmMemoryGrow *instruction) { + fprintf(irp->f, "@wasmMemoryGrow("); + ir_print_other_inst_gen(irp, instruction->index); + fprintf(irp->f, ", "); + ir_print_other_inst_gen(irp, instruction->delta); + fprintf(irp->f, ")"); +} + +static void ir_print_builtin_src(IrPrintSrc *irp, IrInstSrcSrc *instruction) { + fprintf(irp->f, "@src()"); +} + +static void ir_print_memset(IrPrintSrc *irp, IrInstSrcMemset *instruction) { + fprintf(irp->f, "@memset("); + ir_print_other_inst_src(irp, instruction->dest_ptr); + fprintf(irp->f, ", "); + ir_print_other_inst_src(irp, instruction->byte); + fprintf(irp->f, ", "); + ir_print_other_inst_src(irp, instruction->count); + fprintf(irp->f, ")"); +} + +static void ir_print_memset(IrPrintGen *irp, IrInstGenMemset *instruction) { + fprintf(irp->f, "@memset("); + ir_print_other_inst_gen(irp, instruction->dest_ptr); + fprintf(irp->f, ", "); + ir_print_other_inst_gen(irp, instruction->byte); + fprintf(irp->f, ", "); + ir_print_other_inst_gen(irp, instruction->count); + fprintf(irp->f, ")"); +} + +static void ir_print_memcpy(IrPrintSrc *irp, IrInstSrcMemcpy *instruction) { + fprintf(irp->f, "@memcpy("); + ir_print_other_inst_src(irp, instruction->dest_ptr); + fprintf(irp->f, ", "); + ir_print_other_inst_src(irp, instruction->src_ptr); + fprintf(irp->f, ", "); + ir_print_other_inst_src(irp, instruction->count); + fprintf(irp->f, ")"); +} + +static void ir_print_memcpy(IrPrintGen *irp, IrInstGenMemcpy *instruction) { + fprintf(irp->f, "@memcpy("); + ir_print_other_inst_gen(irp, instruction->dest_ptr); + fprintf(irp->f, ", "); + ir_print_other_inst_gen(irp, instruction->src_ptr); + fprintf(irp->f, ", "); + ir_print_other_inst_gen(irp, instruction->count); + fprintf(irp->f, ")"); +} + +static void ir_print_slice_src(IrPrintSrc *irp, IrInstSrcSlice *instruction) { + ir_print_other_inst_src(irp, instruction->ptr); + fprintf(irp->f, "["); + ir_print_other_inst_src(irp, instruction->start); + fprintf(irp->f, ".."); + if (instruction->end) + ir_print_other_inst_src(irp, instruction->end); + fprintf(irp->f, "]result="); + ir_print_result_loc(irp, instruction->result_loc); +} + +static void ir_print_slice_gen(IrPrintGen *irp, IrInstGenSlice *instruction) { + ir_print_other_inst_gen(irp, instruction->ptr); + fprintf(irp->f, "["); + ir_print_other_inst_gen(irp, instruction->start); + fprintf(irp->f, ".."); + if (instruction->end) + ir_print_other_inst_gen(irp, instruction->end); + fprintf(irp->f, "]result="); + ir_print_other_inst_gen(irp, instruction->result_loc); +} + +static void ir_print_breakpoint(IrPrintSrc *irp, IrInstSrcBreakpoint *instruction) { + fprintf(irp->f, "@breakpoint()"); +} + +static void ir_print_breakpoint(IrPrintGen *irp, IrInstGenBreakpoint *instruction) { + fprintf(irp->f, "@breakpoint()"); +} + +static void ir_print_frame_address(IrPrintSrc *irp, IrInstSrcFrameAddress *instruction) { + fprintf(irp->f, "@frameAddress()"); +} + +static void ir_print_frame_address(IrPrintGen *irp, IrInstGenFrameAddress *instruction) { + fprintf(irp->f, "@frameAddress()"); +} + +static void ir_print_handle(IrPrintSrc *irp, IrInstSrcFrameHandle *instruction) { + fprintf(irp->f, "@frame()"); +} + +static void ir_print_handle(IrPrintGen *irp, IrInstGenFrameHandle *instruction) { + fprintf(irp->f, "@frame()"); +} + +static void ir_print_frame_type(IrPrintSrc *irp, IrInstSrcFrameType *instruction) { + fprintf(irp->f, "@Frame("); + ir_print_other_inst_src(irp, instruction->fn); + fprintf(irp->f, ")"); +} + +static void ir_print_frame_size_src(IrPrintSrc *irp, IrInstSrcFrameSize *instruction) { + fprintf(irp->f, "@frameSize("); + ir_print_other_inst_src(irp, instruction->fn); + fprintf(irp->f, ")"); +} + +static void ir_print_frame_size_gen(IrPrintGen *irp, IrInstGenFrameSize *instruction) { + fprintf(irp->f, "@frameSize("); + ir_print_other_inst_gen(irp, instruction->fn); + fprintf(irp->f, ")"); +} + +static void ir_print_return_address(IrPrintSrc *irp, IrInstSrcReturnAddress *instruction) { + fprintf(irp->f, "@returnAddress()"); +} + +static void ir_print_return_address(IrPrintGen *irp, IrInstGenReturnAddress *instruction) { + fprintf(irp->f, "@returnAddress()"); +} + +static void ir_print_align_of(IrPrintSrc *irp, IrInstSrcAlignOf *instruction) { + fprintf(irp->f, "@alignOf("); + ir_print_other_inst_src(irp, instruction->type_value); + fprintf(irp->f, ")"); +} + +static void ir_print_overflow_op(IrPrintSrc *irp, IrInstSrcOverflowOp *instruction) { + switch (instruction->op) { + case IrOverflowOpAdd: + fprintf(irp->f, "@addWithOverflow("); + break; + case IrOverflowOpSub: + fprintf(irp->f, "@subWithOverflow("); + break; + case IrOverflowOpMul: + fprintf(irp->f, "@mulWithOverflow("); + break; + case IrOverflowOpShl: + fprintf(irp->f, "@shlWithOverflow("); + break; + } + ir_print_other_inst_src(irp, instruction->type_value); + fprintf(irp->f, ", "); + ir_print_other_inst_src(irp, instruction->op1); + fprintf(irp->f, ", "); + ir_print_other_inst_src(irp, instruction->op2); + fprintf(irp->f, ", "); + ir_print_other_inst_src(irp, instruction->result_ptr); + fprintf(irp->f, ")"); +} + +static void ir_print_overflow_op(IrPrintGen *irp, IrInstGenOverflowOp *instruction) { + switch (instruction->op) { + case IrOverflowOpAdd: + fprintf(irp->f, "@addWithOverflow("); + break; + case IrOverflowOpSub: + fprintf(irp->f, "@subWithOverflow("); + break; + case IrOverflowOpMul: + fprintf(irp->f, "@mulWithOverflow("); + break; + case IrOverflowOpShl: + fprintf(irp->f, "@shlWithOverflow("); + break; + } + ir_print_other_inst_gen(irp, instruction->op1); + fprintf(irp->f, ", "); + ir_print_other_inst_gen(irp, instruction->op2); + fprintf(irp->f, ", "); + ir_print_other_inst_gen(irp, instruction->result_ptr); + fprintf(irp->f, ")"); +} + +static void ir_print_test_err_src(IrPrintSrc *irp, IrInstSrcTestErr *instruction) { + fprintf(irp->f, "@testError("); + ir_print_other_inst_src(irp, instruction->base_ptr); + fprintf(irp->f, ")"); +} + +static void ir_print_test_err_gen(IrPrintGen *irp, IrInstGenTestErr *instruction) { + fprintf(irp->f, "@testError("); + ir_print_other_inst_gen(irp, instruction->err_union); + fprintf(irp->f, ")"); +} + +static void ir_print_unwrap_err_code(IrPrintSrc *irp, IrInstSrcUnwrapErrCode *instruction) { + fprintf(irp->f, "UnwrapErrorCode("); + ir_print_other_inst_src(irp, instruction->err_union_ptr); + fprintf(irp->f, ")"); +} + +static void ir_print_unwrap_err_code(IrPrintGen *irp, IrInstGenUnwrapErrCode *instruction) { + fprintf(irp->f, "UnwrapErrorCode("); + ir_print_other_inst_gen(irp, instruction->err_union_ptr); + fprintf(irp->f, ")"); +} + +static void ir_print_unwrap_err_payload(IrPrintSrc *irp, IrInstSrcUnwrapErrPayload *instruction) { + fprintf(irp->f, "ErrorUnionFieldPayload("); + ir_print_other_inst_src(irp, instruction->value); + fprintf(irp->f, ")safety=%d,init=%d",instruction->safety_check_on, instruction->initializing); +} + +static void ir_print_unwrap_err_payload(IrPrintGen *irp, IrInstGenUnwrapErrPayload *instruction) { + fprintf(irp->f, "ErrorUnionFieldPayload("); + ir_print_other_inst_gen(irp, instruction->value); + fprintf(irp->f, ")safety=%d,init=%d",instruction->safety_check_on, instruction->initializing); +} + +static void ir_print_optional_wrap(IrPrintGen *irp, IrInstGenOptionalWrap *instruction) { + fprintf(irp->f, "@optionalWrap("); + ir_print_other_inst_gen(irp, instruction->operand); + fprintf(irp->f, ")result="); + ir_print_other_inst_gen(irp, instruction->result_loc); +} + +static void ir_print_err_wrap_code(IrPrintGen *irp, IrInstGenErrWrapCode *instruction) { + fprintf(irp->f, "@errWrapCode("); + ir_print_other_inst_gen(irp, instruction->operand); + fprintf(irp->f, ")result="); + ir_print_other_inst_gen(irp, instruction->result_loc); +} + +static void ir_print_err_wrap_payload(IrPrintGen *irp, IrInstGenErrWrapPayload *instruction) { + fprintf(irp->f, "@errWrapPayload("); + ir_print_other_inst_gen(irp, instruction->operand); + fprintf(irp->f, ")result="); + ir_print_other_inst_gen(irp, instruction->result_loc); +} + +static void ir_print_fn_proto(IrPrintSrc *irp, IrInstSrcFnProto *instruction) { + fprintf(irp->f, "fn("); + for (size_t i = 0; i < instruction->base.base.source_node->data.fn_proto.params.length; i += 1) { + if (i != 0) + fprintf(irp->f, ","); + if (instruction->is_var_args && i == instruction->base.base.source_node->data.fn_proto.params.length - 1) { + fprintf(irp->f, "..."); + } else { + ir_print_other_inst_src(irp, instruction->param_types[i]); + } + } + fprintf(irp->f, ")"); + if (instruction->align_value != nullptr) { + fprintf(irp->f, " align "); + ir_print_other_inst_src(irp, instruction->align_value); + fprintf(irp->f, " "); + } + fprintf(irp->f, "->"); + ir_print_other_inst_src(irp, instruction->return_type); +} + +static void ir_print_test_comptime(IrPrintSrc *irp, IrInstSrcTestComptime *instruction) { + fprintf(irp->f, "@testComptime("); + ir_print_other_inst_src(irp, instruction->value); + fprintf(irp->f, ")"); +} + +static void ir_print_ptr_cast_src(IrPrintSrc *irp, IrInstSrcPtrCast *instruction) { + fprintf(irp->f, "@ptrCast("); + if (instruction->dest_type) { + ir_print_other_inst_src(irp, instruction->dest_type); + } + fprintf(irp->f, ","); + ir_print_other_inst_src(irp, instruction->ptr); + fprintf(irp->f, ")"); +} + +static void ir_print_ptr_cast_gen(IrPrintGen *irp, IrInstGenPtrCast *instruction) { + fprintf(irp->f, "@ptrCast("); + ir_print_other_inst_gen(irp, instruction->ptr); + fprintf(irp->f, ")"); +} + +static void ir_print_implicit_cast(IrPrintSrc *irp, IrInstSrcImplicitCast *instruction) { + fprintf(irp->f, "@implicitCast("); + ir_print_other_inst_src(irp, instruction->operand); + fprintf(irp->f, ")result="); + ir_print_result_loc(irp, &instruction->result_loc_cast->base); +} + +static void ir_print_bit_cast_src(IrPrintSrc *irp, IrInstSrcBitCast *instruction) { + fprintf(irp->f, "@bitCast("); + ir_print_other_inst_src(irp, instruction->operand); + fprintf(irp->f, ")result="); + ir_print_result_loc(irp, &instruction->result_loc_bit_cast->base); +} + +static void ir_print_bit_cast_gen(IrPrintGen *irp, IrInstGenBitCast *instruction) { + fprintf(irp->f, "@bitCast("); + ir_print_other_inst_gen(irp, instruction->operand); + fprintf(irp->f, ")"); +} + +static void ir_print_widen_or_shorten(IrPrintGen *irp, IrInstGenWidenOrShorten *instruction) { + fprintf(irp->f, "WidenOrShorten("); + ir_print_other_inst_gen(irp, instruction->target); + fprintf(irp->f, ")"); +} + +static void ir_print_ptr_to_int(IrPrintSrc *irp, IrInstSrcPtrToInt *instruction) { + fprintf(irp->f, "@ptrToInt("); + ir_print_other_inst_src(irp, instruction->target); + fprintf(irp->f, ")"); +} + +static void ir_print_ptr_to_int(IrPrintGen *irp, IrInstGenPtrToInt *instruction) { + fprintf(irp->f, "@ptrToInt("); + ir_print_other_inst_gen(irp, instruction->target); + fprintf(irp->f, ")"); +} + +static void ir_print_int_to_ptr(IrPrintSrc *irp, IrInstSrcIntToPtr *instruction) { + fprintf(irp->f, "@intToPtr("); + ir_print_other_inst_src(irp, instruction->dest_type); + fprintf(irp->f, ","); + ir_print_other_inst_src(irp, instruction->target); + fprintf(irp->f, ")"); +} + +static void ir_print_int_to_ptr(IrPrintGen *irp, IrInstGenIntToPtr *instruction) { + fprintf(irp->f, "@intToPtr("); + ir_print_other_inst_gen(irp, instruction->target); + fprintf(irp->f, ")"); +} + +static void ir_print_int_to_enum(IrPrintSrc *irp, IrInstSrcIntToEnum *instruction) { + fprintf(irp->f, "@intToEnum("); + ir_print_other_inst_src(irp, instruction->dest_type); + fprintf(irp->f, ","); + ir_print_other_inst_src(irp, instruction->target); + fprintf(irp->f, ")"); +} + +static void ir_print_int_to_enum(IrPrintGen *irp, IrInstGenIntToEnum *instruction) { + fprintf(irp->f, "@intToEnum("); + ir_print_other_inst_gen(irp, instruction->target); + fprintf(irp->f, ")"); +} + +static void ir_print_enum_to_int(IrPrintSrc *irp, IrInstSrcEnumToInt *instruction) { + fprintf(irp->f, "@enumToInt("); + ir_print_other_inst_src(irp, instruction->target); + fprintf(irp->f, ")"); +} + +static void ir_print_check_runtime_scope(IrPrintSrc *irp, IrInstSrcCheckRuntimeScope *instruction) { + fprintf(irp->f, "@checkRuntimeScope("); + ir_print_other_inst_src(irp, instruction->scope_is_comptime); + fprintf(irp->f, ","); + ir_print_other_inst_src(irp, instruction->is_comptime); + fprintf(irp->f, ")"); +} + +static void ir_print_array_to_vector(IrPrintGen *irp, IrInstGenArrayToVector *instruction) { + fprintf(irp->f, "ArrayToVector("); + ir_print_other_inst_gen(irp, instruction->array); + fprintf(irp->f, ")"); +} + +static void ir_print_vector_to_array(IrPrintGen *irp, IrInstGenVectorToArray *instruction) { + fprintf(irp->f, "VectorToArray("); + ir_print_other_inst_gen(irp, instruction->vector); + fprintf(irp->f, ")result="); + ir_print_other_inst_gen(irp, instruction->result_loc); +} + +static void ir_print_ptr_of_array_to_slice(IrPrintGen *irp, IrInstGenPtrOfArrayToSlice *instruction) { + fprintf(irp->f, "PtrOfArrayToSlice("); + ir_print_other_inst_gen(irp, instruction->operand); + fprintf(irp->f, ")result="); + ir_print_other_inst_gen(irp, instruction->result_loc); +} + +static void ir_print_assert_zero(IrPrintGen *irp, IrInstGenAssertZero *instruction) { + fprintf(irp->f, "AssertZero("); + ir_print_other_inst_gen(irp, instruction->target); + fprintf(irp->f, ")"); +} + +static void ir_print_assert_non_null(IrPrintGen *irp, IrInstGenAssertNonNull *instruction) { + fprintf(irp->f, "AssertNonNull("); + ir_print_other_inst_gen(irp, instruction->target); + fprintf(irp->f, ")"); +} + +static void ir_print_alloca_src(IrPrintSrc *irp, IrInstSrcAlloca *instruction) { + fprintf(irp->f, "Alloca(align="); + ir_print_other_inst_src(irp, instruction->align); + fprintf(irp->f, ",name=%s)", instruction->name_hint); +} + +static void ir_print_alloca_gen(IrPrintGen *irp, IrInstGenAlloca *instruction) { + fprintf(irp->f, "Alloca(align=%" PRIu32 ",name=%s)", instruction->align, instruction->name_hint); +} + +static void ir_print_end_expr(IrPrintSrc *irp, IrInstSrcEndExpr *instruction) { + fprintf(irp->f, "EndExpr(result="); + ir_print_result_loc(irp, instruction->result_loc); + fprintf(irp->f, ",value="); + ir_print_other_inst_src(irp, instruction->value); + fprintf(irp->f, ")"); +} + +static void ir_print_int_to_err(IrPrintSrc *irp, IrInstSrcIntToErr *instruction) { + fprintf(irp->f, "inttoerr "); + ir_print_other_inst_src(irp, instruction->target); +} + +static void ir_print_int_to_err(IrPrintGen *irp, IrInstGenIntToErr *instruction) { + fprintf(irp->f, "inttoerr "); + ir_print_other_inst_gen(irp, instruction->target); +} + +static void ir_print_err_to_int(IrPrintSrc *irp, IrInstSrcErrToInt *instruction) { + fprintf(irp->f, "errtoint "); + ir_print_other_inst_src(irp, instruction->target); +} + +static void ir_print_err_to_int(IrPrintGen *irp, IrInstGenErrToInt *instruction) { + fprintf(irp->f, "errtoint "); + ir_print_other_inst_gen(irp, instruction->target); +} + +static void ir_print_check_switch_prongs(IrPrintSrc *irp, IrInstSrcCheckSwitchProngs *instruction) { + fprintf(irp->f, "@checkSwitchProngs("); + ir_print_other_inst_src(irp, instruction->target_value); + fprintf(irp->f, ","); + for (size_t i = 0; i < instruction->range_count; i += 1) { + if (i != 0) + fprintf(irp->f, ","); + ir_print_other_inst_src(irp, instruction->ranges[i].start); + fprintf(irp->f, "..."); + ir_print_other_inst_src(irp, instruction->ranges[i].end); + } + const char *have_else_str = instruction->else_prong != nullptr ? "yes" : "no"; + fprintf(irp->f, ")else:%s", have_else_str); +} + +static void ir_print_check_statement_is_void(IrPrintSrc *irp, IrInstSrcCheckStatementIsVoid *instruction) { + fprintf(irp->f, "@checkStatementIsVoid("); + ir_print_other_inst_src(irp, instruction->statement_value); + fprintf(irp->f, ")"); +} + +static void ir_print_type_name(IrPrintSrc *irp, IrInstSrcTypeName *instruction) { + fprintf(irp->f, "typename "); + ir_print_other_inst_src(irp, instruction->type_value); +} + +static void ir_print_tag_name(IrPrintSrc *irp, IrInstSrcTagName *instruction) { + fprintf(irp->f, "tagname "); + ir_print_other_inst_src(irp, instruction->target); +} + +static void ir_print_tag_name(IrPrintGen *irp, IrInstGenTagName *instruction) { + fprintf(irp->f, "tagname "); + ir_print_other_inst_gen(irp, instruction->target); +} + +static void ir_print_ptr_type(IrPrintSrc *irp, IrInstSrcPtrType *instruction) { + fprintf(irp->f, "&"); + if (instruction->align_value != nullptr) { + fprintf(irp->f, "align("); + ir_print_other_inst_src(irp, instruction->align_value); + fprintf(irp->f, ")"); + } + const char *const_str = instruction->is_const ? "const " : ""; + const char *volatile_str = instruction->is_volatile ? "volatile " : ""; + fprintf(irp->f, ":%" PRIu32 ":%" PRIu32 " %s%s", instruction->bit_offset_start, instruction->host_int_bytes, + const_str, volatile_str); + ir_print_other_inst_src(irp, instruction->child_type); +} + +static void ir_print_decl_ref(IrPrintSrc *irp, IrInstSrcDeclRef *instruction) { + const char *ptr_str = (instruction->lval != LValNone) ? "ptr " : ""; + fprintf(irp->f, "declref %s%s", ptr_str, buf_ptr(instruction->tld->name)); +} + +static void ir_print_panic(IrPrintSrc *irp, IrInstSrcPanic *instruction) { + fprintf(irp->f, "@panic("); + ir_print_other_inst_src(irp, instruction->msg); + fprintf(irp->f, ")"); +} + +static void ir_print_panic(IrPrintGen *irp, IrInstGenPanic *instruction) { + fprintf(irp->f, "@panic("); + ir_print_other_inst_gen(irp, instruction->msg); + fprintf(irp->f, ")"); +} + +static void ir_print_field_parent_ptr(IrPrintSrc *irp, IrInstSrcFieldParentPtr *instruction) { + fprintf(irp->f, "@fieldParentPtr("); + ir_print_other_inst_src(irp, instruction->type_value); + fprintf(irp->f, ","); + ir_print_other_inst_src(irp, instruction->field_name); + fprintf(irp->f, ","); + ir_print_other_inst_src(irp, instruction->field_ptr); + fprintf(irp->f, ")"); +} + +static void ir_print_field_parent_ptr(IrPrintGen *irp, IrInstGenFieldParentPtr *instruction) { + fprintf(irp->f, "@fieldParentPtr(%s,", buf_ptr(instruction->field->name)); + ir_print_other_inst_gen(irp, instruction->field_ptr); + fprintf(irp->f, ")"); +} + +static void ir_print_byte_offset_of(IrPrintSrc *irp, IrInstSrcByteOffsetOf *instruction) { + fprintf(irp->f, "@byte_offset_of("); + ir_print_other_inst_src(irp, instruction->type_value); + fprintf(irp->f, ","); + ir_print_other_inst_src(irp, instruction->field_name); + fprintf(irp->f, ")"); +} + +static void ir_print_bit_offset_of(IrPrintSrc *irp, IrInstSrcBitOffsetOf *instruction) { + fprintf(irp->f, "@bit_offset_of("); + ir_print_other_inst_src(irp, instruction->type_value); + fprintf(irp->f, ","); + ir_print_other_inst_src(irp, instruction->field_name); + fprintf(irp->f, ")"); +} + +static void ir_print_type_info(IrPrintSrc *irp, IrInstSrcTypeInfo *instruction) { + fprintf(irp->f, "@typeInfo("); + ir_print_other_inst_src(irp, instruction->type_value); + fprintf(irp->f, ")"); +} + +static void ir_print_type(IrPrintSrc *irp, IrInstSrcType *instruction) { + fprintf(irp->f, "@Type("); + ir_print_other_inst_src(irp, instruction->type_info); + fprintf(irp->f, ")"); +} + +static void ir_print_has_field(IrPrintSrc *irp, IrInstSrcHasField *instruction) { + fprintf(irp->f, "@hasField("); + ir_print_other_inst_src(irp, instruction->container_type); + fprintf(irp->f, ","); + ir_print_other_inst_src(irp, instruction->field_name); + fprintf(irp->f, ")"); +} + +static void ir_print_set_eval_branch_quota(IrPrintSrc *irp, IrInstSrcSetEvalBranchQuota *instruction) { + fprintf(irp->f, "@setEvalBranchQuota("); + ir_print_other_inst_src(irp, instruction->new_quota); + fprintf(irp->f, ")"); +} + +static void ir_print_align_cast(IrPrintSrc *irp, IrInstSrcAlignCast *instruction) { + fprintf(irp->f, "@alignCast("); + ir_print_other_inst_src(irp, instruction->align_bytes); + fprintf(irp->f, ","); + ir_print_other_inst_src(irp, instruction->target); + fprintf(irp->f, ")"); +} + +static void ir_print_align_cast(IrPrintGen *irp, IrInstGenAlignCast *instruction) { + fprintf(irp->f, "@alignCast("); + ir_print_other_inst_gen(irp, instruction->target); + fprintf(irp->f, ")"); +} + +static void ir_print_resolve_result(IrPrintSrc *irp, IrInstSrcResolveResult *instruction) { + fprintf(irp->f, "ResolveResult("); + ir_print_result_loc(irp, instruction->result_loc); + fprintf(irp->f, ")"); +} + +static void ir_print_reset_result(IrPrintSrc *irp, IrInstSrcResetResult *instruction) { + fprintf(irp->f, "ResetResult("); + ir_print_result_loc(irp, instruction->result_loc); + fprintf(irp->f, ")"); +} + +static void ir_print_set_align_stack(IrPrintSrc *irp, IrInstSrcSetAlignStack *instruction) { + fprintf(irp->f, "@setAlignStack("); + ir_print_other_inst_src(irp, instruction->align_bytes); + fprintf(irp->f, ")"); +} + +static void ir_print_arg_type(IrPrintSrc *irp, IrInstSrcArgType *instruction) { + fprintf(irp->f, "@ArgType("); + ir_print_other_inst_src(irp, instruction->fn_type); + fprintf(irp->f, ","); + ir_print_other_inst_src(irp, instruction->arg_index); + fprintf(irp->f, ")"); +} + +static void ir_print_enum_tag_type(IrPrintSrc *irp, IrInstSrcTagType *instruction) { + fprintf(irp->f, "@TagType("); + ir_print_other_inst_src(irp, instruction->target); + fprintf(irp->f, ")"); +} + +static void ir_print_export(IrPrintSrc *irp, IrInstSrcExport *instruction) { + fprintf(irp->f, "@export("); + ir_print_other_inst_src(irp, instruction->target); + fprintf(irp->f, ","); + ir_print_other_inst_src(irp, instruction->options); + fprintf(irp->f, ")"); +} + +static void ir_print_error_return_trace(IrPrintSrc *irp, IrInstSrcErrorReturnTrace *instruction) { + fprintf(irp->f, "@errorReturnTrace("); + switch (instruction->optional) { + case IrInstErrorReturnTraceNull: + fprintf(irp->f, "Null"); + break; + case IrInstErrorReturnTraceNonNull: + fprintf(irp->f, "NonNull"); + break; + } + fprintf(irp->f, ")"); +} + +static void ir_print_error_return_trace(IrPrintGen *irp, IrInstGenErrorReturnTrace *instruction) { + fprintf(irp->f, "@errorReturnTrace("); + switch (instruction->optional) { + case IrInstErrorReturnTraceNull: + fprintf(irp->f, "Null"); + break; + case IrInstErrorReturnTraceNonNull: + fprintf(irp->f, "NonNull"); + break; + } + fprintf(irp->f, ")"); +} + +static void ir_print_error_union(IrPrintSrc *irp, IrInstSrcErrorUnion *instruction) { + ir_print_other_inst_src(irp, instruction->err_set); + fprintf(irp->f, "!"); + ir_print_other_inst_src(irp, instruction->payload); +} + +static void ir_print_atomic_rmw(IrPrintSrc *irp, IrInstSrcAtomicRmw *instruction) { + fprintf(irp->f, "@atomicRmw("); + ir_print_other_inst_src(irp, instruction->operand_type); + fprintf(irp->f, ","); + ir_print_other_inst_src(irp, instruction->ptr); + fprintf(irp->f, ","); + ir_print_other_inst_src(irp, instruction->op); + fprintf(irp->f, ","); + ir_print_other_inst_src(irp, instruction->operand); + fprintf(irp->f, ","); + ir_print_other_inst_src(irp, instruction->ordering); + fprintf(irp->f, ")"); +} + +static void ir_print_atomic_rmw(IrPrintGen *irp, IrInstGenAtomicRmw *instruction) { + fprintf(irp->f, "@atomicRmw("); + ir_print_other_inst_gen(irp, instruction->ptr); + fprintf(irp->f, ",[TODO print op],"); + ir_print_other_inst_gen(irp, instruction->operand); + fprintf(irp->f, ",%s)", atomic_order_str(instruction->ordering)); +} + +static void ir_print_atomic_load(IrPrintSrc *irp, IrInstSrcAtomicLoad *instruction) { + fprintf(irp->f, "@atomicLoad("); + ir_print_other_inst_src(irp, instruction->operand_type); + fprintf(irp->f, ","); + ir_print_other_inst_src(irp, instruction->ptr); + fprintf(irp->f, ","); + ir_print_other_inst_src(irp, instruction->ordering); + fprintf(irp->f, ")"); +} + +static void ir_print_atomic_load(IrPrintGen *irp, IrInstGenAtomicLoad *instruction) { + fprintf(irp->f, "@atomicLoad("); + ir_print_other_inst_gen(irp, instruction->ptr); + fprintf(irp->f, ",%s)", atomic_order_str(instruction->ordering)); +} + +static void ir_print_atomic_store(IrPrintSrc *irp, IrInstSrcAtomicStore *instruction) { + fprintf(irp->f, "@atomicStore("); + ir_print_other_inst_src(irp, instruction->operand_type); + fprintf(irp->f, ","); + ir_print_other_inst_src(irp, instruction->ptr); + fprintf(irp->f, ","); + ir_print_other_inst_src(irp, instruction->value); + fprintf(irp->f, ","); + ir_print_other_inst_src(irp, instruction->ordering); + fprintf(irp->f, ")"); +} + +static void ir_print_atomic_store(IrPrintGen *irp, IrInstGenAtomicStore *instruction) { + fprintf(irp->f, "@atomicStore("); + ir_print_other_inst_gen(irp, instruction->ptr); + fprintf(irp->f, ","); + ir_print_other_inst_gen(irp, instruction->value); + fprintf(irp->f, ",%s)", atomic_order_str(instruction->ordering)); +} + + +static void ir_print_save_err_ret_addr(IrPrintSrc *irp, IrInstSrcSaveErrRetAddr *instruction) { + fprintf(irp->f, "@saveErrRetAddr()"); +} + +static void ir_print_save_err_ret_addr(IrPrintGen *irp, IrInstGenSaveErrRetAddr *instruction) { + fprintf(irp->f, "@saveErrRetAddr()"); +} + +static void ir_print_add_implicit_return_type(IrPrintSrc *irp, IrInstSrcAddImplicitReturnType *instruction) { + fprintf(irp->f, "@addImplicitReturnType("); + ir_print_other_inst_src(irp, instruction->value); + fprintf(irp->f, ")"); +} + +static void ir_print_float_op(IrPrintSrc *irp, IrInstSrcFloatOp *instruction) { + fprintf(irp->f, "@%s(", float_op_to_name(instruction->fn_id)); + ir_print_other_inst_src(irp, instruction->operand); + fprintf(irp->f, ")"); +} + +static void ir_print_float_op(IrPrintGen *irp, IrInstGenFloatOp *instruction) { + fprintf(irp->f, "@%s(", float_op_to_name(instruction->fn_id)); + ir_print_other_inst_gen(irp, instruction->operand); + fprintf(irp->f, ")"); +} + +static void ir_print_mul_add(IrPrintSrc *irp, IrInstSrcMulAdd *instruction) { + fprintf(irp->f, "@mulAdd("); + ir_print_other_inst_src(irp, instruction->type_value); + fprintf(irp->f, ","); + ir_print_other_inst_src(irp, instruction->op1); + fprintf(irp->f, ","); + ir_print_other_inst_src(irp, instruction->op2); + fprintf(irp->f, ","); + ir_print_other_inst_src(irp, instruction->op3); + fprintf(irp->f, ")"); +} + +static void ir_print_mul_add(IrPrintGen *irp, IrInstGenMulAdd *instruction) { + fprintf(irp->f, "@mulAdd("); + ir_print_other_inst_gen(irp, instruction->op1); + fprintf(irp->f, ","); + ir_print_other_inst_gen(irp, instruction->op2); + fprintf(irp->f, ","); + ir_print_other_inst_gen(irp, instruction->op3); + fprintf(irp->f, ")"); +} + +static void ir_print_decl_var_gen(IrPrintGen *irp, IrInstGenDeclVar *decl_var_instruction) { + ZigVar *var = decl_var_instruction->var; + const char *var_or_const = decl_var_instruction->var->gen_is_const ? "const" : "var"; + const char *name = decl_var_instruction->var->name; + fprintf(irp->f, "%s %s: %s align(%u) = ", var_or_const, name, buf_ptr(&var->var_type->name), + var->align_bytes); + + ir_print_other_inst_gen(irp, decl_var_instruction->var_ptr); +} + +static void ir_print_has_decl(IrPrintSrc *irp, IrInstSrcHasDecl *instruction) { + fprintf(irp->f, "@hasDecl("); + ir_print_other_inst_src(irp, instruction->container); + fprintf(irp->f, ","); + ir_print_other_inst_src(irp, instruction->name); + fprintf(irp->f, ")"); +} + +static void ir_print_undeclared_ident(IrPrintSrc *irp, IrInstSrcUndeclaredIdent *instruction) { + fprintf(irp->f, "@undeclaredIdent(%s)", buf_ptr(instruction->name)); +} + +static void ir_print_union_init_named_field(IrPrintSrc *irp, IrInstSrcUnionInitNamedField *instruction) { + fprintf(irp->f, "@unionInit("); + ir_print_other_inst_src(irp, instruction->union_type); + fprintf(irp->f, ", "); + ir_print_other_inst_src(irp, instruction->field_name); + fprintf(irp->f, ", "); + ir_print_other_inst_src(irp, instruction->field_result_loc); + fprintf(irp->f, ", "); + ir_print_other_inst_src(irp, instruction->result_loc); + fprintf(irp->f, ")"); +} + +static void ir_print_suspend_begin(IrPrintSrc *irp, IrInstSrcSuspendBegin *instruction) { + fprintf(irp->f, "@suspendBegin()"); +} + +static void ir_print_suspend_begin(IrPrintGen *irp, IrInstGenSuspendBegin *instruction) { + fprintf(irp->f, "@suspendBegin()"); +} + +static void ir_print_suspend_finish(IrPrintSrc *irp, IrInstSrcSuspendFinish *instruction) { + fprintf(irp->f, "@suspendFinish()"); +} + +static void ir_print_suspend_finish(IrPrintGen *irp, IrInstGenSuspendFinish *instruction) { + fprintf(irp->f, "@suspendFinish()"); +} + +static void ir_print_resume(IrPrintSrc *irp, IrInstSrcResume *instruction) { + fprintf(irp->f, "resume "); + ir_print_other_inst_src(irp, instruction->frame); +} + +static void ir_print_resume(IrPrintGen *irp, IrInstGenResume *instruction) { + fprintf(irp->f, "resume "); + ir_print_other_inst_gen(irp, instruction->frame); +} + +static void ir_print_await_src(IrPrintSrc *irp, IrInstSrcAwait *instruction) { + fprintf(irp->f, "@await("); + ir_print_other_inst_src(irp, instruction->frame); + fprintf(irp->f, ","); + ir_print_result_loc(irp, instruction->result_loc); + fprintf(irp->f, ")"); +} + +static void ir_print_await_gen(IrPrintGen *irp, IrInstGenAwait *instruction) { + fprintf(irp->f, "@await("); + ir_print_other_inst_gen(irp, instruction->frame); + fprintf(irp->f, ","); + ir_print_other_inst_gen(irp, instruction->result_loc); + fprintf(irp->f, ")"); +} + +static void ir_print_spill_begin(IrPrintSrc *irp, IrInstSrcSpillBegin *instruction) { + fprintf(irp->f, "@spillBegin("); + ir_print_other_inst_src(irp, instruction->operand); + fprintf(irp->f, ")"); +} + +static void ir_print_spill_begin(IrPrintGen *irp, IrInstGenSpillBegin *instruction) { + fprintf(irp->f, "@spillBegin("); + ir_print_other_inst_gen(irp, instruction->operand); + fprintf(irp->f, ")"); +} + +static void ir_print_spill_end(IrPrintSrc *irp, IrInstSrcSpillEnd *instruction) { + fprintf(irp->f, "@spillEnd("); + ir_print_other_inst_src(irp, &instruction->begin->base); + fprintf(irp->f, ")"); +} + +static void ir_print_spill_end(IrPrintGen *irp, IrInstGenSpillEnd *instruction) { + fprintf(irp->f, "@spillEnd("); + ir_print_other_inst_gen(irp, &instruction->begin->base); + fprintf(irp->f, ")"); +} + +static void ir_print_vector_extract_elem(IrPrintGen *irp, IrInstGenVectorExtractElem *instruction) { + fprintf(irp->f, "@vectorExtractElem("); + ir_print_other_inst_gen(irp, instruction->vector); + fprintf(irp->f, ","); + ir_print_other_inst_gen(irp, instruction->index); + fprintf(irp->f, ")"); +} + +static void ir_print_inst_src(IrPrintSrc *irp, IrInstSrc *instruction, bool trailing) { + ir_print_prefix_src(irp, instruction, trailing); + switch (instruction->id) { + case IrInstSrcIdInvalid: + zig_unreachable(); + case IrInstSrcIdReturn: + ir_print_return_src(irp, (IrInstSrcReturn *)instruction); + break; + case IrInstSrcIdConst: + ir_print_const(irp, (IrInstSrcConst *)instruction); + break; + case IrInstSrcIdBinOp: + ir_print_bin_op(irp, (IrInstSrcBinOp *)instruction); + break; + case IrInstSrcIdMergeErrSets: + ir_print_merge_err_sets(irp, (IrInstSrcMergeErrSets *)instruction); + break; + case IrInstSrcIdDeclVar: + ir_print_decl_var_src(irp, (IrInstSrcDeclVar *)instruction); + break; + case IrInstSrcIdCallExtra: + ir_print_call_extra(irp, (IrInstSrcCallExtra *)instruction); + break; + case IrInstSrcIdAsyncCallExtra: + ir_print_async_call_extra(irp, (IrInstSrcAsyncCallExtra *)instruction); + break; + case IrInstSrcIdCall: + ir_print_call_src(irp, (IrInstSrcCall *)instruction); + break; + case IrInstSrcIdCallArgs: + ir_print_call_args(irp, (IrInstSrcCallArgs *)instruction); + break; + case IrInstSrcIdUnOp: + ir_print_un_op(irp, (IrInstSrcUnOp *)instruction); + break; + case IrInstSrcIdCondBr: + ir_print_cond_br(irp, (IrInstSrcCondBr *)instruction); + break; + case IrInstSrcIdBr: + ir_print_br(irp, (IrInstSrcBr *)instruction); + break; + case IrInstSrcIdPhi: + ir_print_phi(irp, (IrInstSrcPhi *)instruction); + break; + case IrInstSrcIdContainerInitList: + ir_print_container_init_list(irp, (IrInstSrcContainerInitList *)instruction); + break; + case IrInstSrcIdContainerInitFields: + ir_print_container_init_fields(irp, (IrInstSrcContainerInitFields *)instruction); + break; + case IrInstSrcIdUnreachable: + ir_print_unreachable(irp, (IrInstSrcUnreachable *)instruction); + break; + case IrInstSrcIdElemPtr: + ir_print_elem_ptr(irp, (IrInstSrcElemPtr *)instruction); + break; + case IrInstSrcIdVarPtr: + ir_print_var_ptr(irp, (IrInstSrcVarPtr *)instruction); + break; + case IrInstSrcIdLoadPtr: + ir_print_load_ptr(irp, (IrInstSrcLoadPtr *)instruction); + break; + case IrInstSrcIdStorePtr: + ir_print_store_ptr(irp, (IrInstSrcStorePtr *)instruction); + break; + case IrInstSrcIdTypeOf: + ir_print_typeof(irp, (IrInstSrcTypeOf *)instruction); + break; + case IrInstSrcIdFieldPtr: + ir_print_field_ptr(irp, (IrInstSrcFieldPtr *)instruction); + break; + case IrInstSrcIdSetCold: + ir_print_set_cold(irp, (IrInstSrcSetCold *)instruction); + break; + case IrInstSrcIdSetRuntimeSafety: + ir_print_set_runtime_safety(irp, (IrInstSrcSetRuntimeSafety *)instruction); + break; + case IrInstSrcIdSetFloatMode: + ir_print_set_float_mode(irp, (IrInstSrcSetFloatMode *)instruction); + break; + case IrInstSrcIdArrayType: + ir_print_array_type(irp, (IrInstSrcArrayType *)instruction); + break; + case IrInstSrcIdSliceType: + ir_print_slice_type(irp, (IrInstSrcSliceType *)instruction); + break; + case IrInstSrcIdAnyFrameType: + ir_print_any_frame_type(irp, (IrInstSrcAnyFrameType *)instruction); + break; + case IrInstSrcIdAsm: + ir_print_asm_src(irp, (IrInstSrcAsm *)instruction); + break; + case IrInstSrcIdSizeOf: + ir_print_size_of(irp, (IrInstSrcSizeOf *)instruction); + break; + case IrInstSrcIdTestNonNull: + ir_print_test_non_null(irp, (IrInstSrcTestNonNull *)instruction); + break; + case IrInstSrcIdOptionalUnwrapPtr: + ir_print_optional_unwrap_ptr(irp, (IrInstSrcOptionalUnwrapPtr *)instruction); + break; + case IrInstSrcIdPopCount: + ir_print_pop_count(irp, (IrInstSrcPopCount *)instruction); + break; + case IrInstSrcIdCtz: + ir_print_ctz(irp, (IrInstSrcCtz *)instruction); + break; + case IrInstSrcIdBswap: + ir_print_bswap(irp, (IrInstSrcBswap *)instruction); + break; + case IrInstSrcIdBitReverse: + ir_print_bit_reverse(irp, (IrInstSrcBitReverse *)instruction); + break; + case IrInstSrcIdSwitchBr: + ir_print_switch_br(irp, (IrInstSrcSwitchBr *)instruction); + break; + case IrInstSrcIdSwitchVar: + ir_print_switch_var(irp, (IrInstSrcSwitchVar *)instruction); + break; + case IrInstSrcIdSwitchElseVar: + ir_print_switch_else_var(irp, (IrInstSrcSwitchElseVar *)instruction); + break; + case IrInstSrcIdSwitchTarget: + ir_print_switch_target(irp, (IrInstSrcSwitchTarget *)instruction); + break; + case IrInstSrcIdImport: + ir_print_import(irp, (IrInstSrcImport *)instruction); + break; + case IrInstSrcIdRef: + ir_print_ref(irp, (IrInstSrcRef *)instruction); + break; + case IrInstSrcIdCompileErr: + ir_print_compile_err(irp, (IrInstSrcCompileErr *)instruction); + break; + case IrInstSrcIdCompileLog: + ir_print_compile_log(irp, (IrInstSrcCompileLog *)instruction); + break; + case IrInstSrcIdErrName: + ir_print_err_name(irp, (IrInstSrcErrName *)instruction); + break; + case IrInstSrcIdCImport: + ir_print_c_import(irp, (IrInstSrcCImport *)instruction); + break; + case IrInstSrcIdCInclude: + ir_print_c_include(irp, (IrInstSrcCInclude *)instruction); + break; + case IrInstSrcIdCDefine: + ir_print_c_define(irp, (IrInstSrcCDefine *)instruction); + break; + case IrInstSrcIdCUndef: + ir_print_c_undef(irp, (IrInstSrcCUndef *)instruction); + break; + case IrInstSrcIdEmbedFile: + ir_print_embed_file(irp, (IrInstSrcEmbedFile *)instruction); + break; + case IrInstSrcIdCmpxchg: + ir_print_cmpxchg_src(irp, (IrInstSrcCmpxchg *)instruction); + break; + case IrInstSrcIdFence: + ir_print_fence(irp, (IrInstSrcFence *)instruction); + break; + case IrInstSrcIdTruncate: + ir_print_truncate(irp, (IrInstSrcTruncate *)instruction); + break; + case IrInstSrcIdIntCast: + ir_print_int_cast(irp, (IrInstSrcIntCast *)instruction); + break; + case IrInstSrcIdFloatCast: + ir_print_float_cast(irp, (IrInstSrcFloatCast *)instruction); + break; + case IrInstSrcIdErrSetCast: + ir_print_err_set_cast(irp, (IrInstSrcErrSetCast *)instruction); + break; + case IrInstSrcIdIntToFloat: + ir_print_int_to_float(irp, (IrInstSrcIntToFloat *)instruction); + break; + case IrInstSrcIdFloatToInt: + ir_print_float_to_int(irp, (IrInstSrcFloatToInt *)instruction); + break; + case IrInstSrcIdBoolToInt: + ir_print_bool_to_int(irp, (IrInstSrcBoolToInt *)instruction); + break; + case IrInstSrcIdVectorType: + ir_print_vector_type(irp, (IrInstSrcVectorType *)instruction); + break; + case IrInstSrcIdShuffleVector: + ir_print_shuffle_vector(irp, (IrInstSrcShuffleVector *)instruction); + break; + case IrInstSrcIdSplat: + ir_print_splat_src(irp, (IrInstSrcSplat *)instruction); + break; + case IrInstSrcIdBoolNot: + ir_print_bool_not(irp, (IrInstSrcBoolNot *)instruction); + break; + case IrInstSrcIdMemset: + ir_print_memset(irp, (IrInstSrcMemset *)instruction); + break; + case IrInstSrcIdMemcpy: + ir_print_memcpy(irp, (IrInstSrcMemcpy *)instruction); + break; + case IrInstSrcIdSlice: + ir_print_slice_src(irp, (IrInstSrcSlice *)instruction); + break; + case IrInstSrcIdBreakpoint: + ir_print_breakpoint(irp, (IrInstSrcBreakpoint *)instruction); + break; + case IrInstSrcIdReturnAddress: + ir_print_return_address(irp, (IrInstSrcReturnAddress *)instruction); + break; + case IrInstSrcIdFrameAddress: + ir_print_frame_address(irp, (IrInstSrcFrameAddress *)instruction); + break; + case IrInstSrcIdFrameHandle: + ir_print_handle(irp, (IrInstSrcFrameHandle *)instruction); + break; + case IrInstSrcIdFrameType: + ir_print_frame_type(irp, (IrInstSrcFrameType *)instruction); + break; + case IrInstSrcIdFrameSize: + ir_print_frame_size_src(irp, (IrInstSrcFrameSize *)instruction); + break; + case IrInstSrcIdAlignOf: + ir_print_align_of(irp, (IrInstSrcAlignOf *)instruction); + break; + case IrInstSrcIdOverflowOp: + ir_print_overflow_op(irp, (IrInstSrcOverflowOp *)instruction); + break; + case IrInstSrcIdTestErr: + ir_print_test_err_src(irp, (IrInstSrcTestErr *)instruction); + break; + case IrInstSrcIdUnwrapErrCode: + ir_print_unwrap_err_code(irp, (IrInstSrcUnwrapErrCode *)instruction); + break; + case IrInstSrcIdUnwrapErrPayload: + ir_print_unwrap_err_payload(irp, (IrInstSrcUnwrapErrPayload *)instruction); + break; + case IrInstSrcIdFnProto: + ir_print_fn_proto(irp, (IrInstSrcFnProto *)instruction); + break; + case IrInstSrcIdTestComptime: + ir_print_test_comptime(irp, (IrInstSrcTestComptime *)instruction); + break; + case IrInstSrcIdPtrCast: + ir_print_ptr_cast_src(irp, (IrInstSrcPtrCast *)instruction); + break; + case IrInstSrcIdBitCast: + ir_print_bit_cast_src(irp, (IrInstSrcBitCast *)instruction); + break; + case IrInstSrcIdPtrToInt: + ir_print_ptr_to_int(irp, (IrInstSrcPtrToInt *)instruction); + break; + case IrInstSrcIdIntToPtr: + ir_print_int_to_ptr(irp, (IrInstSrcIntToPtr *)instruction); + break; + case IrInstSrcIdIntToEnum: + ir_print_int_to_enum(irp, (IrInstSrcIntToEnum *)instruction); + break; + case IrInstSrcIdIntToErr: + ir_print_int_to_err(irp, (IrInstSrcIntToErr *)instruction); + break; + case IrInstSrcIdErrToInt: + ir_print_err_to_int(irp, (IrInstSrcErrToInt *)instruction); + break; + case IrInstSrcIdCheckSwitchProngs: + ir_print_check_switch_prongs(irp, (IrInstSrcCheckSwitchProngs *)instruction); + break; + case IrInstSrcIdCheckStatementIsVoid: + ir_print_check_statement_is_void(irp, (IrInstSrcCheckStatementIsVoid *)instruction); + break; + case IrInstSrcIdTypeName: + ir_print_type_name(irp, (IrInstSrcTypeName *)instruction); + break; + case IrInstSrcIdTagName: + ir_print_tag_name(irp, (IrInstSrcTagName *)instruction); + break; + case IrInstSrcIdPtrType: + ir_print_ptr_type(irp, (IrInstSrcPtrType *)instruction); + break; + case IrInstSrcIdDeclRef: + ir_print_decl_ref(irp, (IrInstSrcDeclRef *)instruction); + break; + case IrInstSrcIdPanic: + ir_print_panic(irp, (IrInstSrcPanic *)instruction); + break; + case IrInstSrcIdFieldParentPtr: + ir_print_field_parent_ptr(irp, (IrInstSrcFieldParentPtr *)instruction); + break; + case IrInstSrcIdByteOffsetOf: + ir_print_byte_offset_of(irp, (IrInstSrcByteOffsetOf *)instruction); + break; + case IrInstSrcIdBitOffsetOf: + ir_print_bit_offset_of(irp, (IrInstSrcBitOffsetOf *)instruction); + break; + case IrInstSrcIdTypeInfo: + ir_print_type_info(irp, (IrInstSrcTypeInfo *)instruction); + break; + case IrInstSrcIdType: + ir_print_type(irp, (IrInstSrcType *)instruction); + break; + case IrInstSrcIdHasField: + ir_print_has_field(irp, (IrInstSrcHasField *)instruction); + break; + case IrInstSrcIdSetEvalBranchQuota: + ir_print_set_eval_branch_quota(irp, (IrInstSrcSetEvalBranchQuota *)instruction); + break; + case IrInstSrcIdAlignCast: + ir_print_align_cast(irp, (IrInstSrcAlignCast *)instruction); + break; + case IrInstSrcIdImplicitCast: + ir_print_implicit_cast(irp, (IrInstSrcImplicitCast *)instruction); + break; + case IrInstSrcIdResolveResult: + ir_print_resolve_result(irp, (IrInstSrcResolveResult *)instruction); + break; + case IrInstSrcIdResetResult: + ir_print_reset_result(irp, (IrInstSrcResetResult *)instruction); + break; + case IrInstSrcIdSetAlignStack: + ir_print_set_align_stack(irp, (IrInstSrcSetAlignStack *)instruction); + break; + case IrInstSrcIdArgType: + ir_print_arg_type(irp, (IrInstSrcArgType *)instruction); + break; + case IrInstSrcIdTagType: + ir_print_enum_tag_type(irp, (IrInstSrcTagType *)instruction); + break; + case IrInstSrcIdExport: + ir_print_export(irp, (IrInstSrcExport *)instruction); + break; + case IrInstSrcIdErrorReturnTrace: + ir_print_error_return_trace(irp, (IrInstSrcErrorReturnTrace *)instruction); + break; + case IrInstSrcIdErrorUnion: + ir_print_error_union(irp, (IrInstSrcErrorUnion *)instruction); + break; + case IrInstSrcIdAtomicRmw: + ir_print_atomic_rmw(irp, (IrInstSrcAtomicRmw *)instruction); + break; + case IrInstSrcIdSaveErrRetAddr: + ir_print_save_err_ret_addr(irp, (IrInstSrcSaveErrRetAddr *)instruction); + break; + case IrInstSrcIdAddImplicitReturnType: + ir_print_add_implicit_return_type(irp, (IrInstSrcAddImplicitReturnType *)instruction); + break; + case IrInstSrcIdFloatOp: + ir_print_float_op(irp, (IrInstSrcFloatOp *)instruction); + break; + case IrInstSrcIdMulAdd: + ir_print_mul_add(irp, (IrInstSrcMulAdd *)instruction); + break; + case IrInstSrcIdAtomicLoad: + ir_print_atomic_load(irp, (IrInstSrcAtomicLoad *)instruction); + break; + case IrInstSrcIdAtomicStore: + ir_print_atomic_store(irp, (IrInstSrcAtomicStore *)instruction); + break; + case IrInstSrcIdEnumToInt: + ir_print_enum_to_int(irp, (IrInstSrcEnumToInt *)instruction); + break; + case IrInstSrcIdCheckRuntimeScope: + ir_print_check_runtime_scope(irp, (IrInstSrcCheckRuntimeScope *)instruction); + break; + case IrInstSrcIdHasDecl: + ir_print_has_decl(irp, (IrInstSrcHasDecl *)instruction); + break; + case IrInstSrcIdUndeclaredIdent: + ir_print_undeclared_ident(irp, (IrInstSrcUndeclaredIdent *)instruction); + break; + case IrInstSrcIdAlloca: + ir_print_alloca_src(irp, (IrInstSrcAlloca *)instruction); + break; + case IrInstSrcIdEndExpr: + ir_print_end_expr(irp, (IrInstSrcEndExpr *)instruction); + break; + case IrInstSrcIdUnionInitNamedField: + ir_print_union_init_named_field(irp, (IrInstSrcUnionInitNamedField *)instruction); + break; + case IrInstSrcIdSuspendBegin: + ir_print_suspend_begin(irp, (IrInstSrcSuspendBegin *)instruction); + break; + case IrInstSrcIdSuspendFinish: + ir_print_suspend_finish(irp, (IrInstSrcSuspendFinish *)instruction); + break; + case IrInstSrcIdResume: + ir_print_resume(irp, (IrInstSrcResume *)instruction); + break; + case IrInstSrcIdAwait: + ir_print_await_src(irp, (IrInstSrcAwait *)instruction); + break; + case IrInstSrcIdSpillBegin: + ir_print_spill_begin(irp, (IrInstSrcSpillBegin *)instruction); + break; + case IrInstSrcIdSpillEnd: + ir_print_spill_end(irp, (IrInstSrcSpillEnd *)instruction); + break; + case IrInstSrcIdClz: + ir_print_clz(irp, (IrInstSrcClz *)instruction); + break; + case IrInstSrcIdWasmMemorySize: + ir_print_wasm_memory_size(irp, (IrInstSrcWasmMemorySize *)instruction); + break; + case IrInstSrcIdWasmMemoryGrow: + ir_print_wasm_memory_grow(irp, (IrInstSrcWasmMemoryGrow *)instruction); + break; + case IrInstSrcIdSrc: + ir_print_builtin_src(irp, (IrInstSrcSrc *)instruction); + break; + } + fprintf(irp->f, "\n"); +} + +static void ir_print_inst_gen(IrPrintGen *irp, IrInstGen *instruction, bool trailing) { + ir_print_prefix_gen(irp, instruction, trailing); + switch (instruction->id) { + case IrInstGenIdInvalid: + zig_unreachable(); + case IrInstGenIdReturn: + ir_print_return_gen(irp, (IrInstGenReturn *)instruction); + break; + case IrInstGenIdConst: + ir_print_const(irp, (IrInstGenConst *)instruction); + break; + case IrInstGenIdBinOp: + ir_print_bin_op(irp, (IrInstGenBinOp *)instruction); + break; + case IrInstGenIdDeclVar: + ir_print_decl_var_gen(irp, (IrInstGenDeclVar *)instruction); + break; + case IrInstGenIdCast: + ir_print_cast(irp, (IrInstGenCast *)instruction); + break; + case IrInstGenIdCall: + ir_print_call_gen(irp, (IrInstGenCall *)instruction); + break; + case IrInstGenIdCondBr: + ir_print_cond_br(irp, (IrInstGenCondBr *)instruction); + break; + case IrInstGenIdBr: + ir_print_br(irp, (IrInstGenBr *)instruction); + break; + case IrInstGenIdPhi: + ir_print_phi(irp, (IrInstGenPhi *)instruction); + break; + case IrInstGenIdUnreachable: + ir_print_unreachable(irp, (IrInstGenUnreachable *)instruction); + break; + case IrInstGenIdElemPtr: + ir_print_elem_ptr(irp, (IrInstGenElemPtr *)instruction); + break; + case IrInstGenIdVarPtr: + ir_print_var_ptr(irp, (IrInstGenVarPtr *)instruction); + break; + case IrInstGenIdReturnPtr: + ir_print_return_ptr(irp, (IrInstGenReturnPtr *)instruction); + break; + case IrInstGenIdLoadPtr: + ir_print_load_ptr_gen(irp, (IrInstGenLoadPtr *)instruction); + break; + case IrInstGenIdStorePtr: + ir_print_store_ptr(irp, (IrInstGenStorePtr *)instruction); + break; + case IrInstGenIdStructFieldPtr: + ir_print_struct_field_ptr(irp, (IrInstGenStructFieldPtr *)instruction); + break; + case IrInstGenIdUnionFieldPtr: + ir_print_union_field_ptr(irp, (IrInstGenUnionFieldPtr *)instruction); + break; + case IrInstGenIdAsm: + ir_print_asm_gen(irp, (IrInstGenAsm *)instruction); + break; + case IrInstGenIdTestNonNull: + ir_print_test_non_null(irp, (IrInstGenTestNonNull *)instruction); + break; + case IrInstGenIdOptionalUnwrapPtr: + ir_print_optional_unwrap_ptr(irp, (IrInstGenOptionalUnwrapPtr *)instruction); + break; + case IrInstGenIdPopCount: + ir_print_pop_count(irp, (IrInstGenPopCount *)instruction); + break; + case IrInstGenIdClz: + ir_print_clz(irp, (IrInstGenClz *)instruction); + break; + case IrInstGenIdCtz: + ir_print_ctz(irp, (IrInstGenCtz *)instruction); + break; + case IrInstGenIdBswap: + ir_print_bswap(irp, (IrInstGenBswap *)instruction); + break; + case IrInstGenIdBitReverse: + ir_print_bit_reverse(irp, (IrInstGenBitReverse *)instruction); + break; + case IrInstGenIdSwitchBr: + ir_print_switch_br(irp, (IrInstGenSwitchBr *)instruction); + break; + case IrInstGenIdUnionTag: + ir_print_union_tag(irp, (IrInstGenUnionTag *)instruction); + break; + case IrInstGenIdRef: + ir_print_ref_gen(irp, (IrInstGenRef *)instruction); + break; + case IrInstGenIdErrName: + ir_print_err_name(irp, (IrInstGenErrName *)instruction); + break; + case IrInstGenIdCmpxchg: + ir_print_cmpxchg_gen(irp, (IrInstGenCmpxchg *)instruction); + break; + case IrInstGenIdFence: + ir_print_fence(irp, (IrInstGenFence *)instruction); + break; + case IrInstGenIdTruncate: + ir_print_truncate(irp, (IrInstGenTruncate *)instruction); + break; + case IrInstGenIdShuffleVector: + ir_print_shuffle_vector(irp, (IrInstGenShuffleVector *)instruction); + break; + case IrInstGenIdSplat: + ir_print_splat_gen(irp, (IrInstGenSplat *)instruction); + break; + case IrInstGenIdBoolNot: + ir_print_bool_not(irp, (IrInstGenBoolNot *)instruction); + break; + case IrInstGenIdMemset: + ir_print_memset(irp, (IrInstGenMemset *)instruction); + break; + case IrInstGenIdMemcpy: + ir_print_memcpy(irp, (IrInstGenMemcpy *)instruction); + break; + case IrInstGenIdSlice: + ir_print_slice_gen(irp, (IrInstGenSlice *)instruction); + break; + case IrInstGenIdBreakpoint: + ir_print_breakpoint(irp, (IrInstGenBreakpoint *)instruction); + break; + case IrInstGenIdReturnAddress: + ir_print_return_address(irp, (IrInstGenReturnAddress *)instruction); + break; + case IrInstGenIdFrameAddress: + ir_print_frame_address(irp, (IrInstGenFrameAddress *)instruction); + break; + case IrInstGenIdFrameHandle: + ir_print_handle(irp, (IrInstGenFrameHandle *)instruction); + break; + case IrInstGenIdFrameSize: + ir_print_frame_size_gen(irp, (IrInstGenFrameSize *)instruction); + break; + case IrInstGenIdOverflowOp: + ir_print_overflow_op(irp, (IrInstGenOverflowOp *)instruction); + break; + case IrInstGenIdTestErr: + ir_print_test_err_gen(irp, (IrInstGenTestErr *)instruction); + break; + case IrInstGenIdUnwrapErrCode: + ir_print_unwrap_err_code(irp, (IrInstGenUnwrapErrCode *)instruction); + break; + case IrInstGenIdUnwrapErrPayload: + ir_print_unwrap_err_payload(irp, (IrInstGenUnwrapErrPayload *)instruction); + break; + case IrInstGenIdOptionalWrap: + ir_print_optional_wrap(irp, (IrInstGenOptionalWrap *)instruction); + break; + case IrInstGenIdErrWrapCode: + ir_print_err_wrap_code(irp, (IrInstGenErrWrapCode *)instruction); + break; + case IrInstGenIdErrWrapPayload: + ir_print_err_wrap_payload(irp, (IrInstGenErrWrapPayload *)instruction); + break; + case IrInstGenIdPtrCast: + ir_print_ptr_cast_gen(irp, (IrInstGenPtrCast *)instruction); + break; + case IrInstGenIdBitCast: + ir_print_bit_cast_gen(irp, (IrInstGenBitCast *)instruction); + break; + case IrInstGenIdWidenOrShorten: + ir_print_widen_or_shorten(irp, (IrInstGenWidenOrShorten *)instruction); + break; + case IrInstGenIdPtrToInt: + ir_print_ptr_to_int(irp, (IrInstGenPtrToInt *)instruction); + break; + case IrInstGenIdIntToPtr: + ir_print_int_to_ptr(irp, (IrInstGenIntToPtr *)instruction); + break; + case IrInstGenIdIntToEnum: + ir_print_int_to_enum(irp, (IrInstGenIntToEnum *)instruction); + break; + case IrInstGenIdIntToErr: + ir_print_int_to_err(irp, (IrInstGenIntToErr *)instruction); + break; + case IrInstGenIdErrToInt: + ir_print_err_to_int(irp, (IrInstGenErrToInt *)instruction); + break; + case IrInstGenIdTagName: + ir_print_tag_name(irp, (IrInstGenTagName *)instruction); + break; + case IrInstGenIdPanic: + ir_print_panic(irp, (IrInstGenPanic *)instruction); + break; + case IrInstGenIdFieldParentPtr: + ir_print_field_parent_ptr(irp, (IrInstGenFieldParentPtr *)instruction); + break; + case IrInstGenIdAlignCast: + ir_print_align_cast(irp, (IrInstGenAlignCast *)instruction); + break; + case IrInstGenIdErrorReturnTrace: + ir_print_error_return_trace(irp, (IrInstGenErrorReturnTrace *)instruction); + break; + case IrInstGenIdAtomicRmw: + ir_print_atomic_rmw(irp, (IrInstGenAtomicRmw *)instruction); + break; + case IrInstGenIdSaveErrRetAddr: + ir_print_save_err_ret_addr(irp, (IrInstGenSaveErrRetAddr *)instruction); + break; + case IrInstGenIdFloatOp: + ir_print_float_op(irp, (IrInstGenFloatOp *)instruction); + break; + case IrInstGenIdMulAdd: + ir_print_mul_add(irp, (IrInstGenMulAdd *)instruction); + break; + case IrInstGenIdAtomicLoad: + ir_print_atomic_load(irp, (IrInstGenAtomicLoad *)instruction); + break; + case IrInstGenIdAtomicStore: + ir_print_atomic_store(irp, (IrInstGenAtomicStore *)instruction); + break; + case IrInstGenIdArrayToVector: + ir_print_array_to_vector(irp, (IrInstGenArrayToVector *)instruction); + break; + case IrInstGenIdVectorToArray: + ir_print_vector_to_array(irp, (IrInstGenVectorToArray *)instruction); + break; + case IrInstGenIdPtrOfArrayToSlice: + ir_print_ptr_of_array_to_slice(irp, (IrInstGenPtrOfArrayToSlice *)instruction); + break; + case IrInstGenIdAssertZero: + ir_print_assert_zero(irp, (IrInstGenAssertZero *)instruction); + break; + case IrInstGenIdAssertNonNull: + ir_print_assert_non_null(irp, (IrInstGenAssertNonNull *)instruction); + break; + case IrInstGenIdAlloca: + ir_print_alloca_gen(irp, (IrInstGenAlloca *)instruction); + break; + case IrInstGenIdSuspendBegin: + ir_print_suspend_begin(irp, (IrInstGenSuspendBegin *)instruction); + break; + case IrInstGenIdSuspendFinish: + ir_print_suspend_finish(irp, (IrInstGenSuspendFinish *)instruction); + break; + case IrInstGenIdResume: + ir_print_resume(irp, (IrInstGenResume *)instruction); + break; + case IrInstGenIdAwait: + ir_print_await_gen(irp, (IrInstGenAwait *)instruction); + break; + case IrInstGenIdSpillBegin: + ir_print_spill_begin(irp, (IrInstGenSpillBegin *)instruction); + break; + case IrInstGenIdSpillEnd: + ir_print_spill_end(irp, (IrInstGenSpillEnd *)instruction); + break; + case IrInstGenIdVectorExtractElem: + ir_print_vector_extract_elem(irp, (IrInstGenVectorExtractElem *)instruction); + break; + case IrInstGenIdVectorStoreElem: + ir_print_vector_store_elem(irp, (IrInstGenVectorStoreElem *)instruction); + break; + case IrInstGenIdBinaryNot: + ir_print_binary_not(irp, (IrInstGenBinaryNot *)instruction); + break; + case IrInstGenIdNegation: + ir_print_negation(irp, (IrInstGenNegation *)instruction); + break; + case IrInstGenIdNegationWrapping: + ir_print_negation_wrapping(irp, (IrInstGenNegationWrapping *)instruction); + break; + case IrInstGenIdWasmMemorySize: + ir_print_wasm_memory_size(irp, (IrInstGenWasmMemorySize *)instruction); + break; + case IrInstGenIdWasmMemoryGrow: + ir_print_wasm_memory_grow(irp, (IrInstGenWasmMemoryGrow *)instruction); + break; + } + fprintf(irp->f, "\n"); +} + +static void irp_print_basic_block_src(IrPrintSrc *irp, IrBasicBlockSrc *current_block) { + fprintf(irp->f, "%s_%" PRIu32 ":\n", current_block->name_hint, current_block->debug_id); + for (size_t instr_i = 0; instr_i < current_block->instruction_list.length; instr_i += 1) { + IrInstSrc *instruction = current_block->instruction_list.at(instr_i); + ir_print_inst_src(irp, instruction, false); + } +} + +static void irp_print_basic_block_gen(IrPrintGen *irp, IrBasicBlockGen *current_block) { + fprintf(irp->f, "%s_%" PRIu32 ":\n", current_block->name_hint, current_block->debug_id); + for (size_t instr_i = 0; instr_i < current_block->instruction_list.length; instr_i += 1) { + IrInstGen *instruction = current_block->instruction_list.at(instr_i); + irp->printed.put(instruction, 0); + irp->pending.clear(); + ir_print_inst_gen(irp, instruction, false); + for (size_t j = 0; j < irp->pending.length; ++j) + ir_print_inst_gen(irp, irp->pending.at(j), true); + } +} + +void ir_print_basic_block_src(CodeGen *codegen, FILE *f, IrBasicBlockSrc *bb, int indent_size) { + IrPrintSrc ir_print = {}; + ir_print.codegen = codegen; + ir_print.f = f; + ir_print.indent = indent_size; + ir_print.indent_size = indent_size; + + irp_print_basic_block_src(&ir_print, bb); +} + +void ir_print_basic_block_gen(CodeGen *codegen, FILE *f, IrBasicBlockGen *bb, int indent_size) { + IrPrintGen ir_print = {}; + ir_print.codegen = codegen; + ir_print.f = f; + ir_print.indent = indent_size; + ir_print.indent_size = indent_size; + ir_print.printed = {}; + ir_print.printed.init(64); + ir_print.pending = {}; + + irp_print_basic_block_gen(&ir_print, bb); + + ir_print.pending.deinit(); + ir_print.printed.deinit(); +} + +void ir_print_src(CodeGen *codegen, FILE *f, IrExecutableSrc *executable, int indent_size) { + IrPrintSrc ir_print = {}; + IrPrintSrc *irp = &ir_print; + irp->codegen = codegen; + irp->f = f; + irp->indent = indent_size; + irp->indent_size = indent_size; + + for (size_t bb_i = 0; bb_i < executable->basic_block_list.length; bb_i += 1) { + irp_print_basic_block_src(irp, executable->basic_block_list.at(bb_i)); + } +} + +void ir_print_gen(CodeGen *codegen, FILE *f, IrExecutableGen *executable, int indent_size) { + IrPrintGen ir_print = {}; + IrPrintGen *irp = &ir_print; + irp->codegen = codegen; + irp->f = f; + irp->indent = indent_size; + irp->indent_size = indent_size; + irp->printed = {}; + irp->printed.init(64); + irp->pending = {}; + + for (size_t bb_i = 0; bb_i < executable->basic_block_list.length; bb_i += 1) { + irp_print_basic_block_gen(irp, executable->basic_block_list.at(bb_i)); + } + + irp->pending.deinit(); + irp->printed.deinit(); +} + +void ir_print_inst_src(CodeGen *codegen, FILE *f, IrInstSrc *instruction, int indent_size) { + IrPrintSrc ir_print = {}; + IrPrintSrc *irp = &ir_print; + irp->codegen = codegen; + irp->f = f; + irp->indent = indent_size; + irp->indent_size = indent_size; + + ir_print_inst_src(irp, instruction, false); +} + +void ir_print_inst_gen(CodeGen *codegen, FILE *f, IrInstGen *instruction, int indent_size) { + IrPrintGen ir_print = {}; + IrPrintGen *irp = &ir_print; + irp->codegen = codegen; + irp->f = f; + irp->indent = indent_size; + irp->indent_size = indent_size; + irp->printed = {}; + irp->printed.init(4); + irp->pending = {}; + + ir_print_inst_gen(irp, instruction, false); +} diff --git a/src/stage1/ir_print.hpp b/src/stage1/ir_print.hpp new file mode 100644 index 0000000000000000000000000000000000000000..dde5aaea67e40804f173f1deb681c1a23f617aad --- /dev/null +++ b/src/stage1/ir_print.hpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2016 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef ZIG_IR_PRINT_HPP +#define ZIG_IR_PRINT_HPP + +#include "all_types.hpp" + +#include + +void ir_print_src(CodeGen *codegen, FILE *f, IrExecutableSrc *executable, int indent_size); +void ir_print_gen(CodeGen *codegen, FILE *f, IrExecutableGen *executable, int indent_size); +void ir_print_inst_src(CodeGen *codegen, FILE *f, IrInstSrc *inst, int indent_size); +void ir_print_inst_gen(CodeGen *codegen, FILE *f, IrInstGen *inst, int indent_size); +void ir_print_basic_block_src(CodeGen *codegen, FILE *f, IrBasicBlockSrc *bb, int indent_size); +void ir_print_basic_block_gen(CodeGen *codegen, FILE *f, IrBasicBlockGen *bb, int indent_size); + +const char* ir_inst_src_type_str(IrInstSrcId id); +const char* ir_inst_gen_type_str(IrInstGenId id); + +#endif diff --git a/src/stage1/list.hpp b/src/stage1/list.hpp new file mode 100644 index 0000000000000000000000000000000000000000..803a2514371d17c9a228169eb38a2c5269b0bf6b --- /dev/null +++ b/src/stage1/list.hpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2015 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef ZIG_LIST_HPP +#define ZIG_LIST_HPP + +#include "util.hpp" + +template +struct ZigList { + void deinit() { + heap::c_allocator.deallocate(items, capacity); + } + void append(const T& item) { + ensure_capacity(length + 1); + items[length++] = item; + } + void append_assuming_capacity(const T& item) { + items[length++] = item; + } + // remember that the pointer to this item is invalid after you + // modify the length of the list + const T & at(size_t index) const { + assert(index != SIZE_MAX); + assert(index < length); + return items[index]; + } + T & at(size_t index) { + assert(index != SIZE_MAX); + assert(index < length); + return items[index]; + } + T pop() { + assert(length >= 1); + return items[--length]; + } + + T *add_one() { + resize(length + 1); + return &last(); + } + + const T & last() const { + assert(length >= 1); + return items[length - 1]; + } + + T & last() { + assert(length >= 1); + return items[length - 1]; + } + + void resize(size_t new_length) { + assert(new_length != SIZE_MAX); + ensure_capacity(new_length); + length = new_length; + } + + void clear() { + length = 0; + } + + void ensure_capacity(size_t new_capacity) { + if (capacity >= new_capacity) + return; + + size_t better_capacity = capacity; + do { + better_capacity = better_capacity * 5 / 2 + 8; + } while (better_capacity < new_capacity); + + items = heap::c_allocator.reallocate_nonzero(items, capacity, better_capacity); + capacity = better_capacity; + } + + T swap_remove(size_t index) { + if (length - 1 == index) return pop(); + + assert(index != SIZE_MAX); + assert(index < length); + + T old_item = items[index]; + items[index] = pop(); + return old_item; + } + + T *items; + size_t length; + size_t capacity; +}; + +#endif diff --git a/src/stage1/mem.cpp b/src/stage1/mem.cpp new file mode 100644 index 0000000000000000000000000000000000000000..48dbd791dea783fa891b05f1292490d3d15ce68e --- /dev/null +++ b/src/stage1/mem.cpp @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2020 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#include "config.h" +#include "mem.hpp" +#include "heap.hpp" + +namespace mem { + +void init() { + heap::bootstrap_allocator_state.init("heap::bootstrap_allocator"); + heap::c_allocator_state.init("heap::c_allocator"); +} + +void deinit() { + heap::c_allocator_state.deinit(); + heap::bootstrap_allocator_state.deinit(); +} + +} // namespace mem diff --git a/src/stage1/mem.hpp b/src/stage1/mem.hpp new file mode 100644 index 0000000000000000000000000000000000000000..3008febbde615bc91cab25296b4432f4f8914ae9 --- /dev/null +++ b/src/stage1/mem.hpp @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2020 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef ZIG_MEM_HPP +#define ZIG_MEM_HPP + +#include +#include +#include + +#include "config.h" +#include "util_base.hpp" +#include "mem_type_info.hpp" + +// +// -- Memory Allocation General Notes -- +// +// `heap::c_allocator` is the preferred general allocator. +// +// `heap::bootstrap_allocator` is an implementation detail for use +// by allocators themselves when incidental heap may be required for +// profiling and statistics. It breaks the infinite recursion cycle. +// +// `mem::os` contains a raw wrapper for system malloc API used in +// preference to calling ::{malloc, free, calloc, realloc} directly. +// This isolates usage and helps with audits: +// +// mem::os::malloc +// mem::os::free +// mem::os::calloc +// mem::os::realloc +// +namespace mem { + +// initialize mem module before any use +void init(); + +// deinitialize mem module to free memory and print report +void deinit(); + +// isolate system/libc allocators +namespace os { + +ATTRIBUTE_RETURNS_NOALIAS +inline void *malloc(size_t size) { +#ifndef NDEBUG + // make behavior when size == 0 portable + if (size == 0) + return nullptr; +#endif + auto ptr = ::malloc(size); + if (ptr == nullptr) + zig_panic("allocation failed"); + return ptr; +} + +inline void free(void *ptr) { + ::free(ptr); +} + +ATTRIBUTE_RETURNS_NOALIAS +inline void *calloc(size_t count, size_t size) { +#ifndef NDEBUG + // make behavior when size == 0 portable + if (count == 0 || size == 0) + return nullptr; +#endif + auto ptr = ::calloc(count, size); + if (ptr == nullptr) + zig_panic("allocation failed"); + return ptr; +} + +inline void *realloc(void *old_ptr, size_t size) { +#ifndef NDEBUG + // make behavior when size == 0 portable + if (old_ptr == nullptr && size == 0) + return nullptr; +#endif + auto ptr = ::realloc(old_ptr, size); + if (ptr == nullptr) + zig_panic("allocation failed"); + return ptr; +} + +} // namespace os + +struct Allocator { + virtual void destruct(Allocator *allocator) = 0; + + template ATTRIBUTE_RETURNS_NOALIAS + T *allocate(size_t count) { + return reinterpret_cast(this->internal_allocate(TypeInfo::make(), count)); + } + + template ATTRIBUTE_RETURNS_NOALIAS + T *allocate_nonzero(size_t count) { + return reinterpret_cast(this->internal_allocate_nonzero(TypeInfo::make(), count)); + } + + template + T *reallocate(T *old_ptr, size_t old_count, size_t new_count) { + return reinterpret_cast(this->internal_reallocate(TypeInfo::make(), old_ptr, old_count, new_count)); + } + + template + T *reallocate_nonzero(T *old_ptr, size_t old_count, size_t new_count) { + return reinterpret_cast(this->internal_reallocate_nonzero(TypeInfo::make(), old_ptr, old_count, new_count)); + } + + template + void deallocate(T *ptr, size_t count) { + this->internal_deallocate(TypeInfo::make(), ptr, count); + } + + template + T *create() { + return reinterpret_cast(this->internal_allocate(TypeInfo::make(), 1)); + } + + template + void destroy(T *ptr) { + this->internal_deallocate(TypeInfo::make(), ptr, 1); + } + +protected: + ATTRIBUTE_RETURNS_NOALIAS virtual void *internal_allocate(const TypeInfo &info, size_t count) = 0; + ATTRIBUTE_RETURNS_NOALIAS virtual void *internal_allocate_nonzero(const TypeInfo &info, size_t count) = 0; + virtual void *internal_reallocate(const TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) = 0; + virtual void *internal_reallocate_nonzero(const TypeInfo &info, void *old_ptr, size_t old_count, size_t new_count) = 0; + virtual void internal_deallocate(const TypeInfo &info, void *ptr, size_t count) = 0; +}; + +} // namespace mem + +#endif diff --git a/src/stage1/mem_hash_map.hpp b/src/stage1/mem_hash_map.hpp new file mode 100644 index 0000000000000000000000000000000000000000..6abbbf665003035a1152de69477bfadffe851873 --- /dev/null +++ b/src/stage1/mem_hash_map.hpp @@ -0,0 +1,244 @@ +/* + * Copyright (c) 2015 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef ZIG_MEM_HASH_MAP_HPP +#define ZIG_MEM_HASH_MAP_HPP + +#include "mem.hpp" + +namespace mem { + +template +class HashMap { +public: + void init(Allocator& allocator, int capacity) { + init_capacity(allocator, capacity); + } + void deinit(Allocator& allocator) { + allocator.deallocate(_entries, _capacity); + } + + struct Entry { + K key; + V value; + bool used; + int distance_from_start_index; + }; + + void clear() { + for (int i = 0; i < _capacity; i += 1) { + _entries[i].used = false; + } + _size = 0; + _max_distance_from_start_index = 0; + _modification_count += 1; + } + + int size() const { + return _size; + } + + void put(Allocator& allocator, const K &key, const V &value) { + _modification_count += 1; + internal_put(key, value); + + // if we get too full (60%), double the capacity + if (_size * 5 >= _capacity * 3) { + Entry *old_entries = _entries; + int old_capacity = _capacity; + init_capacity(allocator, _capacity * 2); + // dump all of the old elements into the new table + for (int i = 0; i < old_capacity; i += 1) { + Entry *old_entry = &old_entries[i]; + if (old_entry->used) + internal_put(old_entry->key, old_entry->value); + } + allocator.deallocate(old_entries, old_capacity); + } + } + + Entry *put_unique(Allocator& allocator, const K &key, const V &value) { + // TODO make this more efficient + Entry *entry = internal_get(key); + if (entry) + return entry; + put(allocator, key, value); + return nullptr; + } + + const V &get(const K &key) const { + Entry *entry = internal_get(key); + if (!entry) + zig_panic("key not found"); + return entry->value; + } + + Entry *maybe_get(const K &key) const { + return internal_get(key); + } + + void maybe_remove(const K &key) { + if (maybe_get(key)) { + remove(key); + } + } + + void remove(const K &key) { + _modification_count += 1; + int start_index = key_to_index(key); + for (int roll_over = 0; roll_over <= _max_distance_from_start_index; roll_over += 1) { + int index = (start_index + roll_over) % _capacity; + Entry *entry = &_entries[index]; + + if (!entry->used) + zig_panic("key not found"); + + if (!EqualFn(entry->key, key)) + continue; + + for (; roll_over < _capacity; roll_over += 1) { + int next_index = (start_index + roll_over + 1) % _capacity; + Entry *next_entry = &_entries[next_index]; + if (!next_entry->used || next_entry->distance_from_start_index == 0) { + entry->used = false; + _size -= 1; + return; + } + *entry = *next_entry; + entry->distance_from_start_index -= 1; + entry = next_entry; + } + zig_panic("shifting everything in the table"); + } + zig_panic("key not found"); + } + + class Iterator { + public: + Entry *next() { + if (_inital_modification_count != _table->_modification_count) + zig_panic("concurrent modification"); + if (_count >= _table->size()) + return NULL; + for (; _index < _table->_capacity; _index += 1) { + Entry *entry = &_table->_entries[_index]; + if (entry->used) { + _index += 1; + _count += 1; + return entry; + } + } + zig_panic("no next item"); + } + + private: + const HashMap * _table; + // how many items have we returned + int _count = 0; + // iterator through the entry array + int _index = 0; + // used to detect concurrent modification + uint32_t _inital_modification_count; + Iterator(const HashMap * table) : + _table(table), _inital_modification_count(table->_modification_count) { + } + friend HashMap; + }; + + // you must not modify the underlying HashMap while this iterator is still in use + Iterator entry_iterator() const { + return Iterator(this); + } + +private: + Entry *_entries; + int _capacity; + int _size; + int _max_distance_from_start_index; + // this is used to detect bugs where a hashtable is edited while an iterator is running. + uint32_t _modification_count; + + void init_capacity(Allocator& allocator, int capacity) { + _capacity = capacity; + _entries = allocator.allocate(_capacity); + _size = 0; + _max_distance_from_start_index = 0; + for (int i = 0; i < _capacity; i += 1) { + _entries[i].used = false; + } + } + + void internal_put(K key, V value) { + int start_index = key_to_index(key); + for (int roll_over = 0, distance_from_start_index = 0; + roll_over < _capacity; roll_over += 1, distance_from_start_index += 1) + { + int index = (start_index + roll_over) % _capacity; + Entry *entry = &_entries[index]; + + if (entry->used && !EqualFn(entry->key, key)) { + if (entry->distance_from_start_index < distance_from_start_index) { + // robin hood to the rescue + Entry tmp = *entry; + if (distance_from_start_index > _max_distance_from_start_index) + _max_distance_from_start_index = distance_from_start_index; + *entry = { + key, + value, + true, + distance_from_start_index, + }; + key = tmp.key; + value = tmp.value; + distance_from_start_index = tmp.distance_from_start_index; + } + continue; + } + + if (!entry->used) { + // adding an entry. otherwise overwriting old value with + // same key + _size += 1; + } + + if (distance_from_start_index > _max_distance_from_start_index) + _max_distance_from_start_index = distance_from_start_index; + *entry = { + key, + value, + true, + distance_from_start_index, + }; + return; + } + zig_panic("put into a full HashMap"); + } + + + Entry *internal_get(const K &key) const { + int start_index = key_to_index(key); + for (int roll_over = 0; roll_over <= _max_distance_from_start_index; roll_over += 1) { + int index = (start_index + roll_over) % _capacity; + Entry *entry = &_entries[index]; + + if (!entry->used) + return NULL; + + if (EqualFn(entry->key, key)) + return entry; + } + return NULL; + } + + int key_to_index(const K &key) const { + return (int)(HashFunction(key) % ((uint32_t)_capacity)); + } +}; + +} // namespace mem + +#endif diff --git a/src/stage1/mem_list.hpp b/src/stage1/mem_list.hpp new file mode 100644 index 0000000000000000000000000000000000000000..df82358ea9542174cd652ec1ee97ee4d4d5b7e4c --- /dev/null +++ b/src/stage1/mem_list.hpp @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2015 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef ZIG_MEM_LIST_HPP +#define ZIG_MEM_LIST_HPP + +#include "mem.hpp" + +namespace mem { + +template +struct List { + void deinit(Allocator *allocator) { + allocator->deallocate(items, capacity); + items = nullptr; + length = 0; + capacity = 0; + } + + void append(Allocator *allocator, const T& item) { + ensure_capacity(allocator, length + 1); + items[length++] = item; + } + + // remember that the pointer to this item is invalid after you + // modify the length of the list + const T & at(size_t index) const { + assert(index != SIZE_MAX); + assert(index < length); + return items[index]; + } + + T & at(size_t index) { + assert(index != SIZE_MAX); + assert(index < length); + return items[index]; + } + + T pop() { + assert(length >= 1); + return items[--length]; + } + + T *add_one() { + resize(length + 1); + return &last(); + } + + const T & last() const { + assert(length >= 1); + return items[length - 1]; + } + + T & last() { + assert(length >= 1); + return items[length - 1]; + } + + void resize(Allocator *allocator, size_t new_length) { + assert(new_length != SIZE_MAX); + ensure_capacity(allocator, new_length); + length = new_length; + } + + void clear() { + length = 0; + } + + void ensure_capacity(Allocator *allocator, size_t new_capacity) { + if (capacity >= new_capacity) + return; + + size_t better_capacity = capacity; + do { + better_capacity = better_capacity * 5 / 2 + 8; + } while (better_capacity < new_capacity); + + items = allocator->reallocate_nonzero(items, capacity, better_capacity); + capacity = better_capacity; + } + + T swap_remove(size_t index) { + if (length - 1 == index) return pop(); + + assert(index != SIZE_MAX); + assert(index < length); + + T old_item = items[index]; + items[index] = pop(); + return old_item; + } + + T *items{nullptr}; + size_t length{0}; + size_t capacity{0}; +}; + +} // namespace mem + +#endif diff --git a/src/stage1/mem_type_info.hpp b/src/stage1/mem_type_info.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d8a793326885ac63df6ae8ac7e37cf9de4532431 --- /dev/null +++ b/src/stage1/mem_type_info.hpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2020 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef ZIG_MEM_TYPE_INFO_HPP +#define ZIG_MEM_TYPE_INFO_HPP + +#include "config.h" + +namespace mem { + +struct TypeInfo { + size_t size; + size_t alignment; + + template + static constexpr TypeInfo make() { + return {sizeof(T), alignof(T)}; + } +}; + +} // namespace mem + +#endif diff --git a/src/stage1/os.cpp b/src/stage1/os.cpp new file mode 100644 index 0000000000000000000000000000000000000000..33d98fd41679c4f5cdd30e5e4b1941bb84e053f0 --- /dev/null +++ b/src/stage1/os.cpp @@ -0,0 +1,2345 @@ +/* + * Copyright (c) 2015 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#include "os.hpp" +#include "buffer.hpp" +#include "heap.hpp" +#include "util.hpp" +#include "error.hpp" +#include "util_base.hpp" +#include +#include + +#if defined(_WIN32) + +#if !defined(NOMINMAX) +#define NOMINMAX +#endif + +#if !defined(VC_EXTRALEAN) +#define VC_EXTRALEAN +#endif + +#if !defined(WIN32_LEAN_AND_MEAN) +#define WIN32_LEAN_AND_MEAN +#endif + +#if !defined(_WIN32_WINNT) +#define _WIN32_WINNT 0x600 +#endif + +#if !defined(NTDDI_VERSION) +#define NTDDI_VERSION 0x06000000 +#endif + +#include +#include +#include +#include +#include + +#if defined(_MSC_VER) +typedef SSIZE_T ssize_t; +#endif +#else +#define ZIG_OS_POSIX + +#include +#include +#include +#include +#include +#include +#include +#include + +#endif + +#if defined(ZIG_OS_LINUX) || defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD) || defined(ZIG_OS_DRAGONFLY) +#include +#endif + +#if defined(ZIG_OS_LINUX) +#include +#endif + +#if defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD) || defined(ZIG_OS_DRAGONFLY) +#include +#endif + +#if defined(__MACH__) +#include +#include +#include +#endif + +#if defined(ZIG_OS_WINDOWS) +static void utf16le_ptr_to_utf8(Buf *out, WCHAR *utf16le); +static size_t utf8_to_utf16le(WCHAR *utf16_le, Slice utf8); +static uint64_t windows_perf_freq; +#elif defined(__MACH__) +static clock_serv_t macos_calendar_clock; +static clock_serv_t macos_monotonic_clock; +#endif + +#include +#include +#include + +#if !defined(environ) +extern char **environ; +#endif + +#if defined(ZIG_OS_POSIX) +static void populate_termination(Termination *term, int status) { + if (WIFEXITED(status)) { + term->how = TerminationIdClean; + term->code = WEXITSTATUS(status); + } else if (WIFSIGNALED(status)) { + term->how = TerminationIdSignaled; + term->code = WTERMSIG(status); + } else if (WIFSTOPPED(status)) { + term->how = TerminationIdStopped; + term->code = WSTOPSIG(status); + } else { + term->how = TerminationIdUnknown; + term->code = status; + } +} + +static void os_spawn_process_posix(ZigList &args, Termination *term) { + const char **argv = heap::c_allocator.allocate(args.length + 1); + for (size_t i = 0; i < args.length; i += 1) { + argv[i] = args.at(i); + } + argv[args.length] = nullptr; + + pid_t pid; + int rc = posix_spawnp(&pid, args.at(0), nullptr, nullptr, const_cast(argv), environ); + if (rc != 0) { + zig_panic("unable to spawn %s: %s", args.at(0), strerror(rc)); + } + + int status; + waitpid(pid, &status, 0); + populate_termination(term, status); +} +#endif + +#if defined(ZIG_OS_WINDOWS) + +static void os_windows_create_command_line(Buf *command_line, ZigList &args) { + buf_resize(command_line, 0); + const char *prefix = "\""; + for (size_t arg_i = 0; arg_i < args.length; arg_i += 1) { + const char *arg = args.at(arg_i); + buf_append_str(command_line, prefix); + prefix = " \""; + size_t arg_len = strlen(arg); + for (size_t c_i = 0; c_i < arg_len; c_i += 1) { + if (arg[c_i] == '\"') { + zig_panic("TODO"); + } + buf_append_char(command_line, arg[c_i]); + } + buf_append_char(command_line, '\"'); + } +} + +static void os_spawn_process_windows(ZigList &args, Termination *term) { + Buf command_line = BUF_INIT; + os_windows_create_command_line(&command_line, args); + + PROCESS_INFORMATION piProcInfo = {0}; + STARTUPINFOW siStartInfo = {0}; + siStartInfo.cb = sizeof(STARTUPINFOW); + + Slice exe_slice = str(args.at(0)); + auto exe_utf16_slice = Slice::alloc(exe_slice.len + 1); + exe_utf16_slice.ptr[utf8_to_utf16le(exe_utf16_slice.ptr, exe_slice)] = 0; + + auto command_line_utf16 = Slice::alloc(buf_len(&command_line) + 1); + command_line_utf16.ptr[utf8_to_utf16le(command_line_utf16.ptr, buf_to_slice(&command_line))] = 0; + + BOOL success = CreateProcessW(exe_utf16_slice.ptr, command_line_utf16.ptr, nullptr, nullptr, TRUE, CREATE_UNICODE_ENVIRONMENT, nullptr, nullptr, + &siStartInfo, &piProcInfo); + + if (!success) { + zig_panic("CreateProcess failed. exe: %s command_line: %s", args.at(0), buf_ptr(&command_line)); + } + + WaitForSingleObject(piProcInfo.hProcess, INFINITE); + + DWORD exit_code; + if (!GetExitCodeProcess(piProcInfo.hProcess, &exit_code)) { + zig_panic("GetExitCodeProcess failed"); + } + term->how = TerminationIdClean; + term->code = exit_code; +} +#endif + +void os_spawn_process(ZigList &args, Termination *term) { +#if defined(ZIG_OS_WINDOWS) + os_spawn_process_windows(args, term); +#elif defined(ZIG_OS_POSIX) + os_spawn_process_posix(args, term); +#else +#error "missing os_spawn_process implementation" +#endif +} + +void os_path_dirname(Buf *full_path, Buf *out_dirname) { + return os_path_split(full_path, out_dirname, nullptr); +} + +bool os_is_sep(uint8_t c) { +#if defined(ZIG_OS_WINDOWS) + return c == '\\' || c == '/'; +#else + return c == '/'; +#endif +} + +void os_path_split(Buf *full_path, Buf *out_dirname, Buf *out_basename) { + size_t len = buf_len(full_path); + if (len != 0) { + size_t last_index = len - 1; + char last_char = buf_ptr(full_path)[last_index]; + if (os_is_sep(last_char)) { + if (last_index == 0) { + if (out_dirname) buf_init_from_mem(out_dirname, &last_char, 1); + if (out_basename) buf_init_from_str(out_basename, ""); + return; + } + last_index -= 1; + } + for (size_t i = last_index;;) { + uint8_t c = buf_ptr(full_path)[i]; + if (os_is_sep(c)) { + if (out_dirname) { + buf_init_from_mem(out_dirname, buf_ptr(full_path), (i == 0) ? 1 : i); + } + if (out_basename) { + buf_init_from_mem(out_basename, buf_ptr(full_path) + i + 1, buf_len(full_path) - (i + 1)); + } + return; + } + if (i == 0) break; + i -= 1; + } + } + if (out_dirname) buf_init_from_mem(out_dirname, ".", 1); + if (out_basename) buf_init_from_buf(out_basename, full_path); +} + +void os_path_extname(Buf *full_path, Buf *out_basename, Buf *out_extname) { + if (buf_len(full_path) == 0) { + if (out_basename) buf_init_from_str(out_basename, ""); + if (out_extname) buf_init_from_str(out_extname, ""); + return; + } + size_t i = buf_len(full_path) - 1; + while (true) { + if (buf_ptr(full_path)[i] == '.') { + if (out_basename) { + buf_resize(out_basename, 0); + buf_append_mem(out_basename, buf_ptr(full_path), i); + } + + if (out_extname) { + buf_resize(out_extname, 0); + buf_append_mem(out_extname, buf_ptr(full_path) + i, buf_len(full_path) - i); + } + return; + } + + if (i == 0) { + if (out_basename) buf_init_from_buf(out_basename, full_path); + if (out_extname) buf_init_from_str(out_extname, ""); + return; + } + i -= 1; + } +} + +void os_path_join(Buf *dirname, Buf *basename, Buf *out_full_path) { + if (buf_len(dirname) == 0) { + buf_init_from_buf(out_full_path, basename); + return; + } + + buf_init_from_buf(out_full_path, dirname); + uint8_t c = *(buf_ptr(out_full_path) + buf_len(out_full_path) - 1); + if (!os_is_sep(c)) + buf_append_char(out_full_path, ZIG_OS_SEP_CHAR); + buf_append_buf(out_full_path, basename); +} + +Error os_path_real(Buf *rel_path, Buf *out_abs_path) { +#if defined(ZIG_OS_WINDOWS) + PathSpace rel_path_space = slice_to_prefixed_file_w(buf_to_slice(rel_path)); + PathSpace out_abs_path_space; + + if (_wfullpath(&out_abs_path_space.data.items[0], &rel_path_space.data.items[0], PATH_MAX_WIDE) == nullptr) { + zig_panic("_wfullpath failed"); + } + utf16le_ptr_to_utf8(out_abs_path, &out_abs_path_space.data.items[0]); + return ErrorNone; +#elif defined(ZIG_OS_POSIX) + buf_resize(out_abs_path, PATH_MAX + 1); + char *result = realpath(buf_ptr(rel_path), buf_ptr(out_abs_path)); + if (!result) { + int err = errno; + if (err == EACCES) { + return ErrorAccess; + } else if (err == ENOENT) { + return ErrorFileNotFound; + } else if (err == ENOMEM) { + return ErrorNoMem; + } else { + return ErrorFileSystem; + } + } + buf_resize(out_abs_path, strlen(buf_ptr(out_abs_path))); + return ErrorNone; +#else +#error "missing os_path_real implementation" +#endif +} + +#if defined(ZIG_OS_WINDOWS) +// Ported from std/os/path.zig +static bool isAbsoluteWindows(Slice path) { + if (path.ptr[0] == '/') + return true; + + if (path.ptr[0] == '\\') { + return true; + } + if (path.len < 3) { + return false; + } + if (path.ptr[1] == ':') { + if (path.ptr[2] == '/') + return true; + if (path.ptr[2] == '\\') + return true; + } + return false; +} +#endif + +bool os_path_is_absolute(Buf *path) { +#if defined(ZIG_OS_WINDOWS) + return isAbsoluteWindows(buf_to_slice(path)); +#elif defined(ZIG_OS_POSIX) + return buf_ptr(path)[0] == '/'; +#else +#error "missing os_path_is_absolute implementation" +#endif +} + +#if defined(ZIG_OS_WINDOWS) + +enum WindowsPathKind { + WindowsPathKindNone, + WindowsPathKindDrive, + WindowsPathKindNetworkShare, +}; + +struct WindowsPath { + Slice disk_designator; + WindowsPathKind kind; + bool is_abs; +}; + + +// Ported from std/os/path.zig +static WindowsPath windowsParsePath(Slice path) { + if (path.len >= 2 && path.ptr[1] == ':') { + return WindowsPath{ + path.slice(0, 2), + WindowsPathKindDrive, + isAbsoluteWindows(path), + }; + } + if (path.len >= 1 && (path.ptr[0] == '/' || path.ptr[0] == '\\') && + (path.len == 1 || (path.ptr[1] != '/' && path.ptr[1] != '\\'))) + { + return WindowsPath{ + path.slice(0, 0), + WindowsPathKindNone, + true, + }; + } + WindowsPath relative_path = { + str(""), + WindowsPathKindNone, + false, + }; + if (path.len < strlen("//a/b")) { + return relative_path; + } + + { + if (memStartsWith(path, str("//"))) { + if (path.ptr[2] == '/') { + return relative_path; + } + + SplitIterator it = memSplit(path, str("/")); + { + Optional> opt_component = SplitIterator_next(&it); + if (!opt_component.is_some) return relative_path; + } + { + Optional> opt_component = SplitIterator_next(&it); + if (!opt_component.is_some) return relative_path; + } + return WindowsPath{ + path.slice(0, it.index), + WindowsPathKindNetworkShare, + isAbsoluteWindows(path), + }; + } + } + { + if (memStartsWith(path, str("\\\\"))) { + if (path.ptr[2] == '\\') { + return relative_path; + } + + SplitIterator it = memSplit(path, str("\\")); + { + Optional> opt_component = SplitIterator_next(&it); + if (!opt_component.is_some) return relative_path; + } + { + Optional> opt_component = SplitIterator_next(&it); + if (!opt_component.is_some) return relative_path; + } + return WindowsPath{ + path.slice(0, it.index), + WindowsPathKindNetworkShare, + isAbsoluteWindows(path), + }; + } + } + return relative_path; +} + +// Ported from std/os/path.zig +static uint8_t asciiUpper(uint8_t byte) { + if (byte >= 'a' && byte <= 'z') { + return 'A' + (byte - 'a'); + } + return byte; +} + +// Ported from std/os/path.zig +static bool asciiEqlIgnoreCase(Slice s1, Slice s2) { + if (s1.len != s2.len) + return false; + for (size_t i = 0; i < s1.len; i += 1) { + if (asciiUpper(s1.ptr[i]) != asciiUpper(s2.ptr[i])) + return false; + } + return true; +} + +// Ported from std/os/path.zig +static bool compareDiskDesignators(WindowsPathKind kind, Slice p1, Slice p2) { + switch (kind) { + case WindowsPathKindNone: + assert(p1.len == 0); + assert(p2.len == 0); + return true; + case WindowsPathKindDrive: + return asciiUpper(p1.ptr[0]) == asciiUpper(p2.ptr[0]); + case WindowsPathKindNetworkShare: + uint8_t sep1 = p1.ptr[0]; + uint8_t sep2 = p2.ptr[0]; + + SplitIterator it1 = memSplit(p1, {&sep1, 1}); + SplitIterator it2 = memSplit(p2, {&sep2, 1}); + + // TODO ASCII is wrong, we actually need full unicode support to compare paths. + return asciiEqlIgnoreCase(SplitIterator_next(&it1).value, SplitIterator_next(&it2).value) && + asciiEqlIgnoreCase(SplitIterator_next(&it1).value, SplitIterator_next(&it2).value); + } + zig_unreachable(); +} + +// Ported from std/os/path.zig +static Buf os_path_resolve_windows(Buf **paths_ptr, size_t paths_len) { + if (paths_len == 0) { + Buf cwd = BUF_INIT; + int err; + if ((err = os_get_cwd(&cwd))) { + zig_panic("get cwd failed"); + } + return cwd; + } + + // determine which disk designator we will result with, if any + char result_drive_buf[3] = {'_', ':', '\0'}; // 0 needed for strlen later + Slice result_disk_designator = str(""); + WindowsPathKind have_drive_kind = WindowsPathKindNone; + bool have_abs_path = false; + size_t first_index = 0; + size_t max_size = 0; + for (size_t i = 0; i < paths_len; i += 1) { + Slice p = buf_to_slice(paths_ptr[i]); + WindowsPath parsed = windowsParsePath(p); + if (parsed.is_abs) { + have_abs_path = true; + first_index = i; + max_size = result_disk_designator.len; + } + switch (parsed.kind) { + case WindowsPathKindDrive: + result_drive_buf[0] = asciiUpper(parsed.disk_designator.ptr[0]); + result_disk_designator = str(result_drive_buf); + have_drive_kind = WindowsPathKindDrive; + break; + case WindowsPathKindNetworkShare: + result_disk_designator = parsed.disk_designator; + have_drive_kind = WindowsPathKindNetworkShare; + break; + case WindowsPathKindNone: + break; + } + max_size += p.len + 1; + } + + // if we will result with a disk designator, loop again to determine + // which is the last time the disk designator is absolutely specified, if any + // and count up the max bytes for paths related to this disk designator + if (have_drive_kind != WindowsPathKindNone) { + have_abs_path = false; + first_index = 0; + max_size = result_disk_designator.len; + bool correct_disk_designator = false; + + for (size_t i = 0; i < paths_len; i += 1) { + Slice p = buf_to_slice(paths_ptr[i]); + WindowsPath parsed = windowsParsePath(p); + if (parsed.kind != WindowsPathKindNone) { + if (parsed.kind == have_drive_kind) { + correct_disk_designator = compareDiskDesignators(have_drive_kind, result_disk_designator, parsed.disk_designator); + } else { + continue; + } + } + if (!correct_disk_designator) { + continue; + } + if (parsed.is_abs) { + first_index = i; + max_size = result_disk_designator.len; + have_abs_path = true; + } + max_size += p.len + 1; + } + } + + // Allocate result and fill in the disk designator, calling getCwd if we have to. + Slice result; + size_t result_index = 0; + + if (have_abs_path) { + switch (have_drive_kind) { + case WindowsPathKindDrive: { + result = Slice::alloc(max_size); + + memCopy(result, result_disk_designator); + result_index += result_disk_designator.len; + break; + } + case WindowsPathKindNetworkShare: { + result = Slice::alloc(max_size); + SplitIterator it = memSplit(buf_to_slice(paths_ptr[first_index]), str("/\\")); + Slice server_name = SplitIterator_next(&it).value; + Slice other_name = SplitIterator_next(&it).value; + + result.ptr[result_index] = '\\'; + result_index += 1; + result.ptr[result_index] = '\\'; + result_index += 1; + memCopy(result.sliceFrom(result_index), server_name); + result_index += server_name.len; + result.ptr[result_index] = '\\'; + result_index += 1; + memCopy(result.sliceFrom(result_index), other_name); + result_index += other_name.len; + + result_disk_designator = result.slice(0, result_index); + break; + } + case WindowsPathKindNone: { + Buf cwd = BUF_INIT; + int err; + if ((err = os_get_cwd(&cwd))) { + zig_panic("get cwd failed"); + } + WindowsPath parsed_cwd = windowsParsePath(buf_to_slice(&cwd)); + result = Slice::alloc(max_size + parsed_cwd.disk_designator.len + 1); + memCopy(result, parsed_cwd.disk_designator); + result_index += parsed_cwd.disk_designator.len; + result_disk_designator = result.slice(0, parsed_cwd.disk_designator.len); + if (parsed_cwd.kind == WindowsPathKindDrive) { + result.ptr[0] = asciiUpper(result.ptr[0]); + } + have_drive_kind = parsed_cwd.kind; + break; + } + } + } else { + // TODO call get cwd for the result_disk_designator instead of the global one + Buf cwd = BUF_INIT; + int err; + if ((err = os_get_cwd(&cwd))) { + zig_panic("get cwd failed"); + } + result = Slice::alloc(max_size + buf_len(&cwd) + 1); + + memCopy(result, buf_to_slice(&cwd)); + result_index += buf_len(&cwd); + WindowsPath parsed_cwd = windowsParsePath(result.slice(0, result_index)); + result_disk_designator = parsed_cwd.disk_designator; + if (parsed_cwd.kind == WindowsPathKindDrive) { + result.ptr[0] = asciiUpper(result.ptr[0]); + } + have_drive_kind = parsed_cwd.kind; + } + + // Now we know the disk designator to use, if any, and what kind it is. And our result + // is big enough to append all the paths to. + bool correct_disk_designator = true; + for (size_t i = 0; i < paths_len; i += 1) { + Slice p = buf_to_slice(paths_ptr[i]); + WindowsPath parsed = windowsParsePath(p); + + if (parsed.kind != WindowsPathKindNone) { + if (parsed.kind == have_drive_kind) { + correct_disk_designator = compareDiskDesignators(have_drive_kind, result_disk_designator, parsed.disk_designator); + } else { + continue; + } + } + if (!correct_disk_designator) { + continue; + } + SplitIterator it = memSplit(p.sliceFrom(parsed.disk_designator.len), str("/\\")); + while (true) { + Optional> opt_component = SplitIterator_next(&it); + if (!opt_component.is_some) break; + Slice component = opt_component.value; + if (memEql(component, str("."))) { + continue; + } else if (memEql(component, str(".."))) { + while (true) { + if (result_index == 0 || result_index == result_disk_designator.len) + break; + result_index -= 1; + if (result.ptr[result_index] == '\\' || result.ptr[result_index] == '/') + break; + } + } else { + result.ptr[result_index] = '\\'; + result_index += 1; + memCopy(result.sliceFrom(result_index), component); + result_index += component.len; + } + } + } + + if (result_index == result_disk_designator.len) { + result.ptr[result_index] = '\\'; + result_index += 1; + } + + Buf return_value = BUF_INIT; + buf_init_from_mem(&return_value, (char *)result.ptr, result_index); + return return_value; +} +#endif + +#if defined(ZIG_OS_POSIX) +// Ported from std/os/path.zig +static Buf os_path_resolve_posix(Buf **paths_ptr, size_t paths_len) { + if (paths_len == 0) { + Buf cwd = BUF_INIT; + int err; + if ((err = os_get_cwd(&cwd))) { + zig_panic("get cwd failed"); + } + return cwd; + } + + size_t first_index = 0; + bool have_abs = false; + size_t max_size = 0; + for (size_t i = 0; i < paths_len; i += 1) { + Buf *p = paths_ptr[i]; + if (os_path_is_absolute(p)) { + first_index = i; + have_abs = true; + max_size = 0; + } + max_size += buf_len(p) + 1; + } + + uint8_t *result_ptr; + size_t result_len; + size_t result_index = 0; + + if (have_abs) { + result_len = max_size; + result_ptr = heap::c_allocator.allocate_nonzero(result_len); + } else { + Buf cwd = BUF_INIT; + int err; + if ((err = os_get_cwd(&cwd))) { + zig_panic("get cwd failed"); + } + result_len = max_size + buf_len(&cwd) + 1; + result_ptr = heap::c_allocator.allocate_nonzero(result_len); + memcpy(result_ptr, buf_ptr(&cwd), buf_len(&cwd)); + result_index += buf_len(&cwd); + } + + for (size_t i = first_index; i < paths_len; i += 1) { + Buf *p = paths_ptr[i]; + SplitIterator it = memSplit(buf_to_slice(p), str("/")); + while (true) { + Optional> opt_component = SplitIterator_next(&it); + if (!opt_component.is_some) break; + Slice component = opt_component.value; + + if (memEql(component, str("."))) { + continue; + } else if (memEql(component, str(".."))) { + while (true) { + if (result_index == 0) + break; + result_index -= 1; + if (result_ptr[result_index] == '/') + break; + } + } else { + result_ptr[result_index] = '/'; + result_index += 1; + memcpy(result_ptr + result_index, component.ptr, component.len); + result_index += component.len; + } + } + } + + if (result_index == 0) { + result_ptr[0] = '/'; + result_index += 1; + } + + Buf return_value = BUF_INIT; + buf_init_from_mem(&return_value, (char *)result_ptr, result_index); + return return_value; +} +#endif + +// Ported from std/os/path.zig +Buf os_path_resolve(Buf **paths_ptr, size_t paths_len) { +#if defined(ZIG_OS_WINDOWS) + return os_path_resolve_windows(paths_ptr, paths_len); +#elif defined(ZIG_OS_POSIX) + return os_path_resolve_posix(paths_ptr, paths_len); +#else +#error "missing os_path_resolve implementation" +#endif +} + +Error os_fetch_file(FILE *f, Buf *out_buf) { + static const ssize_t buf_size = 0x2000; + buf_resize(out_buf, buf_size); + ssize_t actual_buf_len = 0; + + for (;;) { + size_t amt_read = fread(buf_ptr(out_buf) + actual_buf_len, 1, buf_size, f); + actual_buf_len += amt_read; + + if (amt_read != buf_size) { + if (feof(f)) { + buf_resize(out_buf, actual_buf_len); + return ErrorNone; + } else { + return ErrorFileSystem; + } + } + + buf_resize(out_buf, actual_buf_len + buf_size); + } + zig_unreachable(); +} + +Error os_file_exists(Buf *full_path, bool *result) { +#if defined(ZIG_OS_WINDOWS) + PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(full_path)); + *result = GetFileAttributesW(&path_space.data.items[0]) != INVALID_FILE_ATTRIBUTES; + return ErrorNone; +#else + *result = access(buf_ptr(full_path), F_OK) != -1; + return ErrorNone; +#endif +} + +#if defined(ZIG_OS_POSIX) +static Error os_exec_process_posix(ZigList &args, + Termination *term, Buf *out_stderr, Buf *out_stdout) +{ + int stdin_pipe[2]; + int stdout_pipe[2]; + int stderr_pipe[2]; + int err_pipe[2]; + + int err; + if ((err = pipe(stdin_pipe))) + zig_panic("pipe failed"); + if ((err = pipe(stdout_pipe))) + zig_panic("pipe failed"); + if ((err = pipe(stderr_pipe))) + zig_panic("pipe failed"); + if ((err = pipe(err_pipe))) + zig_panic("pipe failed"); + + pid_t pid = fork(); + if (pid == -1) + zig_panic("fork failed: %s", strerror(errno)); + if (pid == 0) { + // child + if (dup2(stdin_pipe[0], STDIN_FILENO) == -1) + zig_panic("dup2 failed"); + + if (dup2(stdout_pipe[1], STDOUT_FILENO) == -1) + zig_panic("dup2 failed"); + + if (dup2(stderr_pipe[1], STDERR_FILENO) == -1) + zig_panic("dup2 failed"); + + const char **argv = heap::c_allocator.allocate(args.length + 1); + argv[args.length] = nullptr; + for (size_t i = 0; i < args.length; i += 1) { + argv[i] = args.at(i); + } + execvp(argv[0], const_cast(argv)); + Error report_err = ErrorUnexpected; + if (errno == ENOENT) { + report_err = ErrorFileNotFound; + } + if (write(err_pipe[1], &report_err, sizeof(Error)) == -1) { + zig_panic("write failed"); + } + exit(1); + } else { + // parent + close(stdin_pipe[0]); + close(stdin_pipe[1]); + close(stdout_pipe[1]); + close(stderr_pipe[1]); + + int status; + waitpid(pid, &status, 0); + populate_termination(term, status); + + FILE *stdout_f = fdopen(stdout_pipe[0], "rb"); + FILE *stderr_f = fdopen(stderr_pipe[0], "rb"); + Error err1 = os_fetch_file(stdout_f, out_stdout); + Error err2 = os_fetch_file(stderr_f, out_stderr); + + fclose(stdout_f); + fclose(stderr_f); + + if (err1) return err1; + if (err2) return err2; + + Error child_err = ErrorNone; + if (write(err_pipe[1], &child_err, sizeof(Error)) == -1) { + zig_panic("write failed"); + } + close(err_pipe[1]); + if (read(err_pipe[0], &child_err, sizeof(Error)) == -1) { + zig_panic("write failed"); + } + close(err_pipe[0]); + return child_err; + } +} +#endif + +#if defined(ZIG_OS_WINDOWS) + +//static void win32_panic(const char *str) { +// DWORD err = GetLastError(); +// LPSTR messageBuffer = nullptr; +// FormatMessageA( +// FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, +// NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL); +// zig_panic(str, messageBuffer); +// LocalFree(messageBuffer); +//} + +static Error os_exec_process_windows(ZigList &args, + Termination *term, Buf *out_stderr, Buf *out_stdout) +{ + Buf command_line = BUF_INIT; + os_windows_create_command_line(&command_line, args); + + HANDLE g_hChildStd_IN_Rd = NULL; + HANDLE g_hChildStd_IN_Wr = NULL; + HANDLE g_hChildStd_OUT_Rd = NULL; + HANDLE g_hChildStd_OUT_Wr = NULL; + HANDLE g_hChildStd_ERR_Rd = NULL; + HANDLE g_hChildStd_ERR_Wr = NULL; + + SECURITY_ATTRIBUTES saAttr; + saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + saAttr.bInheritHandle = TRUE; + saAttr.lpSecurityDescriptor = NULL; + + if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0)) { + zig_panic("StdoutRd CreatePipe"); + } + + if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0)) { + zig_panic("Stdout SetHandleInformation"); + } + + if (!CreatePipe(&g_hChildStd_ERR_Rd, &g_hChildStd_ERR_Wr, &saAttr, 0)) { + zig_panic("stderr CreatePipe"); + } + + if (!SetHandleInformation(g_hChildStd_ERR_Rd, HANDLE_FLAG_INHERIT, 0)) { + zig_panic("stderr SetHandleInformation"); + } + + if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0)) { + zig_panic("Stdin CreatePipe"); + } + + if (!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0)) { + zig_panic("Stdin SetHandleInformation"); + } + + + PROCESS_INFORMATION piProcInfo = {0}; + STARTUPINFO siStartInfo = {0}; + siStartInfo.cb = sizeof(STARTUPINFO); + siStartInfo.hStdError = g_hChildStd_ERR_Wr; + siStartInfo.hStdOutput = g_hChildStd_OUT_Wr; + siStartInfo.hStdInput = g_hChildStd_IN_Rd; + siStartInfo.dwFlags |= STARTF_USESTDHANDLES; + + const char *exe = args.at(0); + BOOL success = CreateProcess(exe, buf_ptr(&command_line), nullptr, nullptr, TRUE, 0, nullptr, nullptr, + &siStartInfo, &piProcInfo); + + if (!success) { + if (GetLastError() == ERROR_FILE_NOT_FOUND) { + CloseHandle(piProcInfo.hProcess); + CloseHandle(piProcInfo.hThread); + return ErrorFileNotFound; + } + zig_panic("CreateProcess failed. exe: %s command_line: %s", exe, buf_ptr(&command_line)); + } + + if (!CloseHandle(g_hChildStd_IN_Wr)) { + zig_panic("stdinwr closehandle"); + } + + CloseHandle(g_hChildStd_IN_Rd); + CloseHandle(g_hChildStd_ERR_Wr); + CloseHandle(g_hChildStd_OUT_Wr); + + static const size_t BUF_SIZE = 4 * 1024; + { + DWORD dwRead; + char chBuf[BUF_SIZE]; + + buf_resize(out_stdout, 0); + for (;;) { + success = ReadFile( g_hChildStd_OUT_Rd, chBuf, BUF_SIZE, &dwRead, NULL); + if (!success || dwRead == 0) break; + + buf_append_mem(out_stdout, chBuf, dwRead); + } + CloseHandle(g_hChildStd_OUT_Rd); + } + { + DWORD dwRead; + char chBuf[BUF_SIZE]; + + buf_resize(out_stderr, 0); + for (;;) { + success = ReadFile( g_hChildStd_ERR_Rd, chBuf, BUF_SIZE, &dwRead, NULL); + if (!success || dwRead == 0) break; + + buf_append_mem(out_stderr, chBuf, dwRead); + } + CloseHandle(g_hChildStd_ERR_Rd); + } + + WaitForSingleObject(piProcInfo.hProcess, INFINITE); + + DWORD exit_code; + if (!GetExitCodeProcess(piProcInfo.hProcess, &exit_code)) { + zig_panic("GetExitCodeProcess failed"); + } + term->how = TerminationIdClean; + term->code = exit_code; + + CloseHandle(piProcInfo.hProcess); + CloseHandle(piProcInfo.hThread); + + return ErrorNone; +} +#endif + +Error os_execv(const char *exe, const char **argv) { +#if defined(ZIG_OS_WINDOWS) + return ErrorUnsupportedOperatingSystem; +#else + execv(exe, (char *const *)argv); + switch (errno) { + case ENOMEM: + return ErrorSystemResources; + case EIO: + return ErrorFileSystem; + default: + return ErrorUnexpected; + } +#endif +} + +Error os_exec_process(ZigList &args, + Termination *term, Buf *out_stderr, Buf *out_stdout) +{ +#if defined(ZIG_OS_WINDOWS) + return os_exec_process_windows(args, term, out_stderr, out_stdout); +#elif defined(ZIG_OS_POSIX) + return os_exec_process_posix(args, term, out_stderr, out_stdout); +#else +#error "missing os_exec_process implementation" +#endif +} + +Error os_write_file(Buf *full_path, Buf *contents) { +#if defined(ZIG_OS_WINDOWS) + PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(full_path)); + FILE *f = _wfopen(&path_space.data.items[0], L"wb"); +#else + FILE *f = fopen(buf_ptr(full_path), "wb"); +#endif + if (!f) { + zig_panic("os_write_file failed for %s", buf_ptr(full_path)); + } + size_t amt_written = fwrite(buf_ptr(contents), 1, buf_len(contents), f); + if (amt_written != (size_t)buf_len(contents)) + zig_panic("write failed: %s", strerror(errno)); + if (fclose(f)) + zig_panic("close failed"); + return ErrorNone; +} + +static Error copy_open_files(FILE *src_f, FILE *dest_f) { + static const size_t buf_size = 2048; + char buf[buf_size]; + for (;;) { + size_t amt_read = fread(buf, 1, buf_size, src_f); + if (amt_read != buf_size) { + if (ferror(src_f)) { + return ErrorFileSystem; + } + } + size_t amt_written = fwrite(buf, 1, amt_read, dest_f); + if (amt_written != amt_read) { + return ErrorFileSystem; + } + if (feof(src_f)) { + return ErrorNone; + } + } +} + +Error os_dump_file(Buf *src_path, FILE *dest_file) { + Error err; + +#if defined(ZIG_OS_WINDOWS) + PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(src_path)); + FILE *src_f = _wfopen(&path_space.data.items[0], L"rb"); +#else + FILE *src_f = fopen(buf_ptr(src_path), "rb"); +#endif + if (!src_f) { + int err = errno; + if (err == ENOENT) { + return ErrorFileNotFound; + } else if (err == EACCES || err == EPERM) { + return ErrorAccess; + } else { + return ErrorFileSystem; + } + } + copy_open_files(src_f, dest_file); + if ((err = copy_open_files(src_f, dest_file))) { + fclose(src_f); + return err; + } + + fclose(src_f); + return ErrorNone; +} + +#if defined(ZIG_OS_WINDOWS) +static void windows_filetime_to_os_timestamp(FILETIME *ft, OsTimeStamp *mtime) { + mtime->sec = (((ULONGLONG) ft->dwHighDateTime) << 32) + ft->dwLowDateTime; + mtime->nsec = 0; +} +static FILETIME windows_os_timestamp_to_filetime(OsTimeStamp mtime) { + FILETIME result; + result.dwHighDateTime = mtime.sec >> 32; + result.dwLowDateTime = mtime.sec; + return result; +} +#endif + +static Error set_file_times(OsFile file, OsTimeStamp ts) { +#if defined(ZIG_OS_WINDOWS) + FILETIME ft = windows_os_timestamp_to_filetime(ts); + if (SetFileTime(file, nullptr, &ft, &ft) == 0) { + return ErrorUnexpected; + } + return ErrorNone; +#else + struct timespec times[2] = { + { (time_t)ts.sec, (long)ts.nsec }, + { (time_t)ts.sec, (long)ts.nsec }, + }; + if (futimens(file, times) == -1) { + switch (errno) { + case EBADF: + zig_panic("futimens EBADF"); + default: + return ErrorUnexpected; + } + } + return ErrorNone; +#endif +} + +Error os_update_file(Buf *src_path, Buf *dst_path) { + Error err; + + OsFile src_file; + OsFileAttr src_attr; + if ((err = os_file_open_r(src_path, &src_file, &src_attr))) { + return err; + } + + OsFile dst_file; + OsFileAttr dst_attr; + if ((err = os_file_open_w(dst_path, &dst_file, &dst_attr, src_attr.mode))) { + os_file_close(&src_file); + return err; + } + + if (src_attr.size == dst_attr.size && + src_attr.mode == dst_attr.mode && + src_attr.mtime.sec == dst_attr.mtime.sec && + src_attr.mtime.nsec == dst_attr.mtime.nsec) + { + os_file_close(&src_file); + os_file_close(&dst_file); + return ErrorNone; + } +#if defined(ZIG_OS_WINDOWS) + if (SetEndOfFile(dst_file) == 0) { + return ErrorUnexpected; + } +#else + if (ftruncate(dst_file, 0) == -1) { + return ErrorUnexpected; + } +#endif +#if defined(ZIG_OS_WINDOWS) + FILE *src_libc_file = _fdopen(_open_osfhandle((intptr_t)src_file, _O_RDONLY), "rb"); + FILE *dst_libc_file = _fdopen(_open_osfhandle((intptr_t)dst_file, 0), "wb"); +#else + FILE *src_libc_file = fdopen(src_file, "rb"); + FILE *dst_libc_file = fdopen(dst_file, "wb"); +#endif + assert(src_libc_file); + assert(dst_libc_file); + + if ((err = copy_open_files(src_libc_file, dst_libc_file))) { + fclose(src_libc_file); + fclose(dst_libc_file); + return err; + } + if (fflush(dst_libc_file) == -1) { + return ErrorUnexpected; + } + err = set_file_times(dst_file, src_attr.mtime); + fclose(src_libc_file); + fclose(dst_libc_file); + return err; +} + +Error os_copy_file(Buf *src_path, Buf *dest_path) { +#if defined(ZIG_OS_WINDOWS) + PathSpace src_path_space = slice_to_prefixed_file_w(buf_to_slice(src_path)); + FILE *src_f = _wfopen(&src_path_space.data.items[0], L"rb"); +#else + FILE *src_f = fopen(buf_ptr(src_path), "rb"); +#endif + if (!src_f) { + int err = errno; + if (err == ENOENT) { + return ErrorFileNotFound; + } else if (err == EACCES || err == EPERM) { + return ErrorAccess; + } else { + return ErrorFileSystem; + } + } +#if defined(ZIG_OS_WINDOWS) + PathSpace dest_path_space = slice_to_prefixed_file_w(buf_to_slice(dest_path)); + FILE *dest_f = _wfopen(&dest_path_space.data.items[0], L"wb"); +#else + FILE *dest_f = fopen(buf_ptr(dest_path), "wb"); +#endif + if (!dest_f) { + int err = errno; + if (err == ENOENT) { + fclose(src_f); + return ErrorFileNotFound; + } else if (err == EACCES || err == EPERM) { + fclose(src_f); + return ErrorAccess; + } else { + fclose(src_f); + return ErrorFileSystem; + } + } + Error err = copy_open_files(src_f, dest_f); + fclose(src_f); + fclose(dest_f); + return err; +} + +Error os_fetch_file_path(Buf *full_path, Buf *out_contents) { +#if defined(ZIG_OS_WINDOWS) + PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(full_path)); + FILE *f = _wfopen(&path_space.data.items[0], L"rb"); +#else + FILE *f = fopen(buf_ptr(full_path), "rb"); +#endif + if (!f) { + switch (errno) { + case EACCES: + return ErrorAccess; + case EINTR: + return ErrorInterrupted; + case EINVAL: + return ErrorInvalidFilename; + case ENFILE: + case ENOMEM: + return ErrorSystemResources; + case ENOENT: + return ErrorFileNotFound; + default: + return ErrorFileSystem; + } + } + Error result = os_fetch_file(f, out_contents); + fclose(f); + return result; +} + +Error os_get_cwd(Buf *out_cwd) { +#if defined(ZIG_OS_WINDOWS) + PathSpace path_space; + if (GetCurrentDirectoryW(PATH_MAX_WIDE, &path_space.data.items[0]) == 0) { + zig_panic("GetCurrentDirectory failed"); + } + utf16le_ptr_to_utf8(out_cwd, &path_space.data.items[0]); + return ErrorNone; +#elif defined(ZIG_OS_POSIX) + char buf[PATH_MAX]; + char *res = getcwd(buf, PATH_MAX); + if (res == nullptr) { + zig_panic("unable to get cwd: %s", strerror(errno)); + } + buf_init_from_str(out_cwd, res); + return ErrorNone; +#else +#error "missing os_get_cwd implementation" +#endif +} + +#if defined(ZIG_OS_WINDOWS) +#define is_wprefix(s, prefix) \ + (wcsncmp((s), (prefix), sizeof(prefix) / sizeof(WCHAR) - 1) == 0) +static bool is_stderr_cyg_pty(void) { + HANDLE stderr_handle = GetStdHandle(STD_ERROR_HANDLE); + if (stderr_handle == INVALID_HANDLE_VALUE) + return false; + + const int size = sizeof(FILE_NAME_INFO) + sizeof(WCHAR) * MAX_PATH; + FILE_NAME_INFO *nameinfo; + WCHAR *p = NULL; + + // Cygwin/msys's pty is a pipe. + if (GetFileType(stderr_handle) != FILE_TYPE_PIPE) { + return 0; + } + nameinfo = reinterpret_cast(heap::c_allocator.allocate(size)); + if (nameinfo == NULL) { + return 0; + } + // Check the name of the pipe: + // '\{cygwin,msys}-XXXXXXXXXXXXXXXX-ptyN-{from,to}-master' + if (GetFileInformationByHandleEx(stderr_handle, FileNameInfo, nameinfo, size)) { + nameinfo->FileName[nameinfo->FileNameLength / sizeof(WCHAR)] = L'\0'; + p = nameinfo->FileName; + if (is_wprefix(p, L"\\cygwin-")) { /* Cygwin */ + p += 8; + } else if (is_wprefix(p, L"\\msys-")) { /* MSYS and MSYS2 */ + p += 6; + } else { + p = NULL; + } + if (p != NULL) { + while (*p && isxdigit(*p)) /* Skip 16-digit hexadecimal. */ + ++p; + if (is_wprefix(p, L"-pty")) { + p += 4; + } else { + p = NULL; + } + } + if (p != NULL) { + while (*p && isdigit(*p)) /* Skip pty number. */ + ++p; + if (is_wprefix(p, L"-from-master")) { + //p += 12; + } else if (is_wprefix(p, L"-to-master")) { + //p += 10; + } else { + p = NULL; + } + } + } + heap::c_allocator.deallocate(reinterpret_cast(nameinfo), size); + return (p != NULL); +} +#endif + +bool os_stderr_tty(void) { +#if defined(ZIG_OS_WINDOWS) + return _isatty(_fileno(stderr)) != 0 || is_stderr_cyg_pty(); +#elif defined(ZIG_OS_POSIX) + return isatty(STDERR_FILENO) != 0; +#else +#error "missing os_stderr_tty implementation" +#endif +} + +Error os_delete_file(Buf *path) { + if (remove(buf_ptr(path))) { + return ErrorFileSystem; + } else { + return ErrorNone; + } +} + +Error os_rename(Buf *src_path, Buf *dest_path) { + if (buf_eql_buf(src_path, dest_path)) { + return ErrorNone; + } +#if defined(ZIG_OS_WINDOWS) + PathSpace src_path_space = slice_to_prefixed_file_w(buf_to_slice(src_path)); + PathSpace dest_path_space = slice_to_prefixed_file_w(buf_to_slice(dest_path)); + if (!MoveFileExW(&src_path_space.data.items[0], &dest_path_space.data.items[0], MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH)) { + return ErrorFileSystem; + } +#else + if (rename(buf_ptr(src_path), buf_ptr(dest_path)) == -1) { + return ErrorFileSystem; + } +#endif + return ErrorNone; +} + +OsTimeStamp os_timestamp_calendar(void) { + OsTimeStamp result; +#if defined(ZIG_OS_WINDOWS) + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + windows_filetime_to_os_timestamp(&ft, &result); +#elif defined(__MACH__) + mach_timespec_t mts; + + kern_return_t err = clock_get_time(macos_calendar_clock, &mts); + assert(!err); + + result.sec = mts.tv_sec; + result.nsec = mts.tv_nsec; +#else + struct timespec tms; + clock_gettime(CLOCK_REALTIME, &tms); + + result.sec = tms.tv_sec; + result.nsec = tms.tv_nsec; +#endif + return result; +} + +OsTimeStamp os_timestamp_monotonic(void) { + OsTimeStamp result; +#if defined(ZIG_OS_WINDOWS) + uint64_t counts; + QueryPerformanceCounter((LARGE_INTEGER*)&counts); + result.sec = counts / windows_perf_freq; + result.nsec = (counts % windows_perf_freq) * 1000000000u / windows_perf_freq; +#elif defined(__MACH__) + mach_timespec_t mts; + + kern_return_t err = clock_get_time(macos_monotonic_clock, &mts); + assert(!err); + + result.sec = mts.tv_sec; + result.nsec = mts.tv_nsec; +#else + struct timespec tms; + clock_gettime(CLOCK_MONOTONIC, &tms); + + result.sec = tms.tv_sec; + result.nsec = tms.tv_nsec; +#endif + return result; +} + +Error os_make_path(Buf *path) { + Buf resolved_path = os_path_resolve(&path, 1); + + size_t end_index = buf_len(&resolved_path); + Error err; + while (true) { + if ((err = os_make_dir(buf_slice(&resolved_path, 0, end_index)))) { + if (err == ErrorPathAlreadyExists) { + if (end_index == buf_len(&resolved_path)) + return ErrorNone; + } else if (err == ErrorFileNotFound) { + // march end_index backward until next path component + while (true) { + end_index -= 1; + if (os_is_sep(buf_ptr(&resolved_path)[end_index])) + break; + } + continue; + } else { + return err; + } + } + if (end_index == buf_len(&resolved_path)) + return ErrorNone; + // march end_index forward until next path component + while (true) { + end_index += 1; + if (end_index == buf_len(&resolved_path) || os_is_sep(buf_ptr(&resolved_path)[end_index])) + break; + } + } + return ErrorNone; +} + +Error os_make_dir(Buf *path) { +#if defined(ZIG_OS_WINDOWS) + PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(path)); + if (memEql(buf_to_slice(path), str("C:\\dev\\tést"))) { + for (size_t i = 0; i < path_space.len; i++) { + fprintf(stderr, "%d ", path_space.data.items[i]); + } + fprintf(stderr, "\n"); + } + + if (!CreateDirectoryW(&path_space.data.items[0], NULL)) { + if (GetLastError() == ERROR_ALREADY_EXISTS) + return ErrorPathAlreadyExists; + if (GetLastError() == ERROR_PATH_NOT_FOUND) + return ErrorFileNotFound; + if (GetLastError() == ERROR_ACCESS_DENIED) + return ErrorAccess; + return ErrorUnexpected; + } + return ErrorNone; +#else + if (mkdir(buf_ptr(path), 0755) == -1) { + if (errno == EEXIST) + return ErrorPathAlreadyExists; + if (errno == ENOENT) + return ErrorFileNotFound; + if (errno == EACCES) + return ErrorAccess; + return ErrorUnexpected; + } + return ErrorNone; +#endif +} + +static void init_rand() { +#if defined(ZIG_OS_WINDOWS) + char bytes[sizeof(unsigned)]; + unsigned seed; + RtlGenRandom(bytes, sizeof(unsigned)); + memcpy(&seed, bytes, sizeof(unsigned)); + srand(seed); +#elif defined(ZIG_OS_LINUX) + unsigned char *ptr_random = (unsigned char*)getauxval(AT_RANDOM); + unsigned seed; + memcpy(&seed, ptr_random, sizeof(seed)); + srand(seed); +#elif defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD) + unsigned seed; + size_t len = sizeof(seed); + int mib[2] = { CTL_KERN, KERN_ARND }; + if (sysctl(mib, 2, &seed, &len, NULL, 0) != 0) { + zig_panic("unable to query random data from sysctl"); + } + srand(seed); +#else + int fd = open("/dev/urandom", O_RDONLY|O_CLOEXEC); + if (fd == -1) { + zig_panic("unable to open /dev/urandom"); + } + char bytes[sizeof(unsigned)]; + ssize_t amt_read; + while ((amt_read = read(fd, bytes, sizeof(unsigned))) == -1) { + if (errno == EINTR) continue; + zig_panic("unable to read /dev/urandom"); + } + if (amt_read != sizeof(unsigned)) { + zig_panic("unable to read enough bytes from /dev/urandom"); + } + close(fd); + unsigned seed; + memcpy(&seed, bytes, sizeof(unsigned)); + srand(seed); +#endif +} + +int os_init(void) { + init_rand(); +#if defined(ZIG_OS_WINDOWS) + _setmode(fileno(stdout), _O_BINARY); + _setmode(fileno(stderr), _O_BINARY); + if (!QueryPerformanceFrequency((LARGE_INTEGER*)&windows_perf_freq)) { + return ErrorSystemResources; + } +#elif defined(__MACH__) + host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &macos_monotonic_clock); + host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &macos_calendar_clock); +#endif +#if defined(ZIG_OS_POSIX) + // Raise the open file descriptor limit. + // Code lifted from node.js + struct rlimit lim; + if (getrlimit(RLIMIT_NOFILE, &lim) == 0 && lim.rlim_cur != lim.rlim_max) { + // Do a binary search for the limit. + rlim_t min = lim.rlim_cur; + rlim_t max = 1 << 20; + // But if there's a defined upper bound, don't search, just set it. + if (lim.rlim_max != RLIM_INFINITY) { + min = lim.rlim_max; + max = lim.rlim_max; + } + do { + lim.rlim_cur = min + (max - min) / 2; + if (setrlimit(RLIMIT_NOFILE, &lim)) { + max = lim.rlim_cur; + } else { + min = lim.rlim_cur; + } + } while (min + 1 < max); + } +#endif + return 0; +} + +Error os_self_exe_path(Buf *out_path) { +#if defined(ZIG_OS_WINDOWS) + PathSpace path_space; + DWORD copied_amt = GetModuleFileNameW(nullptr, &path_space.data.items[0], PATH_MAX_WIDE); + if (copied_amt <= 0) { + return ErrorFileNotFound; + } + utf16le_ptr_to_utf8(out_path, &path_space.data.items[0]); + return ErrorNone; + +#elif defined(ZIG_OS_DARWIN) + // How long is the executable's path? + uint32_t u32_len = 0; + int ret1 = _NSGetExecutablePath(nullptr, &u32_len); + assert(ret1 != 0); + + Buf *tmp = buf_alloc_fixed(u32_len); + + // Fill the executable path. + int ret2 = _NSGetExecutablePath(buf_ptr(tmp), &u32_len); + assert(ret2 == 0); + + // According to libuv project, PATH_MAX*2 works around a libc bug where + // the resolved path is sometimes bigger than PATH_MAX. + buf_resize(out_path, PATH_MAX*2); + char *real_path = realpath(buf_ptr(tmp), buf_ptr(out_path)); + if (!real_path) { + buf_init_from_buf(out_path, tmp); + return ErrorNone; + } + + // Resize out_path for the correct length. + buf_resize(out_path, strlen(buf_ptr(out_path))); + + return ErrorNone; +#elif defined(ZIG_OS_LINUX) + buf_resize(out_path, PATH_MAX); + ssize_t amt = readlink("/proc/self/exe", buf_ptr(out_path), buf_len(out_path)); + if (amt == -1) { + return ErrorUnexpected; + } + buf_resize(out_path, amt); + return ErrorNone; +#elif defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_DRAGONFLY) + buf_resize(out_path, PATH_MAX); + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; + size_t cb = PATH_MAX; + if (sysctl(mib, 4, buf_ptr(out_path), &cb, nullptr, 0) != 0) { + return ErrorUnexpected; + } + buf_resize(out_path, cb - 1); + return ErrorNone; +#elif defined(ZIG_OS_NETBSD) + buf_resize(out_path, PATH_MAX); + int mib[4] = { CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME }; + size_t cb = PATH_MAX; + if (sysctl(mib, 4, buf_ptr(out_path), &cb, nullptr, 0) != 0) { + return ErrorUnexpected; + } + buf_resize(out_path, cb - 1); + return ErrorNone; +#endif + return ErrorFileNotFound; +} + +#define VT_RED "\x1b[31;1m" +#define VT_GREEN "\x1b[32;1m" +#define VT_CYAN "\x1b[36;1m" +#define VT_WHITE "\x1b[37;1m" +#define VT_BOLD "\x1b[0;1m" +#define VT_RESET "\x1b[0m" + +static void set_color_posix(TermColor color) { + switch (color) { + case TermColorRed: + fprintf(stderr, VT_RED); + break; + case TermColorGreen: + fprintf(stderr, VT_GREEN); + break; + case TermColorCyan: + fprintf(stderr, VT_CYAN); + break; + case TermColorWhite: + fprintf(stderr, VT_WHITE); + break; + case TermColorBold: + fprintf(stderr, VT_BOLD); + break; + case TermColorReset: + fprintf(stderr, VT_RESET); + break; + } +} + + +#if defined(ZIG_OS_WINDOWS) +bool got_orig_console_attrs = false; +WORD original_console_attributes = FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE; +#endif + +void os_stderr_set_color(TermColor color) { +#if defined(ZIG_OS_WINDOWS) + if (is_stderr_cyg_pty()) { + set_color_posix(color); + return; + } + HANDLE stderr_handle = GetStdHandle(STD_ERROR_HANDLE); + if (stderr_handle == INVALID_HANDLE_VALUE) + zig_panic("unable to get stderr handle"); + fflush(stderr); + + if (!got_orig_console_attrs) { + got_orig_console_attrs = true; + CONSOLE_SCREEN_BUFFER_INFO info; + if (GetConsoleScreenBufferInfo(stderr_handle, &info)) { + original_console_attributes = info.wAttributes; + } + } + + switch (color) { + case TermColorRed: + SetConsoleTextAttribute(stderr_handle, FOREGROUND_RED|FOREGROUND_INTENSITY); + break; + case TermColorGreen: + SetConsoleTextAttribute(stderr_handle, FOREGROUND_GREEN|FOREGROUND_INTENSITY); + break; + case TermColorCyan: + SetConsoleTextAttribute(stderr_handle, FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_INTENSITY); + break; + case TermColorWhite: + case TermColorBold: + SetConsoleTextAttribute(stderr_handle, + FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_BLUE|FOREGROUND_INTENSITY); + break; + case TermColorReset: + SetConsoleTextAttribute(stderr_handle, original_console_attributes); + break; + } +#else + set_color_posix(color); +#endif +} + +#if defined(ZIG_OS_WINDOWS) +// Ported from std/unicode.zig +struct Utf16LeIterator { + uint8_t *bytes; + size_t i; +}; + +// Ported from std/unicode.zig +static Utf16LeIterator Utf16LeIterator_init(WCHAR *ptr) { + return {(uint8_t*)ptr, 0}; +} + +// Ported from std/unicode.zig +static Optional Utf16LeIterator_nextCodepoint(Utf16LeIterator *it) { + if (it->bytes[it->i] == 0 && it->bytes[it->i + 1] == 0) + return {}; + uint32_t c0 = ((uint32_t)it->bytes[it->i]) | (((uint32_t)it->bytes[it->i + 1]) << 8); + if ((c0 & ~((uint32_t)0x03ff)) == 0xd800) { + // surrogate pair + it->i += 2; + assert(it->bytes[it->i] != 0 || it->bytes[it->i + 1] != 0); + uint32_t c1 = ((uint32_t)it->bytes[it->i]) | (((uint32_t)it->bytes[it->i + 1]) << 8); + assert((c1 & ~((uint32_t)0x03ff)) == 0xdc00); + it->i += 2; + return Optional::some(0x10000 + (((c0 & 0x03ff) << 10) | (c1 & 0x03ff))); + } else { + assert((c0 & ~((uint32_t)0x03ff)) != 0xdc00); + it->i += 2; + return Optional::some(c0); + } +} + +// Ported from std/unicode.zig +static uint8_t utf8CodepointSequenceLength(uint32_t c) { + if (c < 0x80) return 1; + if (c < 0x800) return 2; + if (c < 0x10000) return 3; + if (c < 0x110000) return 4; + zig_unreachable(); +} + +// Ported from std.unicode.utf8ByteSequenceLength +static uint8_t utf8ByteSequenceLength(uint8_t first_byte) { + if (first_byte < 0b10000000) return 1; + if ((first_byte & 0b11100000) == 0b11000000) return 2; + if ((first_byte & 0b11110000) == 0b11100000) return 3; + if ((first_byte & 0b11111000) == 0b11110000) return 4; + zig_unreachable(); +} + +// Ported from std/unicode.zig +static size_t utf8Encode(uint32_t c, Slice out) { + size_t length = utf8CodepointSequenceLength(c); + assert(out.len >= length); + switch (length) { + // The pattern for each is the same + // - Increasing the initial shift by 6 each time + // - Each time after the first shorten the shifted + // value to a max of 0b111111 (63) + case 1: + out.ptr[0] = c; // Can just do 0 + codepoint for initial range + break; + case 2: + out.ptr[0] = 0b11000000 | (c >> 6); + out.ptr[1] = 0b10000000 | (c & 0b111111); + break; + case 3: + assert(!(0xd800 <= c && c <= 0xdfff)); + out.ptr[0] = 0b11100000 | (c >> 12); + out.ptr[1] = 0b10000000 | ((c >> 6) & 0b111111); + out.ptr[2] = 0b10000000 | (c & 0b111111); + break; + case 4: + out.ptr[0] = 0b11110000 | (c >> 18); + out.ptr[1] = 0b10000000 | ((c >> 12) & 0b111111); + out.ptr[2] = 0b10000000 | ((c >> 6) & 0b111111); + out.ptr[3] = 0b10000000 | (c & 0b111111); + break; + default: + zig_unreachable(); + } + return length; +} + +// Ported from std.unicode.utf8Decode2 +static uint32_t utf8Decode2(Slice bytes) { + assert(bytes.len == 2); + assert((bytes.at(0) & 0b11100000) == 0b11000000); + + uint32_t value = bytes.at(0) & 0b00011111; + assert((bytes.at(1) & 0b11000000) == 0b10000000); + value <<= 6; + value |= bytes.at(1) & 0b00111111; + + assert(value >= 0x80); + return value; +} + +// Ported from std.unicode.utf8Decode3 +static uint32_t utf8Decode3(Slice bytes) { + assert(bytes.len == 3); + assert((bytes.at(0) & 0b11110000) == 0b11100000); + + uint32_t value = bytes.at(0) & 0b00001111; + assert((bytes.at(1) & 0b11000000) == 0b10000000); + value <<= 6; + value |= bytes.at(1) & 0b00111111; + + assert((bytes.at(2) & 0b11000000) == 0b10000000); + value <<= 6; + value |= bytes.at(2) & 0b00111111; + + assert(value >= 0x80); + assert(value < 0xd800 || value > 0xdfff); + return value; +} + +// Ported from std.unicode.utf8Decode4 +static uint32_t utf8Decode4(Slice bytes) { + assert(bytes.len == 4); + assert((bytes.at(0) & 0b11111000) == 0b11110000); + + uint32_t value = bytes.at(0) & 0b00000111; + assert((bytes.at(1) & 0b11000000) == 0b10000000); + value <<= 6; + value |= bytes.at(1) & 0b00111111; + + assert((bytes.at(2) & 0b11000000) == 0b10000000); + value <<= 6; + value |= bytes.at(2) & 0b00111111; + + assert((bytes.at(3) & 0b11000000) == 0b10000000); + value <<= 6; + value |= bytes.at(3) & 0b00111111; + + assert(value >= 0x10000 && value <= 0x10FFFF); + return value; +} + +// Ported from std.unicode.utf8Decode +static uint32_t utf8Decode(Slice bytes) { + switch (bytes.len) { + case 1: + return bytes.at(0); + break; + case 2: + return utf8Decode2(bytes); + break; + case 3: + return utf8Decode3(bytes); + break; + case 4: + return utf8Decode4(bytes); + break; + default: + zig_unreachable(); + } +} +// Ported from std.unicode.utf16leToUtf8Alloc +static void utf16le_ptr_to_utf8(Buf *out, WCHAR *utf16le) { + // optimistically guess that it will all be ascii. + buf_resize(out, 0); + size_t out_index = 0; + Utf16LeIterator it = Utf16LeIterator_init(utf16le); + for (;;) { + Optional opt_codepoint = Utf16LeIterator_nextCodepoint(&it); + if (!opt_codepoint.is_some) break; + uint32_t codepoint = opt_codepoint.value; + + size_t utf8_len = utf8CodepointSequenceLength(codepoint); + buf_resize(out, buf_len(out) + utf8_len); + utf8Encode(codepoint, {(uint8_t*)buf_ptr(out)+out_index, buf_len(out)-out_index}); + out_index += utf8_len; + } +} + +// Ported from std.unicode.utf8ToUtf16Le +static size_t utf8_to_utf16le(WCHAR *utf16_le, Slice utf8) { + size_t dest_i = 0; + size_t src_i = 0; + while (src_i < utf8.len) { + uint8_t n = utf8ByteSequenceLength(utf8.at(src_i)); + size_t next_src_i = src_i + n; + uint32_t codepoint = utf8Decode(utf8.slice(src_i, next_src_i)); + if (codepoint < 0x10000) { + utf16_le[dest_i] = codepoint; + dest_i += 1; + } else { + WCHAR high = ((codepoint - 0x10000) >> 10) + 0xD800; + WCHAR low = (codepoint & 0x3FF) + 0xDC00; + utf16_le[dest_i] = high; + utf16_le[dest_i + 1] = low; + dest_i += 2; + } + src_i = next_src_i; + } + return dest_i; +} + +// Ported from std.os.windows.sliceToPrefixedFileW +PathSpace slice_to_prefixed_file_w(Slice path) { + PathSpace path_space; + for (size_t idx = 0; idx < path.len; idx++) { + assert(path.ptr[idx] != '*' && path.ptr[idx] != '?' && path.ptr[idx] != '"' && + path.ptr[idx] != '<' && path.ptr[idx] != '>' && path.ptr[idx] != '|'); + } + + size_t start_index; + if (memStartsWith(path, str("\\?")) || !isAbsoluteWindows(path)) { + start_index = 0; + } else { + static WCHAR prefix[4] = { u'\\', u'?', u'?', u'\\' }; + memCopy(path_space.data.slice(), Slice { prefix, 4 }); + start_index = 4; + } + + path_space.len = start_index + utf8_to_utf16le(path_space.data.slice().sliceFrom(start_index).ptr, path); + assert(path_space.len <= path_space.data.len); + + Slice path_slice = path_space.data.slice().slice(0, path_space.len); + for (size_t elem_idx = 0; elem_idx < path_slice.len; elem_idx += 1) { + if (path_slice.at(elem_idx) == '/') { + path_slice.at(elem_idx) = '\\'; + } + } + + path_space.data.items[path_space.len] = 0; + return path_space; +} +#endif + +// Ported from std.os.getAppDataDir +Error os_get_app_data_dir(Buf *out_path, const char *appname) { +#if defined(ZIG_OS_WINDOWS) + WCHAR *dir_path_ptr; + switch (SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, nullptr, &dir_path_ptr)) { + case S_OK: + // defer os.windows.CoTaskMemFree(@ptrCast(*c_void, dir_path_ptr)); + utf16le_ptr_to_utf8(out_path, dir_path_ptr); + CoTaskMemFree(dir_path_ptr); + buf_appendf(out_path, "\\%s", appname); + return ErrorNone; + case E_OUTOFMEMORY: + return ErrorNoMem; + default: + return ErrorUnexpected; + } + zig_unreachable(); +#elif defined(ZIG_OS_DARWIN) + const char *home_dir = getenv("HOME"); + if (home_dir == nullptr) { + // TODO use /etc/passwd + return ErrorFileNotFound; + } + buf_resize(out_path, 0); + buf_appendf(out_path, "%s/Library/Application Support/%s", home_dir, appname); + return ErrorNone; +#elif defined(ZIG_OS_POSIX) + const char *cache_dir = getenv("XDG_CACHE_HOME"); + if (cache_dir == nullptr) { + cache_dir = getenv("HOME"); + if (cache_dir == nullptr) { + // TODO use /etc/passwd + return ErrorFileNotFound; + } + if (cache_dir[0] == 0) { + return ErrorFileNotFound; + } + buf_init_from_str(out_path, cache_dir); + if (buf_ptr(out_path)[buf_len(out_path) - 1] != '/') { + buf_append_char(out_path, '/'); + } + buf_appendf(out_path, ".cache/%s", appname); + } else { + if (cache_dir[0] == 0) { + return ErrorFileNotFound; + } + buf_init_from_str(out_path, cache_dir); + if (buf_ptr(out_path)[buf_len(out_path) - 1] != '/') { + buf_append_char(out_path, '/'); + } + buf_appendf(out_path, "%s", appname); + } + return ErrorNone; +#endif +} + +#if defined(ZIG_OS_LINUX) || defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD) || defined(ZIG_OS_DRAGONFLY) +static int self_exe_shared_libs_callback(struct dl_phdr_info *info, size_t size, void *data) { + ZigList *libs = reinterpret_cast< ZigList *>(data); + if (info->dlpi_name[0] == '/') { + libs->append(buf_create_from_str(info->dlpi_name)); + } + return 0; +} +#endif + +Error os_self_exe_shared_libs(ZigList &paths) { +#if defined(ZIG_OS_LINUX) || defined(ZIG_OS_FREEBSD) || defined(ZIG_OS_NETBSD) || defined(ZIG_OS_DRAGONFLY) + paths.resize(0); + dl_iterate_phdr(self_exe_shared_libs_callback, &paths); + return ErrorNone; +#elif defined(ZIG_OS_DARWIN) + paths.resize(0); + uint32_t img_count = _dyld_image_count(); + for (uint32_t i = 0; i != img_count; i += 1) { + const char *name = _dyld_get_image_name(i); + paths.append(buf_create_from_str(name)); + } + return ErrorNone; +#elif defined(ZIG_OS_WINDOWS) + // zig is built statically on windows, so we can return an empty list + paths.resize(0); + return ErrorNone; +#else +#error unimplemented +#endif +} + +Error os_file_open_rw(Buf *full_path, OsFile *out_file, OsFileAttr *attr, bool need_write, uint32_t mode) { +#if defined(ZIG_OS_WINDOWS) + PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(full_path)); + HANDLE result = CreateFileW(&path_space.data.items[0], + need_write ? (GENERIC_READ|GENERIC_WRITE) : GENERIC_READ, + need_write ? 0 : FILE_SHARE_READ, + nullptr, + need_write ? OPEN_ALWAYS : OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, nullptr); + + if (result == INVALID_HANDLE_VALUE) { + DWORD err = GetLastError(); + switch (err) { + case ERROR_SHARING_VIOLATION: + return ErrorSharingViolation; + case ERROR_ALREADY_EXISTS: + return ErrorPathAlreadyExists; + case ERROR_FILE_EXISTS: + return ErrorPathAlreadyExists; + case ERROR_FILE_NOT_FOUND: + return ErrorFileNotFound; + case ERROR_PATH_NOT_FOUND: + return ErrorFileNotFound; + case ERROR_ACCESS_DENIED: + return ErrorAccess; + case ERROR_PIPE_BUSY: + return ErrorPipeBusy; + default: + return ErrorUnexpected; + } + } + *out_file = result; + + if (attr != nullptr) { + BY_HANDLE_FILE_INFORMATION file_info; + if (!GetFileInformationByHandle(result, &file_info)) { + CloseHandle(result); + return ErrorUnexpected; + } + windows_filetime_to_os_timestamp(&file_info.ftLastWriteTime, &attr->mtime); + attr->inode = (((uint64_t)file_info.nFileIndexHigh) << 32) | file_info.nFileIndexLow; + attr->mode = 0; + attr->size = (((uint64_t)file_info.nFileSizeHigh) << 32) | file_info.nFileSizeLow; + } + + return ErrorNone; +#else + for (;;) { + int fd = open(buf_ptr(full_path), + need_write ? (O_RDWR|O_CLOEXEC|O_CREAT) : (O_RDONLY|O_CLOEXEC), mode); + if (fd == -1) { + switch (errno) { + case EINTR: + continue; + case EINVAL: + zig_unreachable(); + case EFAULT: + zig_unreachable(); + case EACCES: + case EPERM: + return ErrorAccess; + case EISDIR: + return ErrorIsDir; + case ENOENT: + return ErrorFileNotFound; + default: + return ErrorFileSystem; + } + } + struct stat statbuf; + if (fstat(fd, &statbuf) == -1) { + close(fd); + return ErrorFileSystem; + } + if (S_ISDIR(statbuf.st_mode)) { + close(fd); + return ErrorIsDir; + } + *out_file = fd; + + if (attr != nullptr) { + attr->inode = statbuf.st_ino; +#if defined(ZIG_OS_DARWIN) + attr->mtime.sec = statbuf.st_mtimespec.tv_sec; + attr->mtime.nsec = statbuf.st_mtimespec.tv_nsec; +#else + attr->mtime.sec = statbuf.st_mtim.tv_sec; + attr->mtime.nsec = statbuf.st_mtim.tv_nsec; +#endif + attr->mode = statbuf.st_mode; + attr->size = statbuf.st_size; + } + return ErrorNone; + } +#endif +} + +Error os_file_open_r(Buf *full_path, OsFile *out_file, OsFileAttr *attr) { + return os_file_open_rw(full_path, out_file, attr, false, 0); +} + +Error os_file_open_w(Buf *full_path, OsFile *out_file, OsFileAttr *attr, uint32_t mode) { + return os_file_open_rw(full_path, out_file, attr, true, mode); +} + +Error os_file_open_lock_rw(Buf *full_path, OsFile *out_file) { +#if defined(ZIG_OS_WINDOWS) + PathSpace path_space = slice_to_prefixed_file_w(buf_to_slice(full_path)); + for (;;) { + HANDLE result = CreateFileW(&path_space.data.items[0], GENERIC_READ | GENERIC_WRITE, + 0, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); + + if (result == INVALID_HANDLE_VALUE) { + DWORD err = GetLastError(); + switch (err) { + case ERROR_SHARING_VIOLATION: + // TODO wait for the lock instead of sleeping + Sleep(10); + continue; + case ERROR_ALREADY_EXISTS: + return ErrorPathAlreadyExists; + case ERROR_FILE_EXISTS: + return ErrorPathAlreadyExists; + case ERROR_FILE_NOT_FOUND: + return ErrorFileNotFound; + case ERROR_PATH_NOT_FOUND: + return ErrorFileNotFound; + case ERROR_ACCESS_DENIED: + return ErrorAccess; + case ERROR_PIPE_BUSY: + return ErrorPipeBusy; + default: + return ErrorUnexpected; + } + } + *out_file = result; + return ErrorNone; + } +#else + int fd; + for (;;) { + fd = open(buf_ptr(full_path), O_RDWR|O_CLOEXEC|O_CREAT, 0666); + if (fd == -1) { + switch (errno) { + case EINTR: + continue; + case EINVAL: + zig_unreachable(); + case EFAULT: + zig_unreachable(); + case EACCES: + case EPERM: + return ErrorAccess; + case EISDIR: + return ErrorIsDir; + case ENOENT: + return ErrorFileNotFound; + case ENOTDIR: + return ErrorNotDir; + default: + return ErrorFileSystem; + } + } + break; + } + for (;;) { + struct flock lock; + lock.l_type = F_WRLCK; + lock.l_whence = SEEK_SET; + lock.l_start = 0; + lock.l_len = 0; + if (fcntl(fd, F_SETLKW, &lock) == -1) { + switch (errno) { + case EINTR: + continue; + case EBADF: + zig_unreachable(); + case EFAULT: + zig_unreachable(); + case EINVAL: + zig_unreachable(); + default: + close(fd); + return ErrorFileSystem; + } + } + break; + } + *out_file = fd; + return ErrorNone; +#endif +} + +Error os_file_read(OsFile file, void *ptr, size_t *len) { +#if defined(ZIG_OS_WINDOWS) + DWORD amt_read; + if (ReadFile(file, ptr, *len, &amt_read, nullptr) == 0) + return ErrorUnexpected; + *len = amt_read; + return ErrorNone; +#else + for (;;) { + ssize_t rc = read(file, ptr, *len); + if (rc == -1) { + switch (errno) { + case EINTR: + continue; + case EBADF: + zig_unreachable(); + case EFAULT: + zig_unreachable(); + case EISDIR: + return ErrorIsDir; + default: + return ErrorFileSystem; + } + } + *len = rc; + return ErrorNone; + } +#endif +} + +Error os_file_read_all(OsFile file, Buf *contents) { + Error err; + size_t index = 0; + for (;;) { + size_t amt = buf_len(contents) - index; + + if (amt < 4096) { + buf_resize(contents, buf_len(contents) + (4096 - amt)); + amt = buf_len(contents) - index; + } + + if ((err = os_file_read(file, buf_ptr(contents) + index, &amt))) + return err; + + if (amt == 0) { + buf_resize(contents, index); + return ErrorNone; + } + + index += amt; + } +} + +Error os_file_overwrite(OsFile file, Buf *contents) { +#if defined(ZIG_OS_WINDOWS) + if (SetFilePointer(file, 0, nullptr, FILE_BEGIN) == INVALID_SET_FILE_POINTER) + return ErrorFileSystem; + if (!SetEndOfFile(file)) + return ErrorFileSystem; + DWORD bytes_written; + if (!WriteFile(file, buf_ptr(contents), buf_len(contents), &bytes_written, nullptr)) + return ErrorFileSystem; + return ErrorNone; +#else + if (lseek(file, 0, SEEK_SET) == -1) + return ErrorUnexpectedSeekFailure; + if (ftruncate(file, 0) == -1) + return ErrorUnexpectedFileTruncationFailure; + for (;;) { + if (write(file, buf_ptr(contents), buf_len(contents)) == -1) { + switch (errno) { + case EINTR: + continue; + case EINVAL: + zig_unreachable(); + case EBADF: + zig_unreachable(); + case EFAULT: + zig_unreachable(); + case EDQUOT: + return ErrorDiskQuota; + case ENOSPC: + return ErrorDiskSpace; + case EFBIG: + return ErrorFileTooBig; + case EIO: + return ErrorFileSystem; + case EPERM: + return ErrorAccess; + default: + return ErrorUnexpectedWriteFailure; + } + } + return ErrorNone; + } +#endif +} + +void os_file_close(OsFile *file) { +#if defined(ZIG_OS_WINDOWS) + CloseHandle(*file); + *file = NULL; +#else + close(*file); + *file = -1; +#endif +} diff --git a/src/stage1/os.hpp b/src/stage1/os.hpp new file mode 100644 index 0000000000000000000000000000000000000000..9792a42c453754adc60326e97903f66fe1bd34b1 --- /dev/null +++ b/src/stage1/os.hpp @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2015 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef ZIG_OS_HPP +#define ZIG_OS_HPP + +#include "list.hpp" +#include "buffer.hpp" +#include "error.hpp" +#include "zig_llvm.h" +#include "windows_sdk.h" + +#include +#include + +#if defined(__APPLE__) +#define ZIG_OS_DARWIN +#elif defined(_WIN32) +#define ZIG_OS_WINDOWS +#elif defined(__linux__) +#define ZIG_OS_LINUX +#elif defined(__FreeBSD__) +#define ZIG_OS_FREEBSD +#elif defined(__NetBSD__) +#define ZIG_OS_NETBSD +#elif defined(__DragonFly__) +#define ZIG_OS_DRAGONFLY +#else +#define ZIG_OS_UNKNOWN +#endif + +#if defined(__x86_64__) +#define ZIG_ARCH_X86_64 +#elif defined(__aarch64__) +#define ZIG_ARCH_ARM64 +#elif defined(__ARM_EABI__) +#define ZIG_ARCH_ARM +#else +#define ZIG_ARCH_UNKNOWN +#endif + +#if defined(ZIG_OS_WINDOWS) +#define ZIG_PRI_usize "I64u" +#define ZIG_PRI_i64 "I64d" +#define ZIG_PRI_u64 "I64u" +#define ZIG_PRI_llu "I64u" +#define ZIG_PRI_x64 "I64x" +#define OS_SEP "\\" +#define ZIG_OS_SEP_CHAR '\\' +#else +#define ZIG_PRI_usize "zu" +#define ZIG_PRI_i64 PRId64 +#define ZIG_PRI_u64 PRIu64 +#define ZIG_PRI_llu "llu" +#define ZIG_PRI_x64 PRIx64 +#define OS_SEP "/" +#define ZIG_OS_SEP_CHAR '/' +#endif + +enum TermColor { + TermColorRed, + TermColorGreen, + TermColorCyan, + TermColorWhite, + TermColorBold, + TermColorReset, +}; + +enum TerminationId { + TerminationIdClean, + TerminationIdSignaled, + TerminationIdStopped, + TerminationIdUnknown, +}; + +struct Termination { + TerminationId how; + int code; +}; + +#if defined(ZIG_OS_WINDOWS) +#define OsFile void * +#else +#define OsFile int +#endif + +struct OsTimeStamp { + int64_t sec; + int64_t nsec; +}; + +struct OsFileAttr { + OsTimeStamp mtime; + uint64_t size; + uint64_t inode; + uint32_t mode; +}; + +int os_init(void); + +void os_spawn_process(ZigList &args, Termination *term); +Error os_exec_process(ZigList &args, + Termination *term, Buf *out_stderr, Buf *out_stdout); +Error os_execv(const char *exe, const char **argv); + +void os_path_dirname(Buf *full_path, Buf *out_dirname); +void os_path_split(Buf *full_path, Buf *out_dirname, Buf *out_basename); +void os_path_extname(Buf *full_path, Buf *out_basename, Buf *out_extname); +void os_path_join(Buf *dirname, Buf *basename, Buf *out_full_path); +Error os_path_real(Buf *rel_path, Buf *out_abs_path); +Buf os_path_resolve(Buf **paths_ptr, size_t paths_len); +bool os_path_is_absolute(Buf *path); + +Error ATTRIBUTE_MUST_USE os_make_path(Buf *path); +Error ATTRIBUTE_MUST_USE os_make_dir(Buf *path); + +Error ATTRIBUTE_MUST_USE os_file_open_r(Buf *full_path, OsFile *out_file, OsFileAttr *attr); +Error ATTRIBUTE_MUST_USE os_file_open_w(Buf *full_path, OsFile *out_file, OsFileAttr *attr, uint32_t mode); +Error ATTRIBUTE_MUST_USE os_file_open_lock_rw(Buf *full_path, OsFile *out_file); +Error ATTRIBUTE_MUST_USE os_file_read(OsFile file, void *ptr, size_t *len); +Error ATTRIBUTE_MUST_USE os_file_read_all(OsFile file, Buf *contents); +Error ATTRIBUTE_MUST_USE os_file_overwrite(OsFile file, Buf *contents); +void os_file_close(OsFile *file); + +Error ATTRIBUTE_MUST_USE os_write_file(Buf *full_path, Buf *contents); +Error ATTRIBUTE_MUST_USE os_copy_file(Buf *src_path, Buf *dest_path); +Error ATTRIBUTE_MUST_USE os_update_file(Buf *src_path, Buf *dest_path); +Error ATTRIBUTE_MUST_USE os_dump_file(Buf *src_path, FILE *dest_file); + +Error ATTRIBUTE_MUST_USE os_fetch_file(FILE *file, Buf *out_contents); +Error ATTRIBUTE_MUST_USE os_fetch_file_path(Buf *full_path, Buf *out_contents); + +Error ATTRIBUTE_MUST_USE os_get_cwd(Buf *out_cwd); + +bool os_stderr_tty(void); +void os_stderr_set_color(TermColor color); + +Error os_delete_file(Buf *path); + +Error ATTRIBUTE_MUST_USE os_file_exists(Buf *full_path, bool *result); + +Error os_rename(Buf *src_path, Buf *dest_path); +OsTimeStamp os_timestamp_monotonic(void); +OsTimeStamp os_timestamp_calendar(void); + +bool os_is_sep(uint8_t c); + +Error ATTRIBUTE_MUST_USE os_self_exe_path(Buf *out_path); + +Error ATTRIBUTE_MUST_USE os_get_app_data_dir(Buf *out_path, const char *appname); + +Error ATTRIBUTE_MUST_USE os_self_exe_shared_libs(ZigList &paths); + +const size_t PATH_MAX_WIDE = 32767; + +struct PathSpace { + Array data; + size_t len; +}; + +PathSpace slice_to_prefixed_file_w(Slice path); +#endif diff --git a/src/stage1/parse_f128.c b/src/stage1/parse_f128.c new file mode 100644 index 0000000000000000000000000000000000000000..9b5c287a3c015388a6e749fcc0a07379b3d2e58f --- /dev/null +++ b/src/stage1/parse_f128.c @@ -0,0 +1,1084 @@ +// Code ported from musl libc 8f12c4e110acb3bbbdc8abfb3a552c3ced718039 +// and then modified to use softfloat and to assume f128 for everything + +#include "parse_f128.h" +#include "softfloat.h" +#include +#include +#include +#include +#include +#include + +#define shcnt(f) ((f)->shcnt + ((f)->rpos - (f)->buf)) +#define shlim(f, lim) __shlim((f), (lim)) +#define shgetc(f) (((f)->rpos != (f)->shend) ? *(f)->rpos++ : __shgetc(f)) +#define shunget(f) ((f)->shlim>=0 ? (void)(f)->rpos-- : (void)0) + +#define sh_fromstring(f, s) \ + ((f)->buf = (f)->rpos = (void *)(s), (f)->rend = (void*)-1) + +#define LD_B1B_DIG 4 +#define LD_B1B_MAX 10384593, 717069655, 257060992, 658440191 +#define KMAX 2048 + +#define MASK (KMAX-1) + +#define CONCAT2(x,y) x ## y +#define CONCAT(x,y) CONCAT2(x,y) + +#define F_PERM 1 +#define F_NORD 4 +#define F_NOWR 8 +#define F_EOF 16 +#define F_ERR 32 +#define F_SVB 64 +#define F_APP 128 + +#define EOF (-1) + +#define LDBL_MANT_DIG 113 +#define LDBL_MIN_EXP (-16381) +#define LDBL_MAX_EXP 16384 + +#define LDBL_DIG 33 +#define LDBL_MIN_10_EXP (-4931) +#define LDBL_MAX_10_EXP 4932 + +#define DECIMAL_DIG 36 + + +#if __BYTE_ORDER == __LITTLE_ENDIAN +union ldshape { + float128_t f; + struct { + uint64_t lo; + uint32_t mid; + uint16_t top; + uint16_t se; + } i; + struct { + uint64_t lo; + uint64_t hi; + } i2; +}; +#elif __BYTE_ORDER == __BIG_ENDIAN +union ldshape { + float128_t f; + struct { + uint16_t se; + uint16_t top; + uint32_t mid; + uint64_t lo; + } i; + struct { + uint64_t hi; + uint64_t lo; + } i2; +}; +#error Unsupported endian +#endif + +struct MuslFILE { + unsigned flags; + unsigned char *rpos, *rend; + int (*close)(struct MuslFILE *); + unsigned char *wend, *wpos; + unsigned char *mustbezero_1; + unsigned char *wbase; + size_t (*read)(struct MuslFILE *, unsigned char *, size_t); + size_t (*write)(struct MuslFILE *, const unsigned char *, size_t); + off_t (*seek)(struct MuslFILE *, off_t, int); + unsigned char *buf; + size_t buf_size; + struct MuslFILE *prev, *next; + int fd; + int pipe_pid; + long lockcount; + int mode; + volatile int lock; + int lbf; + void *cookie; + off_t off; + char *getln_buf; + void *mustbezero_2; + unsigned char *shend; + off_t shlim, shcnt; + struct MuslFILE *prev_locked, *next_locked; + struct __locale_struct *locale; +}; + +static void __shlim(struct MuslFILE *f, off_t lim) +{ + f->shlim = lim; + f->shcnt = f->buf - f->rpos; + /* If lim is nonzero, rend must be a valid pointer. */ + if (lim && f->rend - f->rpos > lim) + f->shend = f->rpos + lim; + else + f->shend = f->rend; +} + +static int __toread(struct MuslFILE *f) +{ + f->mode |= f->mode-1; + if (f->wpos != f->wbase) f->write(f, 0, 0); + f->wpos = f->wbase = f->wend = 0; + if (f->flags & F_NORD) { + f->flags |= F_ERR; + return EOF; + } + f->rpos = f->rend = f->buf + f->buf_size; + return (f->flags & F_EOF) ? EOF : 0; +} + +static int __uflow(struct MuslFILE *f) +{ + unsigned char c; + if (!__toread(f) && f->read(f, &c, 1)==1) return c; + return EOF; +} + +static int __shgetc(struct MuslFILE *f) +{ + int c; + off_t cnt = shcnt(f); + if ((f->shlim && cnt >= f->shlim) || (c=__uflow(f)) < 0) { + f->shcnt = f->buf - f->rpos + cnt; + f->shend = f->rpos; + f->shlim = -1; + return EOF; + } + cnt++; + if (f->shlim && f->rend - f->rpos > f->shlim - cnt) + f->shend = f->rpos + (f->shlim - cnt); + else + f->shend = f->rend; + f->shcnt = f->buf - f->rpos + cnt; + if (f->rpos[-1] != c) f->rpos[-1] = c; + return c; +} + +static long long scanexp(struct MuslFILE *f, int pok) +{ + int c; + int x; + long long y; + int neg = 0; + + c = shgetc(f); + if (c=='+' || c=='-') { + neg = (c=='-'); + c = shgetc(f); + if (c-'0'>=10U && pok) shunget(f); + } + if (c-'0'>=10U && c!='_') { + shunget(f); + return LLONG_MIN; + } + for (x=0; ; c = shgetc(f)) { + if (c=='_') { + continue; + } else if (c-'0'<10U && x>16) | 1ULL<<48; + yhi = (uy.i2.hi & -1ULL>>16) | 1ULL<<48; + xlo = ux.i2.lo; + ylo = uy.i2.lo; + for (; ex > ey; ex--) { + hi = xhi - yhi; + lo = xlo - ylo; + if (xlo < ylo) + hi -= 1; + if (hi >> 63 == 0) { + if ((hi|lo) == 0) { + //return 0*x; + float128_t result; + f128M_mul(&zero, &x, &result); + return result; + } + xhi = 2*hi + (lo>>63); + xlo = 2*lo; + } else { + xhi = 2*xhi + (xlo>>63); + xlo = 2*xlo; + } + } + hi = xhi - yhi; + lo = xlo - ylo; + if (xlo < ylo) + hi -= 1; + if (hi >> 63 == 0) { + if ((hi|lo) == 0) { + //return 0*x; + float128_t result; + f128M_mul(&zero, &x, &result); + return result; + } + xhi = hi; + xlo = lo; + } + for (; xhi >> 48 == 0; xhi = 2*xhi + (xlo>>63), xlo = 2*xlo, ex--); + ux.i2.hi = xhi; + ux.i2.lo = xlo; + + /* scale result */ + if (ex <= 0) { + ux.i.se = (ex+120)|sx; + //ux.f *= 0x1p-120f; + mul_eq_f128_float(&ux.f, 0x1p-120f); + } else + ux.i.se = ex|sx; + return ux.f; +} + +static float128_t int_mul_f128_cast_u32(int sign, uint32_t x0) { + float128_t x0_f128; + ui32_to_f128M(x0, &x0_f128); + float128_t sign_f128; + i32_to_f128M(sign, &sign_f128); + float128_t result; + f128M_mul(&sign_f128, &x0_f128, &result); + return result; +} + +static float128_t triple_divide(int sign, uint32_t x0, int p10s) { + float128_t part1 = int_mul_f128_cast_u32(sign, x0); + float128_t p10s_f128; + i32_to_f128M(p10s, &p10s_f128); + float128_t result; + f128M_div(&part1, &p10s_f128, &result); + return result; +} + +static float128_t triple_multiply(int sign, uint32_t x0, int p10s) { + float128_t part1 = int_mul_f128_cast_u32(sign, x0); + float128_t p10s_f128; + i32_to_f128M(p10s, &p10s_f128); + float128_t result; + f128M_mul(&part1, &p10s_f128, &result); + return result; +} + +static void mul_eq_f128_int(float128_t *y, int sign) { + float128_t sign_f128; + i32_to_f128M(sign, &sign_f128); + float128_t new_value; + f128M_mul(y, &sign_f128, &new_value); + *y = new_value; +} + +static float128_t make_f128(uint64_t hi, uint64_t lo) { + union ldshape ux; + ux.i2.hi = hi; + ux.i2.lo = lo; + return ux.f; +} + +static void mul_eq_f128_f128(float128_t *a, float128_t b) { + float128_t new_value; + f128M_mul(a, &b, &new_value); + *a = new_value; +} + +static void add_eq_f128_dbl(float128_t *a, double b) { + float64_t b_f64; + memcpy(&b_f64, &b, sizeof(double)); + + float128_t b_f128; + f64_to_f128M(b_f64, &b_f128); + + float128_t new_value; + f128M_add(a, &b_f128, &new_value); + *a = new_value; +} + +static float128_t scalbnf128(float128_t x, int n) +{ + union ldshape u; + + if (n > 16383) { + //x *= 0x1p16383q; + mul_eq_f128_f128(&x, make_f128(0x7ffe000000000000, 0x0000000000000000)); + n -= 16383; + if (n > 16383) { + //x *= 0x1p16383q; + mul_eq_f128_f128(&x, make_f128(0x7ffe000000000000, 0x0000000000000000)); + n -= 16383; + if (n > 16383) + n = 16383; + } + } else if (n < -16382) { + //x *= 0x1p-16382q * 0x1p113q; + { + float128_t mul_result; + float128_t a = make_f128(0x0001000000000000, 0x0000000000000000); + float128_t b = make_f128(0x4070000000000000, 0x0000000000000000); + f128M_mul(&a, &b, &mul_result); + mul_eq_f128_f128(&x, mul_result); + } + n += 16382 - 113; + if (n < -16382) { + //x *= 0x1p-16382q * 0x1p113q; + { + float128_t mul_result; + float128_t a = make_f128(0x0001000000000000, 0x0000000000000000); + float128_t b = make_f128(0x4070000000000000, 0x0000000000000000); + f128M_mul(&a, &b, &mul_result); + mul_eq_f128_f128(&x, mul_result); + } + n += 16382 - 113; + if (n < -16382) + n = -16382; + } + } + //u.f = 1.0; + ui32_to_f128M(1, &u.f); + u.i.se = 0x3fff + n; + mul_eq_f128_f128(&x, u.f); + return x; +} + +static float128_t fabsf128(float128_t x) +{ + union ldshape u = {x}; + + u.i.se &= 0x7fff; + return u.f; +} + +static float128_t decfloat(struct MuslFILE *f, int c, int bits, int emin, int sign, int pok) +{ + uint32_t x[KMAX]; + static const uint32_t th[] = { LD_B1B_MAX }; + int i, j, k, a, z; + long long lrp=0, dc=0; + long long e10=0; + int lnz = 0; + int gotdig = 0, gotrad = 0; + int rp; + int e2; + int emax = -emin-bits+3; + int denormal = 0; + float128_t y; + float128_t zero; + ui32_to_f128M(0, &zero); + float128_t frac=zero; + float128_t bias=zero; + static const int p10s[] = { 10, 100, 1000, 10000, + 100000, 1000000, 10000000, 100000000 }; + + j=0; + k=0; + + /* Don't let leading zeros/underscores consume buffer space */ + for (; ; c = shgetc(f)) { + if (c=='_') { + continue; + } else if (c=='0') { + gotdig=1; + } else { + break; + } + } + + if (c=='.') { + gotrad = 1; + for (c = shgetc(f); ; c = shgetc(f)) { + if (c == '_') { + continue; + } else if (c=='0') { + gotdig=1; + lrp--; + } else { + break; + } + } + } + + x[0] = 0; + for (; c-'0'<10U || c=='.' || c=='_'; c = shgetc(f)) { + if (c == '_') { + continue; + } else if (c == '.') { + if (gotrad) break; + gotrad = 1; + lrp = dc; + } else if (k < KMAX-3) { + dc++; + if (c!='0') lnz = dc; + if (j) x[k] = x[k]*10 + c-'0'; + else x[k] = c-'0'; + if (++j==9) { + k++; + j=0; + } + gotdig=1; + } else { + dc++; + if (c!='0') { + lnz = (KMAX-4)*9; + x[KMAX-4] |= 1; + } + } + } + if (!gotrad) lrp=dc; + + if (gotdig && (c|32)=='e') { + e10 = scanexp(f, pok); + if (e10 == LLONG_MIN) { + if (pok) { + shunget(f); + } else { + shlim(f, 0); + return zero; + } + e10 = 0; + } + lrp += e10; + } else if (c>=0) { + shunget(f); + } + if (!gotdig) { + errno = EINVAL; + shlim(f, 0); + return zero; + } + + /* Handle zero specially to avoid nasty special cases later */ + if (!x[0]) { + //return sign * 0.0; + return dbl_to_f128(sign * 0.0); + } + + /* Optimize small integers (w/no exponent) and over/under-flow */ + if (lrp==dc && dc<10 && (bits>30 || x[0]>>bits==0)) { + //return sign * (float128_t)x[0]; + float128_t sign_f128; + i32_to_f128M(sign, &sign_f128); + float128_t x0_f128; + ui32_to_f128M(x[0], &x0_f128); + float128_t result; + f128M_mul(&sign_f128, &x0_f128, &result); + return result; + } + if (lrp > -emin/2) { + errno = ERANGE; + //return sign * LDBL_MAX * LDBL_MAX; + return zero; + } + if (lrp < emin-2*LDBL_MANT_DIG) { + errno = ERANGE; + //return sign * LDBL_MIN * LDBL_MIN; + return zero; + } + + /* Align incomplete final B1B digit */ + if (j) { + for (; j<9; j++) x[k]*=10; + k++; + j=0; + } + + a = 0; + z = k; + e2 = 0; + rp = lrp; + + /* Optimize small to mid-size integers (even in exp. notation) */ + if (lnz<9 && lnz<=rp && rp < 18) { + if (rp == 9) { + //return sign * (float128_t)(x[0]); + return int_mul_f128_cast_u32(sign, x[0]); + } + if (rp < 9) { + //return sign * (float128_t)(x[0]) / p10s[8-rp]; + return triple_divide(sign, x[0], p10s[8-rp]); + } + int bitlim = bits-3*(int)(rp-9); + if (bitlim>30 || x[0]>>bitlim==0) + //return sign * (float128_t)(x[0]) * p10s[rp-10]; + return triple_multiply(sign, x[0], p10s[rp-10]); + } + + /* Drop trailing zeros */ + for (; !x[z-1]; z--); + + /* Align radix point to B1B digit boundary */ + if (rp % 9) { + int rpm9 = rp>=0 ? rp%9 : rp%9+9; + int p10 = p10s[8-rpm9]; + uint32_t carry = 0; + for (k=a; k!=z; k++) { + uint32_t tmp = x[k] % p10; + x[k] = x[k]/p10 + carry; + carry = 1000000000/p10 * tmp; + if (k==a && !x[k]) { + a = (a+1 & MASK); + rp -= 9; + } + } + if (carry) x[z++] = carry; + rp += 9-rpm9; + } + + /* Upscale until desired number of bits are left of radix point */ + while (rp < 9*LD_B1B_DIG || (rp == 9*LD_B1B_DIG && x[a] 1000000000) { + carry = tmp / 1000000000; + x[k] = tmp % 1000000000; + } else { + carry = 0; + x[k] = tmp; + } + if (k==(z-1 & MASK) && k!=a && !x[k]) z = k; + if (k==a) break; + } + if (carry) { + rp += 9; + a = (a-1 & MASK); + if (a == z) { + z = (z-1 & MASK); + x[z-1 & MASK] |= x[z]; + } + x[a] = carry; + } + } + + /* Downscale until exactly number of bits are left of radix point */ + for (;;) { + uint32_t carry = 0; + int sh = 1; + for (i=0; i th[i]) break; + } + if (i==LD_B1B_DIG && rp==9*LD_B1B_DIG) break; + /* FIXME: find a way to compute optimal sh */ + if (rp > 9+9*LD_B1B_DIG) sh = 9; + e2 += sh; + for (k=a; k!=z; k=(k+1 & MASK)) { + uint32_t tmp = x[k] & (1<>sh) + carry; + carry = (1000000000>>sh) * tmp; + if (k==a && !x[k]) { + a = (a+1 & MASK); + i--; + rp -= 9; + } + } + if (carry) { + if ((z+1 & MASK) != a) { + x[z] = carry; + z = (z+1 & MASK); + } else x[z-1 & MASK] |= 1; + } + } + + /* Assemble desired bits into floating point variable */ + for (y=zero,i=0; i LDBL_MANT_DIG+e2-emin) { + bits = LDBL_MANT_DIG+e2-emin; + if (bits<0) bits=0; + denormal = 1; + } + + /* Calculate bias term to force rounding, move out lower bits */ + if (bits < LDBL_MANT_DIG) { + bias = copysignf128(dbl_to_f128(scalbn(1, 2*LDBL_MANT_DIG-bits-1)), y); + frac = fmodf128(y, dbl_to_f128(scalbn(1, LDBL_MANT_DIG-bits))); + //y -= frac; + { + float128_t new_value; + f128M_sub(&y, &frac, &new_value); + y = new_value; + } + //y += bias; + { + float128_t new_value; + f128M_add(&y, &frac, &new_value); + y = new_value; + } + } + + /* Process tail of decimal input so it can affect rounding */ + if ((a+i & MASK) != z) { + uint32_t t = x[a+i & MASK]; + if (t < 500000000 && (t || (a+i+1 & MASK) != z)) { + //frac += 0.25*sign; + add_eq_f128_dbl(&frac, 0.25*sign); + } else if (t > 500000000) { + //frac += 0.75*sign; + add_eq_f128_dbl(&frac, 0.75*sign); + } else if (t == 500000000) { + if ((a+i+1 & MASK) == z) { + //frac += 0.5*sign; + add_eq_f128_dbl(&frac, 0.5*sign); + } else { + //frac += 0.75*sign; + add_eq_f128_dbl(&frac, 0.75*sign); + } + } + //if (LDBL_MANT_DIG-bits >= 2 && !fmodf128(frac, 1)) + if (LDBL_MANT_DIG-bits >= 2) { + float128_t one; + ui32_to_f128M(1, &one); + float128_t mod_result = fmodf128(frac, one); + if (f128M_eq(&mod_result, &zero)) { + //frac++; + add_eq_f128_dbl(&frac, 1.0); + } + } + } + + //y += frac; + { + float128_t new_value; + f128M_add(&y, &frac, &new_value); + y = new_value; + } + //y -= bias; + { + float128_t new_value; + f128M_sub(&y, &bias, &new_value); + y = new_value; + } + + if ((e2+LDBL_MANT_DIG & INT_MAX) > emax-5) { + //if (fabsf128(y) >= 0x1p113) + float128_t abs_y = fabsf128(y); + float128_t mant_f128 = make_f128(0x4070000000000000, 0x0000000000000000); + if (!f128M_lt(&abs_y, &mant_f128)) { + if (denormal && bits==LDBL_MANT_DIG+e2-emin) + denormal = 0; + //y *= 0.5; + { + float128_t point_5 = dbl_to_f128(0.5); + float128_t new_value; + f128M_mul(&y, &point_5, &new_value); + y = new_value; + } + + e2++; + } + if (e2+LDBL_MANT_DIG>emax || (denormal && !f128M_eq(&frac, &zero))) + errno = ERANGE; + } + + return scalbnf128(y, e2); +} + +static float128_t hexfloat(struct MuslFILE *f, int bits, int emin, int sign, int pok) +{ + float128_t zero; + ui32_to_f128M(0, &zero); + float128_t one; + ui32_to_f128M(1, &one); + float128_t sixteen; + ui32_to_f128M(16, &sixteen); + float128_t point_5 = dbl_to_f128(0.5); + + uint32_t x = 0; + float128_t y = zero; + float128_t scale = one; + float128_t bias = zero; + int gottail = 0, gotrad = 0, gotdig = 0; + long long rp = 0; + long long dc = 0; + long long e2 = 0; + int d; + int c; + + c = shgetc(f); + + /* Skip leading zeros/underscores */ + for (; c=='0' || c=='_'; c = shgetc(f)) gotdig = 1; + + if (c=='.') { + gotrad = 1; + c = shgetc(f); + /* Count zeros after the radix point before significand */ + for (rp=0; ; c = shgetc(f)) { + if (c == '_') { + continue; + } else if (c == '0') { + gotdig = 1; + rp--; + } else { + break; + } + } + } + + for (; c-'0'<10U || (c|32)-'a'<6U || c=='.' || c=='_'; c = shgetc(f)) { + if (c=='_') { + continue; + } else if (c=='.') { + if (gotrad) break; + rp = dc; + gotrad = 1; + } else { + gotdig = 1; + if (c > '9') d = (c|32)+10-'a'; + else d = c-'0'; + if (dc<8) { + x = x*16 + d; + } else if (dc < LDBL_MANT_DIG/4+1) { + //y += d*(scale/=16); + { + float128_t divided; + f128M_div(&scale, &sixteen, ÷d); + scale = divided; + float128_t d_f128; + i32_to_f128M(d, &d_f128); + float128_t add_op; + f128M_mul(&d_f128, &scale, &add_op); + float128_t new_y; + f128M_add(&y, &add_op, &new_y); + y = new_y; + } + } else if (d && !gottail) { + //y += 0.5*scale; + { + float128_t add_op; + f128M_mul(&point_5, &scale, &add_op); + float128_t new_y; + f128M_add(&y, &add_op, &new_y); + y = new_y; + } + gottail = 1; + } + dc++; + } + } + if (!gotdig) { + shunget(f); + if (pok) { + shunget(f); + if (gotrad) shunget(f); + } else { + shlim(f, 0); + } + //return sign * 0.0; + return dbl_to_f128(sign * 0.0); + } + if (!gotrad) rp = dc; + while (dc<8) x *= 16, dc++; + if ((c|32)=='p') { + e2 = scanexp(f, pok); + if (e2 == LLONG_MIN) { + if (pok) { + shunget(f); + } else { + shlim(f, 0); + return zero; + } + e2 = 0; + } + } else { + shunget(f); + } + e2 += 4*rp - 32; + + if (!x) { + //return sign * 0.0; + return dbl_to_f128(sign * 0.0); + } + if (e2 > -emin) { + errno = ERANGE; + //return sign * LDBL_MAX * LDBL_MAX; + return zero; + } + if (e2 < emin-2*LDBL_MANT_DIG) { + errno = ERANGE; + //return sign * LDBL_MIN * LDBL_MIN; + return zero; + } + + while (x < 0x80000000) { + //if (y>=0.5) + if (!f128M_lt(&y, &point_5)) { + x += x + 1; + //y += y - 1; + { + float128_t minus_one; + f128M_sub(&y, &one, &minus_one); + float128_t new_y; + f128M_add(&y, &minus_one, &new_y); + y = new_y; + } + } else { + x += x; + //y += y; + { + float128_t new_y; + f128M_add(&y, &y, &new_y); + y = new_y; + } + } + e2--; + } + + if (bits > 32+e2-emin) { + bits = 32+e2-emin; + if (bits<0) bits=0; + } + + if (bits < LDBL_MANT_DIG) { + float128_t sign_f128; + i32_to_f128M(sign, &sign_f128); + bias = copysignf128(dbl_to_f128(scalbn(1, 32+LDBL_MANT_DIG-bits-1)), sign_f128); + } + + //if (bits<32 && y && !(x&1)) x++, y=0; + if (bits<32 && !f128M_eq(&y, &zero) && !(x&1)) x++, y=zero; + + //y = bias + sign*(float128_t)x + sign*y; + { + float128_t x_f128; + ui32_to_f128M(x, &x_f128); + float128_t sign_f128; + i32_to_f128M(sign, &sign_f128); + float128_t sign_mul_x; + f128M_mul(&sign_f128, &x_f128, &sign_mul_x); + float128_t sign_mul_y; + f128M_mul(&sign_f128, &y, &sign_mul_y); + float128_t bias_op; + f128M_add(&bias, &sign_mul_x, &bias_op); + float128_t new_y; + f128M_add(&bias_op, &sign_mul_y, &new_y); + y = new_y; + } + //y -= bias; + { + float128_t new_y; + f128M_sub(&y, &bias, &new_y); + y = new_y; + } + + if (f128M_eq(&y, &zero)) errno = ERANGE; + + return scalbnf128(y, e2); +} + +static int isspace(int c) +{ + return c == ' ' || (unsigned)c-'\t' < 5; +} + +static inline float128_t makeInf128() { + union ldshape ux; + ux.i2.hi = 0x7fff000000000000UL; + ux.i2.lo = 0x0UL; + return ux.f; +} + +static inline float128_t makeNaN128() { + uint64_t rand = 0UL; + union ldshape ux; + ux.i2.hi = 0x7fff000000000000UL | (rand & 0xffffffffffffUL); + ux.i2.lo = 0x0UL; + return ux.f; +} + +float128_t __floatscan(struct MuslFILE *f, int prec, int pok) +{ + int sign = 1; + size_t i; + int bits = LDBL_MANT_DIG; + int emin = LDBL_MIN_EXP-bits; + int c; + + while (isspace((c=shgetc(f)))); + + if (c=='+' || c=='-') { + sign -= 2*(c=='-'); + c = shgetc(f); + } + + for (i=0; i<8 && (c|32)=="infinity"[i]; i++) + if (i<7) c = shgetc(f); + if (i==3 || i==8 || (i>3 && pok)) { + if (i!=8) { + shunget(f); + if (pok) for (; i>3; i--) shunget(f); + } + //return sign * INFINITY; + float128_t sign_f128; + i32_to_f128M(sign, &sign_f128); + float128_t infinity_f128 = makeInf128(); + float128_t result; + f128M_mul(&sign_f128, &infinity_f128, &result); + return result; + } + if (!i) for (i=0; i<3 && (c|32)=="nan"[i]; i++) + if (i<2) c = shgetc(f); + if (i==3) { + if (shgetc(f) != '(') { + shunget(f); + return makeNaN128(); + } + for (i=1; ; i++) { + c = shgetc(f); + if (c-'0'<10U || c-'A'<26U || c-'a'<26U || c=='_') + continue; + if (c==')') return makeNaN128(); + shunget(f); + if (!pok) { + errno = EINVAL; + shlim(f, 0); + float128_t zero; + ui32_to_f128M(0, &zero); + return zero; + } + while (i--) shunget(f); + return makeNaN128(); + } + return makeNaN128(); + } + + if (i) { + shunget(f); + errno = EINVAL; + shlim(f, 0); + float128_t zero; + ui32_to_f128M(0, &zero); + return zero; + } + + if (c=='0') { + c = shgetc(f); + if ((c|32) == 'x') + return hexfloat(f, bits, emin, sign, pok); + shunget(f); + c = '0'; + } + + return decfloat(f, c, bits, emin, sign, pok); +} + +float128_t parse_f128(const char *s, char **p) { + struct MuslFILE f; + sh_fromstring(&f, s); + shlim(&f, 0); + float128_t y = __floatscan(&f, 2, 1); + off_t cnt = shcnt(&f); + if (p) *p = cnt ? (char *)s + cnt : (char *)s; + return y; +} diff --git a/src/stage1/parse_f128.h b/src/stage1/parse_f128.h new file mode 100644 index 0000000000000000000000000000000000000000..82cdf6c9a0bfcc38a6e91f2e2da29df58cb757a5 --- /dev/null +++ b/src/stage1/parse_f128.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2015 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef ZIG_PARSE_F128_H +#define ZIG_PARSE_F128_H + +#include "softfloat_types.h" + +#ifdef __cplusplus +#define ZIG_EXTERN_C extern "C" +#else +#define ZIG_EXTERN_C +#endif + +ZIG_EXTERN_C float128_t parse_f128(const char *s, char **p); + +#endif diff --git a/src/stage1/parser.cpp b/src/stage1/parser.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1253baf9ea5104ee655e42d061927ccec3b5bc61 --- /dev/null +++ b/src/stage1/parser.cpp @@ -0,0 +1,3216 @@ +/* + * Copyright (c) 2015 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#include "parser.hpp" +#include "errmsg.hpp" +#include "analyze.hpp" + +#include +#include +#include +#include + +struct ParseContext { + Buf *buf; + size_t current_token; + ZigList *tokens; + ZigType *owner; + ErrColor err_color; +}; + +struct PtrPayload { + Token *asterisk; + Token *payload; +}; + +struct PtrIndexPayload { + Token *asterisk; + Token *payload; + Token *index; +}; + +static AstNode *ast_parse_root(ParseContext *pc); +static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc); +static AstNode *ast_parse_test_decl(ParseContext *pc); +static AstNode *ast_parse_top_level_comptime(ParseContext *pc); +static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod, Buf *doc_comments); +static AstNode *ast_parse_fn_proto(ParseContext *pc); +static AstNode *ast_parse_var_decl(ParseContext *pc); +static AstNode *ast_parse_container_field(ParseContext *pc); +static AstNode *ast_parse_statement(ParseContext *pc); +static AstNode *ast_parse_if_statement(ParseContext *pc); +static AstNode *ast_parse_labeled_statement(ParseContext *pc); +static AstNode *ast_parse_loop_statement(ParseContext *pc); +static AstNode *ast_parse_for_statement(ParseContext *pc); +static AstNode *ast_parse_while_statement(ParseContext *pc); +static AstNode *ast_parse_block_expr_statement(ParseContext *pc); +static AstNode *ast_parse_block_expr(ParseContext *pc); +static AstNode *ast_parse_assign_expr(ParseContext *pc); +static AstNode *ast_parse_expr(ParseContext *pc); +static AstNode *ast_parse_bool_or_expr(ParseContext *pc); +static AstNode *ast_parse_bool_and_expr(ParseContext *pc); +static AstNode *ast_parse_compare_expr(ParseContext *pc); +static AstNode *ast_parse_bitwise_expr(ParseContext *pc); +static AstNode *ast_parse_bit_shift_expr(ParseContext *pc); +static AstNode *ast_parse_addition_expr(ParseContext *pc); +static AstNode *ast_parse_multiply_expr(ParseContext *pc); +static AstNode *ast_parse_prefix_expr(ParseContext *pc); +static AstNode *ast_parse_primary_expr(ParseContext *pc); +static AstNode *ast_parse_if_expr(ParseContext *pc); +static AstNode *ast_parse_block(ParseContext *pc); +static AstNode *ast_parse_loop_expr(ParseContext *pc); +static AstNode *ast_parse_for_expr(ParseContext *pc); +static AstNode *ast_parse_while_expr(ParseContext *pc); +static AstNode *ast_parse_curly_suffix_expr(ParseContext *pc); +static AstNode *ast_parse_init_list(ParseContext *pc); +static AstNode *ast_parse_type_expr(ParseContext *pc); +static AstNode *ast_parse_error_union_expr(ParseContext *pc); +static AstNode *ast_parse_suffix_expr(ParseContext *pc); +static AstNode *ast_parse_primary_type_expr(ParseContext *pc); +static AstNode *ast_parse_container_decl(ParseContext *pc); +static AstNode *ast_parse_error_set_decl(ParseContext *pc); +static AstNode *ast_parse_grouped_expr(ParseContext *pc); +static AstNode *ast_parse_if_type_expr(ParseContext *pc); +static AstNode *ast_parse_labeled_type_expr(ParseContext *pc); +static AstNode *ast_parse_loop_type_expr(ParseContext *pc); +static AstNode *ast_parse_for_type_expr(ParseContext *pc); +static AstNode *ast_parse_while_type_expr(ParseContext *pc); +static AstNode *ast_parse_switch_expr(ParseContext *pc); +static AstNode *ast_parse_asm_expr(ParseContext *pc); +static AstNode *ast_parse_anon_lit(ParseContext *pc); +static AstNode *ast_parse_asm_output(ParseContext *pc); +static AsmOutput *ast_parse_asm_output_item(ParseContext *pc); +static AstNode *ast_parse_asm_input(ParseContext *pc); +static AsmInput *ast_parse_asm_input_item(ParseContext *pc); +static AstNode *ast_parse_asm_clobbers(ParseContext *pc); +static Token *ast_parse_break_label(ParseContext *pc); +static Token *ast_parse_block_label(ParseContext *pc); +static AstNode *ast_parse_field_init(ParseContext *pc); +static AstNode *ast_parse_while_continue_expr(ParseContext *pc); +static AstNode *ast_parse_link_section(ParseContext *pc); +static AstNode *ast_parse_callconv(ParseContext *pc); +static AstNode *ast_parse_param_decl(ParseContext *pc); +static AstNode *ast_parse_param_type(ParseContext *pc); +static AstNode *ast_parse_if_prefix(ParseContext *pc); +static AstNode *ast_parse_while_prefix(ParseContext *pc); +static AstNode *ast_parse_for_prefix(ParseContext *pc); +static Token *ast_parse_payload(ParseContext *pc); +static Optional ast_parse_ptr_payload(ParseContext *pc); +static Optional ast_parse_ptr_index_payload(ParseContext *pc); +static AstNode *ast_parse_switch_prong(ParseContext *pc); +static AstNode *ast_parse_switch_case(ParseContext *pc); +static AstNode *ast_parse_switch_item(ParseContext *pc); +static AstNode *ast_parse_assign_op(ParseContext *pc); +static AstNode *ast_parse_compare_op(ParseContext *pc); +static AstNode *ast_parse_bitwise_op(ParseContext *pc); +static AstNode *ast_parse_bit_shift_op(ParseContext *pc); +static AstNode *ast_parse_addition_op(ParseContext *pc); +static AstNode *ast_parse_multiply_op(ParseContext *pc); +static AstNode *ast_parse_prefix_op(ParseContext *pc); +static AstNode *ast_parse_prefix_type_op(ParseContext *pc); +static AstNode *ast_parse_suffix_op(ParseContext *pc); +static AstNode *ast_parse_fn_call_arguments(ParseContext *pc); +static AstNode *ast_parse_array_type_start(ParseContext *pc); +static AstNode *ast_parse_ptr_type_start(ParseContext *pc); +static AstNode *ast_parse_container_decl_auto(ParseContext *pc); +static AstNode *ast_parse_container_decl_type(ParseContext *pc); +static AstNode *ast_parse_byte_align(ParseContext *pc); + +ATTRIBUTE_PRINTF(3, 4) +ATTRIBUTE_NORETURN +static void ast_error(ParseContext *pc, Token *token, const char *format, ...) { + va_list ap; + va_start(ap, format); + Buf *msg = buf_vprintf(format, ap); + va_end(ap); + + + ErrorMsg *err = err_msg_create_with_line(pc->owner->data.structure.root_struct->path, + token->start_line, token->start_column, + pc->owner->data.structure.root_struct->source_code, + pc->owner->data.structure.root_struct->line_offsets, msg); + err->line_start = token->start_line; + err->column_start = token->start_column; + + print_err_msg(err, pc->err_color); + exit(EXIT_FAILURE); +} + +ATTRIBUTE_NORETURN +static void ast_invalid_token_error(ParseContext *pc, Token *token) { + ast_error(pc, token, "invalid token: '%s'", token_name(token->id)); +} + +static AstNode *ast_create_node_no_line_info(ParseContext *pc, NodeType type) { + AstNode *node = heap::c_allocator.create(); + node->type = type; + node->owner = pc->owner; + return node; +} + +static AstNode *ast_create_node(ParseContext *pc, NodeType type, Token *first_token) { + assert(first_token); + AstNode *node = ast_create_node_no_line_info(pc, type); + node->line = first_token->start_line; + node->column = first_token->start_column; + return node; +} + +static AstNode *ast_create_node_copy_line_info(ParseContext *pc, NodeType type, AstNode *from) { + assert(from); + AstNode *node = ast_create_node_no_line_info(pc, type); + node->line = from->line; + node->column = from->column; + return node; +} + +static Token *peek_token_i(ParseContext *pc, size_t i) { + return &pc->tokens->at(pc->current_token + i); +} + +static Token *peek_token(ParseContext *pc) { + return peek_token_i(pc, 0); +} + +static Token *eat_token(ParseContext *pc) { + Token *res = peek_token(pc); + pc->current_token += 1; + return res; +} + +static Token *eat_token_if(ParseContext *pc, TokenId id) { + Token *res = peek_token(pc); + if (res->id == id) + return eat_token(pc); + + return nullptr; +} + +static Token *expect_token(ParseContext *pc, TokenId id) { + Token *res = eat_token(pc); + if (res->id != id) + ast_error(pc, res, "expected token '%s', found '%s'", token_name(id), token_name(res->id)); + + return res; +} + +static void put_back_token(ParseContext *pc) { + pc->current_token -= 1; +} + +static Buf *token_buf(Token *token) { + if (token == nullptr) + return nullptr; + assert(token->id == TokenIdStringLiteral || token->id == TokenIdMultilineStringLiteral || token->id == TokenIdSymbol); + return &token->data.str_lit.str; +} + +static BigInt *token_bigint(Token *token) { + assert(token->id == TokenIdIntLiteral); + return &token->data.int_lit.bigint; +} + +static AstNode *token_symbol(ParseContext *pc, Token *token) { + assert(token->id == TokenIdSymbol); + AstNode *res = ast_create_node(pc, NodeTypeSymbol, token); + res->data.symbol_expr.symbol = token_buf(token); + return res; +} + +// (Rule SEP)* Rule? +template +static ZigList ast_parse_list(ParseContext *pc, TokenId sep, T *(*parser)(ParseContext*)) { + ZigList res = {}; + while (true) { + T *curr = parser(pc); + if (curr == nullptr) + break; + + res.append(curr); + if (eat_token_if(pc, sep) == nullptr) + break; + } + + return res; +} + +static AstNode *ast_expect(ParseContext *pc, AstNode *(*parser)(ParseContext*)) { + AstNode *res = parser(pc); + if (res == nullptr) + ast_invalid_token_error(pc, peek_token(pc)); + return res; +} + +enum BinOpChain { + BinOpChainOnce, + BinOpChainInf, +}; + +// Op* Child +static AstNode *ast_parse_prefix_op_expr( + ParseContext *pc, + AstNode *(*op_parser)(ParseContext *), + AstNode *(*child_parser)(ParseContext *) +) { + AstNode *res = nullptr; + AstNode **right = &res; + while (true) { + AstNode *prefix = op_parser(pc); + if (prefix == nullptr) + break; + + *right = prefix; + switch (prefix->type) { + case NodeTypePrefixOpExpr: + right = &prefix->data.prefix_op_expr.primary_expr; + break; + case NodeTypeReturnExpr: + right = &prefix->data.return_expr.expr; + break; + case NodeTypeAwaitExpr: + right = &prefix->data.await_expr.expr; + break; + case NodeTypeAnyFrameType: + right = &prefix->data.anyframe_type.payload_type; + break; + case NodeTypeArrayType: + right = &prefix->data.array_type.child_type; + break; + case NodeTypeInferredArrayType: + right = &prefix->data.inferred_array_type.child_type; + break; + case NodeTypePointerType: { + // We might get two pointers from *_ptr_type_start + AstNode *child = prefix->data.pointer_type.op_expr; + if (child == nullptr) + child = prefix; + right = &child->data.pointer_type.op_expr; + break; + } + default: + zig_unreachable(); + } + } + + // If we have already consumed a token, and determined that + // this node is a prefix op, then we expect that the node has + // a child. + if (res != nullptr) { + *right = ast_expect(pc, child_parser); + } else { + // Otherwise, if we didn't consume a token, then we can return + // null, if the child expr did. + *right = child_parser(pc); + if (*right == nullptr) + return nullptr; + } + + return res; +} + +// Child (Op Child)(*/?) +static AstNode *ast_parse_bin_op_expr( + ParseContext *pc, + BinOpChain chain, + AstNode *(*op_parse)(ParseContext*), + AstNode *(*child_parse)(ParseContext*) +) { + AstNode *res = child_parse(pc); + if (res == nullptr) + return nullptr; + + do { + AstNode *op = op_parse(pc); + if (op == nullptr) + break; + + AstNode *left = res; + AstNode *right = ast_expect(pc, child_parse); + res = op; + switch (op->type) { + case NodeTypeBinOpExpr: + op->data.bin_op_expr.op1 = left; + op->data.bin_op_expr.op2 = right; + break; + case NodeTypeCatchExpr: + op->data.unwrap_err_expr.op1 = left; + op->data.unwrap_err_expr.op2 = right; + break; + default: + zig_unreachable(); + } + } while (chain == BinOpChainInf); + + return res; +} + +// IfPrefix Body (KEYWORD_else Payload? Body)? +static AstNode *ast_parse_if_expr_helper(ParseContext *pc, AstNode *(*body_parser)(ParseContext*)) { + AstNode *res = ast_parse_if_prefix(pc); + if (res == nullptr) + return nullptr; + + AstNode *body = ast_expect(pc, body_parser); + Token *err_payload = nullptr; + AstNode *else_body = nullptr; + if (eat_token_if(pc, TokenIdKeywordElse) != nullptr) { + err_payload = ast_parse_payload(pc); + else_body = ast_expect(pc, body_parser); + } + + assert(res->type == NodeTypeIfOptional); + if (err_payload != nullptr) { + AstNodeTestExpr old = res->data.test_expr; + res->type = NodeTypeIfErrorExpr; + res->data.if_err_expr.target_node = old.target_node; + res->data.if_err_expr.var_is_ptr = old.var_is_ptr; + res->data.if_err_expr.var_symbol = old.var_symbol; + res->data.if_err_expr.then_node = body; + res->data.if_err_expr.err_symbol = token_buf(err_payload); + res->data.if_err_expr.else_node = else_body; + return res; + } + + if (res->data.test_expr.var_symbol != nullptr) { + res->data.test_expr.then_node = body; + res->data.test_expr.else_node = else_body; + return res; + } + + AstNodeTestExpr old = res->data.test_expr; + res->type = NodeTypeIfBoolExpr; + res->data.if_bool_expr.condition = old.target_node; + res->data.if_bool_expr.then_block = body; + res->data.if_bool_expr.else_node = else_body; + return res; +} + +// KEYWORD_inline? (ForLoop / WhileLoop) +static AstNode *ast_parse_loop_expr_helper( + ParseContext *pc, + AstNode *(*for_parser)(ParseContext *), + AstNode *(*while_parser)(ParseContext *) +) { + Token *inline_token = eat_token_if(pc, TokenIdKeywordInline); + AstNode *for_expr = for_parser(pc); + if (for_expr != nullptr) { + assert(for_expr->type == NodeTypeForExpr); + for_expr->data.for_expr.is_inline = inline_token != nullptr; + return for_expr; + } + + AstNode *while_expr = while_parser(pc); + if (while_expr != nullptr) { + assert(while_expr->type == NodeTypeWhileExpr); + while_expr->data.while_expr.is_inline = inline_token != nullptr; + return while_expr; + } + + if (inline_token != nullptr) + ast_invalid_token_error(pc, peek_token(pc)); + return nullptr; +} + +// ForPrefix Body (KEYWORD_else Body)? +static AstNode *ast_parse_for_expr_helper(ParseContext *pc, AstNode *(*body_parser)(ParseContext*)) { + AstNode *res = ast_parse_for_prefix(pc); + if (res == nullptr) + return nullptr; + + AstNode *body = ast_expect(pc, body_parser); + AstNode *else_body = nullptr; + if (eat_token_if(pc, TokenIdKeywordElse) != nullptr) + else_body = ast_expect(pc, body_parser); + + assert(res->type == NodeTypeForExpr); + res->data.for_expr.body = body; + res->data.for_expr.else_node = else_body; + return res; +} + +// WhilePrefix Body (KEYWORD_else Payload? Body)? +static AstNode *ast_parse_while_expr_helper(ParseContext *pc, AstNode *(*body_parser)(ParseContext*)) { + AstNode *res = ast_parse_while_prefix(pc); + if (res == nullptr) + return nullptr; + + AstNode *body = ast_expect(pc, body_parser); + Token *err_payload = nullptr; + AstNode *else_body = nullptr; + if (eat_token_if(pc, TokenIdKeywordElse) != nullptr) { + err_payload = ast_parse_payload(pc); + else_body = ast_expect(pc, body_parser); + } + + assert(res->type == NodeTypeWhileExpr); + res->data.while_expr.body = body; + res->data.while_expr.err_symbol = token_buf(err_payload); + res->data.while_expr.else_node = else_body; + return res; +} + +template +AstNode *ast_parse_bin_op_simple(ParseContext *pc) { + Token *op_token = eat_token_if(pc, id); + if (op_token == nullptr) + return nullptr; + + AstNode *res = ast_create_node(pc, NodeTypeBinOpExpr, op_token); + res->data.bin_op_expr.bin_op = op; + return res; +} + +AstNode *ast_parse(Buf *buf, ZigList *tokens, ZigType *owner, ErrColor err_color) { + ParseContext pc = {}; + pc.err_color = err_color; + pc.owner = owner; + pc.buf = buf; + pc.tokens = tokens; + return ast_parse_root(&pc); +} + +// Root <- skip ContainerMembers eof +static AstNode *ast_parse_root(ParseContext *pc) { + Token *first = peek_token(pc); + AstNodeContainerDecl members = ast_parse_container_members(pc); + if (pc->current_token != pc->tokens->length - 1) + ast_invalid_token_error(pc, peek_token(pc)); + + AstNode *node = ast_create_node(pc, NodeTypeContainerDecl, first); + node->data.container_decl.fields = members.fields; + node->data.container_decl.decls = members.decls; + node->data.container_decl.layout = ContainerLayoutAuto; + node->data.container_decl.kind = ContainerKindStruct; + node->data.container_decl.is_root = true; + if (buf_len(&members.doc_comments) != 0) { + node->data.container_decl.doc_comments = members.doc_comments; + } + + return node; +} + +static Token *ast_parse_doc_comments(ParseContext *pc, Buf *buf) { + Token *first_doc_token = nullptr; + Token *doc_token = nullptr; + while ((doc_token = eat_token_if(pc, TokenIdDocComment))) { + if (first_doc_token == nullptr) { + first_doc_token = doc_token; + } + if (buf->list.length == 0) { + buf_resize(buf, 0); + } + // chops off '///' but leaves '\n' + buf_append_mem(buf, buf_ptr(pc->buf) + doc_token->start_pos + 3, + doc_token->end_pos - doc_token->start_pos - 3); + } + return first_doc_token; +} + +static void ast_parse_container_doc_comments(ParseContext *pc, Buf *buf) { + if (buf_len(buf) != 0 && peek_token(pc)->id == TokenIdContainerDocComment) { + buf_append_char(buf, '\n'); + } + Token *doc_token = nullptr; + while ((doc_token = eat_token_if(pc, TokenIdContainerDocComment))) { + if (buf->list.length == 0) { + buf_resize(buf, 0); + } + // chops off '//!' but leaves '\n' + buf_append_mem(buf, buf_ptr(pc->buf) + doc_token->start_pos + 3, + doc_token->end_pos - doc_token->start_pos - 3); + } +} + +enum ContainerFieldState { + // no fields have been seen + ContainerFieldStateNone, + // currently parsing fields + ContainerFieldStateSeen, + // saw fields and then a declaration after them + ContainerFieldStateEnd, +}; + +// ContainerMembers +// <- TestDecl ContainerMembers +// / TopLevelComptime ContainerMembers +// / KEYWORD_pub? TopLevelDecl ContainerMembers +// / ContainerField COMMA ContainerMembers +// / ContainerField +// / +static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc) { + AstNodeContainerDecl res = {}; + Buf tld_doc_comment_buf = BUF_INIT; + buf_resize(&tld_doc_comment_buf, 0); + ContainerFieldState field_state = ContainerFieldStateNone; + Token *first_token = nullptr; + for (;;) { + ast_parse_container_doc_comments(pc, &tld_doc_comment_buf); + + Token *peeked_token = peek_token(pc); + + AstNode *test_decl = ast_parse_test_decl(pc); + if (test_decl != nullptr) { + if (field_state == ContainerFieldStateSeen) { + field_state = ContainerFieldStateEnd; + first_token = peeked_token; + } + res.decls.append(test_decl); + continue; + } + + AstNode *top_level_comptime = ast_parse_top_level_comptime(pc); + if (top_level_comptime != nullptr) { + if (field_state == ContainerFieldStateSeen) { + field_state = ContainerFieldStateEnd; + first_token = peeked_token; + } + res.decls.append(top_level_comptime); + continue; + } + + Buf doc_comment_buf = BUF_INIT; + ast_parse_doc_comments(pc, &doc_comment_buf); + + peeked_token = peek_token(pc); + + Token *visib_token = eat_token_if(pc, TokenIdKeywordPub); + VisibMod visib_mod = visib_token != nullptr ? VisibModPub : VisibModPrivate; + + AstNode *top_level_decl = ast_parse_top_level_decl(pc, visib_mod, &doc_comment_buf); + if (top_level_decl != nullptr) { + if (field_state == ContainerFieldStateSeen) { + field_state = ContainerFieldStateEnd; + first_token = peeked_token; + } + res.decls.append(top_level_decl); + continue; + } + + if (visib_token != nullptr) { + ast_error(pc, peek_token(pc), "expected function or variable declaration after pub"); + } + + Token *comptime_token = eat_token_if(pc, TokenIdKeywordCompTime); + + AstNode *container_field = ast_parse_container_field(pc); + if (container_field != nullptr) { + switch (field_state) { + case ContainerFieldStateNone: + field_state = ContainerFieldStateSeen; + break; + case ContainerFieldStateSeen: + break; + case ContainerFieldStateEnd: + ast_error(pc, first_token, "declarations are not allowed between container fields"); + } + + assert(container_field->type == NodeTypeStructField); + container_field->data.struct_field.doc_comments = doc_comment_buf; + container_field->data.struct_field.comptime_token = comptime_token; + res.fields.append(container_field); + if (eat_token_if(pc, TokenIdComma) != nullptr) { + continue; + } else { + break; + } + } + + break; + } + res.doc_comments = tld_doc_comment_buf; + return res; +} + +// TestDecl <- KEYWORD_test STRINGLITERALSINGLE Block +static AstNode *ast_parse_test_decl(ParseContext *pc) { + Token *test = eat_token_if(pc, TokenIdKeywordTest); + if (test == nullptr) + return nullptr; + + Token *name = expect_token(pc, TokenIdStringLiteral); + AstNode *block = ast_expect(pc, ast_parse_block); + AstNode *res = ast_create_node(pc, NodeTypeTestDecl, test); + res->data.test_decl.name = token_buf(name); + res->data.test_decl.body = block; + return res; +} + +// TopLevelComptime <- KEYWORD_comptime BlockExpr +static AstNode *ast_parse_top_level_comptime(ParseContext *pc) { + Token *comptime = eat_token_if(pc, TokenIdKeywordCompTime); + if (comptime == nullptr) + return nullptr; + + // 1 token lookahead because it could be a comptime struct field + Token *lbrace = peek_token(pc); + if (lbrace->id != TokenIdLBrace) { + put_back_token(pc); + return nullptr; + } + + AstNode *block = ast_expect(pc, ast_parse_block_expr); + AstNode *res = ast_create_node(pc, NodeTypeCompTime, comptime); + res->data.comptime_expr.expr = block; + return res; +} + +// TopLevelDecl +// <- (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE? / (KEYWORD_inline / KEYWORD_noinline))? FnProto (SEMICOLON / Block) +// / (KEYWORD_export / KEYWORD_extern STRINGLITERALSINGLE?)? KEYWORD_threadlocal? VarDecl +// / KEYWORD_use Expr SEMICOLON +static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod, Buf *doc_comments) { + Token *first = eat_token_if(pc, TokenIdKeywordExport); + if (first == nullptr) + first = eat_token_if(pc, TokenIdKeywordExtern); + if (first == nullptr) + first = eat_token_if(pc, TokenIdKeywordInline); + if (first == nullptr) + first = eat_token_if(pc, TokenIdKeywordNoInline); + if (first != nullptr) { + Token *lib_name = nullptr; + if (first->id == TokenIdKeywordExtern) + lib_name = eat_token_if(pc, TokenIdStringLiteral); + + if (first->id != TokenIdKeywordInline && first->id != TokenIdKeywordNoInline) { + Token *thread_local_kw = eat_token_if(pc, TokenIdKeywordThreadLocal); + AstNode *var_decl = ast_parse_var_decl(pc); + if (var_decl != nullptr) { + assert(var_decl->type == NodeTypeVariableDeclaration); + if (first->id == TokenIdKeywordExtern && var_decl->data.variable_declaration.expr != nullptr) { + ast_error(pc, first, "extern variables have no initializers"); + } + var_decl->line = first->start_line; + var_decl->column = first->start_column; + var_decl->data.variable_declaration.threadlocal_tok = thread_local_kw; + var_decl->data.variable_declaration.visib_mod = visib_mod; + var_decl->data.variable_declaration.doc_comments = *doc_comments; + var_decl->data.variable_declaration.is_extern = first->id == TokenIdKeywordExtern; + var_decl->data.variable_declaration.is_export = first->id == TokenIdKeywordExport; + var_decl->data.variable_declaration.lib_name = token_buf(lib_name); + return var_decl; + } + + if (thread_local_kw != nullptr) + put_back_token(pc); + } + + AstNode *fn_proto = ast_parse_fn_proto(pc); + if (fn_proto != nullptr) { + AstNode *body = ast_parse_block(pc); + if (body == nullptr) + expect_token(pc, TokenIdSemicolon); + + assert(fn_proto->type == NodeTypeFnProto); + fn_proto->line = first->start_line; + fn_proto->column = first->start_column; + fn_proto->data.fn_proto.visib_mod = visib_mod; + fn_proto->data.fn_proto.doc_comments = *doc_comments; + if (!fn_proto->data.fn_proto.is_extern) + fn_proto->data.fn_proto.is_extern = first->id == TokenIdKeywordExtern; + fn_proto->data.fn_proto.is_export = first->id == TokenIdKeywordExport; + switch (first->id) { + case TokenIdKeywordInline: + fn_proto->data.fn_proto.fn_inline = FnInlineAlways; + break; + case TokenIdKeywordNoInline: + fn_proto->data.fn_proto.fn_inline = FnInlineNever; + break; + default: + fn_proto->data.fn_proto.fn_inline = FnInlineAuto; + break; + } + fn_proto->data.fn_proto.lib_name = token_buf(lib_name); + + AstNode *res = fn_proto; + if (body != nullptr) { + if (fn_proto->data.fn_proto.is_extern) { + ast_error(pc, first, "extern functions have no body"); + } + res = ast_create_node_copy_line_info(pc, NodeTypeFnDef, fn_proto); + res->data.fn_def.fn_proto = fn_proto; + res->data.fn_def.body = body; + fn_proto->data.fn_proto.fn_def_node = res; + } + + return res; + } + + ast_invalid_token_error(pc, peek_token(pc)); + } + + Token *thread_local_kw = eat_token_if(pc, TokenIdKeywordThreadLocal); + AstNode *var_decl = ast_parse_var_decl(pc); + if (var_decl != nullptr) { + assert(var_decl->type == NodeTypeVariableDeclaration); + var_decl->data.variable_declaration.visib_mod = visib_mod; + var_decl->data.variable_declaration.doc_comments = *doc_comments; + var_decl->data.variable_declaration.threadlocal_tok = thread_local_kw; + return var_decl; + } + + if (thread_local_kw != nullptr) + put_back_token(pc); + + AstNode *fn_proto = ast_parse_fn_proto(pc); + if (fn_proto != nullptr) { + AstNode *body = ast_parse_block(pc); + if (body == nullptr) + expect_token(pc, TokenIdSemicolon); + + assert(fn_proto->type == NodeTypeFnProto); + fn_proto->data.fn_proto.visib_mod = visib_mod; + fn_proto->data.fn_proto.doc_comments = *doc_comments; + AstNode *res = fn_proto; + if (body != nullptr) { + res = ast_create_node_copy_line_info(pc, NodeTypeFnDef, fn_proto); + res->data.fn_def.fn_proto = fn_proto; + res->data.fn_def.body = body; + fn_proto->data.fn_proto.fn_def_node = res; + } + + return res; + } + + Token *usingnamespace = eat_token_if(pc, TokenIdKeywordUsingNamespace); + if (usingnamespace != nullptr) { + AstNode *expr = ast_expect(pc, ast_parse_expr); + expect_token(pc, TokenIdSemicolon); + + AstNode *res = ast_create_node(pc, NodeTypeUsingNamespace, usingnamespace); + res->data.using_namespace.visib_mod = visib_mod; + res->data.using_namespace.expr = expr; + return res; + } + + return nullptr; +} + +// FnProto <- KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? EXCLAMATIONMARK? (KEYWORD_anytype / TypeExpr) +static AstNode *ast_parse_fn_proto(ParseContext *pc) { + Token *first = eat_token_if(pc, TokenIdKeywordFn); + if (first == nullptr) { + return nullptr; + } + + Token *identifier = eat_token_if(pc, TokenIdSymbol); + expect_token(pc, TokenIdLParen); + ZigList params = ast_parse_list(pc, TokenIdComma, ast_parse_param_decl); + expect_token(pc, TokenIdRParen); + + AstNode *align_expr = ast_parse_byte_align(pc); + AstNode *section_expr = ast_parse_link_section(pc); + AstNode *callconv_expr = ast_parse_callconv(pc); + Token *anytype = eat_token_if(pc, TokenIdKeywordAnyType); + Token *exmark = nullptr; + AstNode *return_type = nullptr; + if (anytype == nullptr) { + exmark = eat_token_if(pc, TokenIdBang); + return_type = ast_expect(pc, ast_parse_type_expr); + } + + AstNode *res = ast_create_node(pc, NodeTypeFnProto, first); + res->data.fn_proto = {}; + res->data.fn_proto.name = token_buf(identifier); + res->data.fn_proto.params = params; + res->data.fn_proto.align_expr = align_expr; + res->data.fn_proto.section_expr = section_expr; + res->data.fn_proto.callconv_expr = callconv_expr; + res->data.fn_proto.return_anytype_token = anytype; + res->data.fn_proto.auto_err_set = exmark != nullptr; + res->data.fn_proto.return_type = return_type; + + for (size_t i = 0; i < params.length; i++) { + AstNode *param_decl = params.at(i); + assert(param_decl->type == NodeTypeParamDecl); + if (param_decl->data.param_decl.is_var_args) + res->data.fn_proto.is_var_args = true; + if (i != params.length - 1 && res->data.fn_proto.is_var_args) + ast_error(pc, first, "Function prototype have varargs as a none last parameter."); + } + return res; +} + +// VarDecl <- (KEYWORD_const / KEYWORD_var) IDENTIFIER (COLON TypeExpr)? ByteAlign? LinkSection? (EQUAL Expr)? SEMICOLON +static AstNode *ast_parse_var_decl(ParseContext *pc) { + Token *mut_kw = eat_token_if(pc, TokenIdKeywordConst); + if (mut_kw == nullptr) + mut_kw = eat_token_if(pc, TokenIdKeywordVar); + if (mut_kw == nullptr) + return nullptr; + + Token *identifier = expect_token(pc, TokenIdSymbol); + AstNode *type_expr = nullptr; + if (eat_token_if(pc, TokenIdColon) != nullptr) + type_expr = ast_expect(pc, ast_parse_type_expr); + + AstNode *align_expr = ast_parse_byte_align(pc); + AstNode *section_expr = ast_parse_link_section(pc); + AstNode *expr = nullptr; + if (eat_token_if(pc, TokenIdEq) != nullptr) + expr = ast_expect(pc, ast_parse_expr); + + expect_token(pc, TokenIdSemicolon); + + AstNode *res = ast_create_node(pc, NodeTypeVariableDeclaration, mut_kw); + res->data.variable_declaration.is_const = mut_kw->id == TokenIdKeywordConst; + res->data.variable_declaration.symbol = token_buf(identifier); + res->data.variable_declaration.type = type_expr; + res->data.variable_declaration.align_expr = align_expr; + res->data.variable_declaration.section_expr = section_expr; + res->data.variable_declaration.expr = expr; + return res; +} + +// ContainerField <- KEYWORD_comptime? IDENTIFIER (COLON TypeExpr ByteAlign?)? (EQUAL Expr)? +static AstNode *ast_parse_container_field(ParseContext *pc) { + Token *identifier = eat_token_if(pc, TokenIdSymbol); + if (identifier == nullptr) + return nullptr; + + AstNode *type_expr = nullptr; + if (eat_token_if(pc, TokenIdColon) != nullptr) { + Token *anytype_tok = eat_token_if(pc, TokenIdKeywordAnyType); + if (anytype_tok != nullptr) { + type_expr = ast_create_node(pc, NodeTypeAnyTypeField, anytype_tok); + } else { + type_expr = ast_expect(pc, ast_parse_type_expr); + } + } + AstNode *align_expr = ast_parse_byte_align(pc); + AstNode *expr = nullptr; + if (eat_token_if(pc, TokenIdEq) != nullptr) + expr = ast_expect(pc, ast_parse_expr); + + AstNode *res = ast_create_node(pc, NodeTypeStructField, identifier); + res->data.struct_field.name = token_buf(identifier); + res->data.struct_field.type = type_expr; + res->data.struct_field.value = expr; + res->data.struct_field.align_expr = align_expr; + return res; +} + +// Statement +// <- KEYWORD_comptime? VarDecl +// / KEYWORD_comptime BlockExprStatement +// / KEYWORD_nosuspend BlockExprStatement +// / KEYWORD_suspend (SEMICOLON / BlockExprStatement) +// / KEYWORD_defer BlockExprStatement +// / KEYWORD_errdefer Payload? BlockExprStatement +// / IfStatement +// / LabeledStatement +// / SwitchExpr +// / AssignExpr SEMICOLON +static AstNode *ast_parse_statement(ParseContext *pc) { + Token *comptime = eat_token_if(pc, TokenIdKeywordCompTime); + AstNode *var_decl = ast_parse_var_decl(pc); + if (var_decl != nullptr) { + assert(var_decl->type == NodeTypeVariableDeclaration); + var_decl->data.variable_declaration.is_comptime = comptime != nullptr; + return var_decl; + } + + if (comptime != nullptr) { + AstNode *statement = ast_expect(pc, ast_parse_block_expr_statement); + AstNode *res = ast_create_node(pc, NodeTypeCompTime, comptime); + res->data.comptime_expr.expr = statement; + return res; + } + + Token *nosuspend = eat_token_if(pc, TokenIdKeywordNoSuspend); + if (nosuspend != nullptr) { + AstNode *statement = ast_expect(pc, ast_parse_block_expr_statement); + AstNode *res = ast_create_node(pc, NodeTypeNoSuspend, nosuspend); + res->data.nosuspend_expr.expr = statement; + return res; + } + + Token *suspend = eat_token_if(pc, TokenIdKeywordSuspend); + if (suspend != nullptr) { + AstNode *statement = nullptr; + if (eat_token_if(pc, TokenIdSemicolon) == nullptr) + statement = ast_expect(pc, ast_parse_block_expr_statement); + + AstNode *res = ast_create_node(pc, NodeTypeSuspend, suspend); + res->data.suspend.block = statement; + return res; + } + + Token *defer = eat_token_if(pc, TokenIdKeywordDefer); + if (defer == nullptr) + defer = eat_token_if(pc, TokenIdKeywordErrdefer); + if (defer != nullptr) { + Token *payload = (defer->id == TokenIdKeywordErrdefer) ? + ast_parse_payload(pc) : nullptr; + AstNode *statement = ast_expect(pc, ast_parse_block_expr_statement); + AstNode *res = ast_create_node(pc, NodeTypeDefer, defer); + + res->data.defer.kind = ReturnKindUnconditional; + res->data.defer.expr = statement; + if (defer->id == TokenIdKeywordErrdefer) { + res->data.defer.kind = ReturnKindError; + if (payload != nullptr) + res->data.defer.err_payload = token_symbol(pc, payload); + } + return res; + } + + AstNode *if_statement = ast_parse_if_statement(pc); + if (if_statement != nullptr) + return if_statement; + + AstNode *labeled_statement = ast_parse_labeled_statement(pc); + if (labeled_statement != nullptr) + return labeled_statement; + + AstNode *switch_expr = ast_parse_switch_expr(pc); + if (switch_expr != nullptr) + return switch_expr; + + AstNode *assign = ast_parse_assign_expr(pc); + if (assign != nullptr) { + expect_token(pc, TokenIdSemicolon); + return assign; + } + + return nullptr; +} + +// IfStatement +// <- IfPrefix BlockExpr ( KEYWORD_else Payload? Statement )? +// / IfPrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement ) +static AstNode *ast_parse_if_statement(ParseContext *pc) { + AstNode *res = ast_parse_if_prefix(pc); + if (res == nullptr) + return nullptr; + + AstNode *body = ast_parse_block_expr(pc); + bool requires_semi = false; + if (body == nullptr) { + requires_semi = true; + body = ast_parse_assign_expr(pc); + } + + if (body == nullptr) { + Token *tok = eat_token(pc); + ast_error(pc, tok, "expected if body, found '%s'", token_name(tok->id)); + } + + Token *err_payload = nullptr; + AstNode *else_body = nullptr; + if (eat_token_if(pc, TokenIdKeywordElse) != nullptr) { + err_payload = ast_parse_payload(pc); + else_body = ast_expect(pc, ast_parse_statement); + } + + if (requires_semi && else_body == nullptr) + expect_token(pc, TokenIdSemicolon); + + assert(res->type == NodeTypeIfOptional); + if (err_payload != nullptr) { + AstNodeTestExpr old = res->data.test_expr; + res->type = NodeTypeIfErrorExpr; + res->data.if_err_expr.target_node = old.target_node; + res->data.if_err_expr.var_is_ptr = old.var_is_ptr; + res->data.if_err_expr.var_symbol = old.var_symbol; + res->data.if_err_expr.then_node = body; + res->data.if_err_expr.err_symbol = token_buf(err_payload); + res->data.if_err_expr.else_node = else_body; + return res; + } + + if (res->data.test_expr.var_symbol != nullptr) { + res->data.test_expr.then_node = body; + res->data.test_expr.else_node = else_body; + return res; + } + + AstNodeTestExpr old = res->data.test_expr; + res->type = NodeTypeIfBoolExpr; + res->data.if_bool_expr.condition = old.target_node; + res->data.if_bool_expr.then_block = body; + res->data.if_bool_expr.else_node = else_body; + return res; +} + +// LabeledStatement <- BlockLabel? (Block / LoopStatement) +static AstNode *ast_parse_labeled_statement(ParseContext *pc) { + Token *label = ast_parse_block_label(pc); + AstNode *block = ast_parse_block(pc); + if (block != nullptr) { + assert(block->type == NodeTypeBlock); + block->data.block.name = token_buf(label); + return block; + } + + AstNode *loop = ast_parse_loop_statement(pc); + if (loop != nullptr) { + switch (loop->type) { + case NodeTypeForExpr: + loop->data.for_expr.name = token_buf(label); + break; + case NodeTypeWhileExpr: + loop->data.while_expr.name = token_buf(label); + break; + default: + zig_unreachable(); + } + return loop; + } + + if (label != nullptr) + ast_invalid_token_error(pc, peek_token(pc)); + return nullptr; +} + +// LoopStatement <- KEYWORD_inline? (ForStatement / WhileStatement) +static AstNode *ast_parse_loop_statement(ParseContext *pc) { + Token *inline_token = eat_token_if(pc, TokenIdKeywordInline); + AstNode *for_statement = ast_parse_for_statement(pc); + if (for_statement != nullptr) { + assert(for_statement->type == NodeTypeForExpr); + for_statement->data.for_expr.is_inline = inline_token != nullptr; + return for_statement; + } + + AstNode *while_statement = ast_parse_while_statement(pc); + if (while_statement != nullptr) { + assert(while_statement->type == NodeTypeWhileExpr); + while_statement->data.while_expr.is_inline = inline_token != nullptr; + return while_statement; + } + + if (inline_token != nullptr) + ast_invalid_token_error(pc, peek_token(pc)); + return nullptr; +} + +// ForStatement +// <- ForPrefix BlockExpr ( KEYWORD_else Statement )? +// / ForPrefix AssignExpr ( SEMICOLON / KEYWORD_else Statement ) +static AstNode *ast_parse_for_statement(ParseContext *pc) { + AstNode *res = ast_parse_for_prefix(pc); + if (res == nullptr) + return nullptr; + + AstNode *body = ast_parse_block_expr(pc); + bool requires_semi = false; + if (body == nullptr) { + requires_semi = true; + body = ast_parse_assign_expr(pc); + } + + if (body == nullptr) { + Token *tok = eat_token(pc); + ast_error(pc, tok, "expected loop body, found '%s'", token_name(tok->id)); + } + + AstNode *else_body = nullptr; + if (eat_token_if(pc, TokenIdKeywordElse) != nullptr) { + else_body = ast_expect(pc, ast_parse_statement); + } + + if (requires_semi && else_body == nullptr) + expect_token(pc, TokenIdSemicolon); + + assert(res->type == NodeTypeForExpr); + res->data.for_expr.body = body; + res->data.for_expr.else_node = else_body; + return res; +} + +// WhileStatement +// <- WhilePrefix BlockExpr ( KEYWORD_else Payload? Statement )? +// / WhilePrefix AssignExpr ( SEMICOLON / KEYWORD_else Payload? Statement ) +static AstNode *ast_parse_while_statement(ParseContext *pc) { + AstNode *res = ast_parse_while_prefix(pc); + if (res == nullptr) + return nullptr; + + AstNode *body = ast_parse_block_expr(pc); + bool requires_semi = false; + if (body == nullptr) { + requires_semi = true; + body = ast_parse_assign_expr(pc); + } + + if (body == nullptr) { + Token *tok = eat_token(pc); + ast_error(pc, tok, "expected loop body, found '%s'", token_name(tok->id)); + } + + Token *err_payload = nullptr; + AstNode *else_body = nullptr; + if (eat_token_if(pc, TokenIdKeywordElse) != nullptr) { + err_payload = ast_parse_payload(pc); + else_body = ast_expect(pc, ast_parse_statement); + } + + if (requires_semi && else_body == nullptr) + expect_token(pc, TokenIdSemicolon); + + assert(res->type == NodeTypeWhileExpr); + res->data.while_expr.body = body; + res->data.while_expr.err_symbol = token_buf(err_payload); + res->data.while_expr.else_node = else_body; + return res; +} + + +// BlockExprStatement +// <- BlockExpr +// / AssignExpr SEMICOLON +static AstNode *ast_parse_block_expr_statement(ParseContext *pc) { + AstNode *block = ast_parse_block_expr(pc); + if (block != nullptr) + return block; + + AstNode *assign_expr = ast_parse_assign_expr(pc); + if (assign_expr != nullptr) { + expect_token(pc, TokenIdSemicolon); + return assign_expr; + } + + return nullptr; +} + +// BlockExpr <- BlockLabel? Block +static AstNode *ast_parse_block_expr(ParseContext *pc) { + Token *label = ast_parse_block_label(pc); + if (label != nullptr) { + AstNode *res = ast_expect(pc, ast_parse_block); + assert(res->type == NodeTypeBlock); + res->data.block.name = token_buf(label); + return res; + } + + return ast_parse_block(pc); +} + +// AssignExpr <- Expr (AssignOp Expr)? +static AstNode *ast_parse_assign_expr(ParseContext *pc) { + return ast_parse_bin_op_expr(pc, BinOpChainOnce, ast_parse_assign_op, ast_parse_expr); +} + +// Expr <- KEYWORD_try* BoolOrExpr +static AstNode *ast_parse_expr(ParseContext *pc) { + return ast_parse_prefix_op_expr( + pc, + [](ParseContext *context) { + Token *try_token = eat_token_if(context, TokenIdKeywordTry); + if (try_token != nullptr) { + AstNode *res = ast_create_node(context, NodeTypeReturnExpr, try_token); + res->data.return_expr.kind = ReturnKindError; + return res; + } + + return (AstNode*)nullptr; + }, + ast_parse_bool_or_expr + ); +} + +// BoolOrExpr <- BoolAndExpr (KEYWORD_or BoolAndExpr)* +static AstNode *ast_parse_bool_or_expr(ParseContext *pc) { + return ast_parse_bin_op_expr( + pc, + BinOpChainInf, + ast_parse_bin_op_simple, + ast_parse_bool_and_expr + ); +} + +// BoolAndExpr <- CompareExpr (KEYWORD_and CompareExpr)* +static AstNode *ast_parse_bool_and_expr(ParseContext *pc) { + return ast_parse_bin_op_expr( + pc, + BinOpChainInf, + ast_parse_bin_op_simple, + ast_parse_compare_expr + ); +} + +// CompareExpr <- BitwiseExpr (CompareOp BitwiseExpr)? +static AstNode *ast_parse_compare_expr(ParseContext *pc) { + return ast_parse_bin_op_expr(pc, BinOpChainOnce, ast_parse_compare_op, ast_parse_bitwise_expr); +} + +// BitwiseExpr <- BitShiftExpr (BitwiseOp BitShiftExpr)* +static AstNode *ast_parse_bitwise_expr(ParseContext *pc) { + return ast_parse_bin_op_expr(pc, BinOpChainInf, ast_parse_bitwise_op, ast_parse_bit_shift_expr); +} + +// BitShiftExpr <- AdditionExpr (BitShiftOp AdditionExpr)* +static AstNode *ast_parse_bit_shift_expr(ParseContext *pc) { + return ast_parse_bin_op_expr(pc, BinOpChainInf, ast_parse_bit_shift_op, ast_parse_addition_expr); +} + +// AdditionExpr <- MultiplyExpr (AdditionOp MultiplyExpr)* +static AstNode *ast_parse_addition_expr(ParseContext *pc) { + return ast_parse_bin_op_expr(pc, BinOpChainInf, ast_parse_addition_op, ast_parse_multiply_expr); +} + +// MultiplyExpr <- PrefixExpr (MultiplyOp PrefixExpr)* +static AstNode *ast_parse_multiply_expr(ParseContext *pc) { + return ast_parse_bin_op_expr(pc, BinOpChainInf, ast_parse_multiply_op, ast_parse_prefix_expr); +} + +// PrefixExpr <- PrefixOp* PrimaryExpr +static AstNode *ast_parse_prefix_expr(ParseContext *pc) { + return ast_parse_prefix_op_expr( + pc, + ast_parse_prefix_op, + ast_parse_primary_expr + ); +} + +// PrimaryExpr +// <- AsmExpr +// / IfExpr +// / KEYWORD_break BreakLabel? Expr? +// / KEYWORD_comptime Expr +// / KEYWORD_nosuspend Expr +// / KEYWORD_continue BreakLabel? +// / KEYWORD_resume Expr +// / KEYWORD_return Expr? +// / BlockLabel? LoopExpr +// / Block +// / CurlySuffixExpr +static AstNode *ast_parse_primary_expr(ParseContext *pc) { + AstNode *asm_expr = ast_parse_asm_expr(pc); + if (asm_expr != nullptr) + return asm_expr; + + AstNode *if_expr = ast_parse_if_expr(pc); + if (if_expr != nullptr) + return if_expr; + + Token *break_token = eat_token_if(pc, TokenIdKeywordBreak); + if (break_token != nullptr) { + Token *label = ast_parse_break_label(pc); + AstNode *expr = ast_parse_expr(pc); + + AstNode *res = ast_create_node(pc, NodeTypeBreak, break_token); + res->data.break_expr.name = token_buf(label); + res->data.break_expr.expr = expr; + return res; + } + + Token *comptime = eat_token_if(pc, TokenIdKeywordCompTime); + if (comptime != nullptr) { + AstNode *expr = ast_expect(pc, ast_parse_expr); + AstNode *res = ast_create_node(pc, NodeTypeCompTime, comptime); + res->data.comptime_expr.expr = expr; + return res; + } + + Token *nosuspend = eat_token_if(pc, TokenIdKeywordNoSuspend); + if (nosuspend != nullptr) { + AstNode *expr = ast_expect(pc, ast_parse_expr); + AstNode *res = ast_create_node(pc, NodeTypeNoSuspend, nosuspend); + res->data.nosuspend_expr.expr = expr; + return res; + } + + Token *continue_token = eat_token_if(pc, TokenIdKeywordContinue); + if (continue_token != nullptr) { + Token *label = ast_parse_break_label(pc); + AstNode *res = ast_create_node(pc, NodeTypeContinue, continue_token); + res->data.continue_expr.name = token_buf(label); + return res; + } + + Token *resume = eat_token_if(pc, TokenIdKeywordResume); + if (resume != nullptr) { + AstNode *expr = ast_expect(pc, ast_parse_expr); + AstNode *res = ast_create_node(pc, NodeTypeResume, resume); + res->data.resume_expr.expr = expr; + return res; + } + + Token *return_token = eat_token_if(pc, TokenIdKeywordReturn); + if (return_token != nullptr) { + AstNode *expr = ast_parse_expr(pc); + AstNode *res = ast_create_node(pc, NodeTypeReturnExpr, return_token); + res->data.return_expr.expr = expr; + return res; + } + + Token *label = ast_parse_block_label(pc); + AstNode *loop = ast_parse_loop_expr(pc); + if (loop != nullptr) { + switch (loop->type) { + case NodeTypeForExpr: + loop->data.for_expr.name = token_buf(label); + break; + case NodeTypeWhileExpr: + loop->data.while_expr.name = token_buf(label); + break; + default: + zig_unreachable(); + } + return loop; + } else if (label != nullptr) { + // Restore the tokens that we eaten by ast_parse_block_label. + put_back_token(pc); + put_back_token(pc); + } + + AstNode *block = ast_parse_block(pc); + if (block != nullptr) + return block; + + AstNode *curly_suffix = ast_parse_curly_suffix_expr(pc); + if (curly_suffix != nullptr) + return curly_suffix; + + return nullptr; +} + +// IfExpr <- IfPrefix Expr (KEYWORD_else Payload? Expr)? +static AstNode *ast_parse_if_expr(ParseContext *pc) { + return ast_parse_if_expr_helper(pc, ast_parse_expr); +} + +// Block <- LBRACE Statement* RBRACE +static AstNode *ast_parse_block(ParseContext *pc) { + Token *lbrace = eat_token_if(pc, TokenIdLBrace); + if (lbrace == nullptr) + return nullptr; + + ZigList statements = {}; + AstNode *statement; + while ((statement = ast_parse_statement(pc)) != nullptr) + statements.append(statement); + + expect_token(pc, TokenIdRBrace); + + AstNode *res = ast_create_node(pc, NodeTypeBlock, lbrace); + res->data.block.statements = statements; + return res; +} + +// LoopExpr <- KEYWORD_inline? (ForExpr / WhileExpr) +static AstNode *ast_parse_loop_expr(ParseContext *pc) { + return ast_parse_loop_expr_helper( + pc, + ast_parse_for_expr, + ast_parse_while_expr + ); +} + +// ForExpr <- ForPrefix Expr (KEYWORD_else Expr)? +static AstNode *ast_parse_for_expr(ParseContext *pc) { + return ast_parse_for_expr_helper(pc, ast_parse_expr); +} + +// WhileExpr <- WhilePrefix Expr (KEYWORD_else Payload? Expr)? +static AstNode *ast_parse_while_expr(ParseContext *pc) { + return ast_parse_while_expr_helper(pc, ast_parse_expr); +} + +// CurlySuffixExpr <- TypeExpr InitList? +static AstNode *ast_parse_curly_suffix_expr(ParseContext *pc) { + AstNode *type_expr = ast_parse_type_expr(pc); + if (type_expr == nullptr) + return nullptr; + + AstNode *res = ast_parse_init_list(pc); + if (res == nullptr) + return type_expr; + + assert(res->type == NodeTypeContainerInitExpr); + res->data.container_init_expr.type = type_expr; + return res; +} + +// InitList +// <- LBRACE FieldInit (COMMA FieldInit)* COMMA? RBRACE +// / LBRACE Expr (COMMA Expr)* COMMA? RBRACE +// / LBRACE RBRACE +static AstNode *ast_parse_init_list(ParseContext *pc) { + Token *lbrace = eat_token_if(pc, TokenIdLBrace); + if (lbrace == nullptr) + return nullptr; + + AstNode *first = ast_parse_field_init(pc); + if (first != nullptr) { + AstNode *res = ast_create_node(pc, NodeTypeContainerInitExpr, lbrace); + res->data.container_init_expr.kind = ContainerInitKindStruct; + res->data.container_init_expr.entries.append(first); + + while (eat_token_if(pc, TokenIdComma) != nullptr) { + AstNode *field_init = ast_parse_field_init(pc); + if (field_init == nullptr) + break; + res->data.container_init_expr.entries.append(field_init); + } + + expect_token(pc, TokenIdRBrace); + return res; + } + + AstNode *res = ast_create_node(pc, NodeTypeContainerInitExpr, lbrace); + res->data.container_init_expr.kind = ContainerInitKindArray; + + first = ast_parse_expr(pc); + if (first != nullptr) { + res->data.container_init_expr.entries.append(first); + + while (eat_token_if(pc, TokenIdComma) != nullptr) { + AstNode *expr = ast_parse_expr(pc); + if (expr == nullptr) + break; + res->data.container_init_expr.entries.append(expr); + } + + expect_token(pc, TokenIdRBrace); + return res; + } + + expect_token(pc, TokenIdRBrace); + return res; +} + +// TypeExpr <- PrefixTypeOp* ErrorUnionExpr +static AstNode *ast_parse_type_expr(ParseContext *pc) { + return ast_parse_prefix_op_expr( + pc, + ast_parse_prefix_type_op, + ast_parse_error_union_expr + ); +} + +// ErrorUnionExpr <- SuffixExpr (EXCLAMATIONMARK TypeExpr)? +static AstNode *ast_parse_error_union_expr(ParseContext *pc) { + AstNode *res = ast_parse_suffix_expr(pc); + if (res == nullptr) + return nullptr; + + AstNode *op = ast_parse_bin_op_simple(pc); + if (op == nullptr) + return res; + + AstNode *right = ast_expect(pc, ast_parse_type_expr); + assert(op->type == NodeTypeBinOpExpr); + op->data.bin_op_expr.op1 = res; + op->data.bin_op_expr.op2 = right; + return op; +} + +// SuffixExpr +// <- KEYWORD_async PrimaryTypeExpr SuffixOp* FnCallArguments +// / PrimaryTypeExpr (SuffixOp / FnCallArguments)* +static AstNode *ast_parse_suffix_expr(ParseContext *pc) { + Token *async_token = eat_token_if(pc, TokenIdKeywordAsync); + if (async_token) { + AstNode *child = ast_expect(pc, ast_parse_primary_type_expr); + while (true) { + AstNode *suffix = ast_parse_suffix_op(pc); + if (suffix == nullptr) + break; + + switch (suffix->type) { + case NodeTypeSliceExpr: + suffix->data.slice_expr.array_ref_expr = child; + break; + case NodeTypeArrayAccessExpr: + suffix->data.array_access_expr.array_ref_expr = child; + break; + case NodeTypeFieldAccessExpr: + suffix->data.field_access_expr.struct_expr = child; + break; + case NodeTypeUnwrapOptional: + suffix->data.unwrap_optional.expr = child; + break; + case NodeTypePtrDeref: + suffix->data.ptr_deref_expr.target = child; + break; + default: + zig_unreachable(); + } + child = suffix; + } + + // TODO: Both *_async_prefix and *_fn_call_arguments returns an + // AstNode *. All we really want here is the arguments of + // the call we parse. We therefor "leak" the node for now. + // Wait till we get async rework to fix this. + AstNode *args = ast_parse_fn_call_arguments(pc); + if (args == nullptr) + ast_invalid_token_error(pc, peek_token(pc)); + + assert(args->type == NodeTypeFnCallExpr); + + AstNode *res = ast_create_node(pc, NodeTypeFnCallExpr, async_token); + res->data.fn_call_expr.modifier = CallModifierAsync; + res->data.fn_call_expr.seen = false; + res->data.fn_call_expr.fn_ref_expr = child; + res->data.fn_call_expr.params = args->data.fn_call_expr.params; + return res; + } + + AstNode *res = ast_parse_primary_type_expr(pc); + if (res == nullptr) + return nullptr; + + while (true) { + AstNode *suffix = ast_parse_suffix_op(pc); + if (suffix != nullptr) { + switch (suffix->type) { + case NodeTypeSliceExpr: + suffix->data.slice_expr.array_ref_expr = res; + break; + case NodeTypeArrayAccessExpr: + suffix->data.array_access_expr.array_ref_expr = res; + break; + case NodeTypeFieldAccessExpr: + suffix->data.field_access_expr.struct_expr = res; + break; + case NodeTypeUnwrapOptional: + suffix->data.unwrap_optional.expr = res; + break; + case NodeTypePtrDeref: + suffix->data.ptr_deref_expr.target = res; + break; + default: + zig_unreachable(); + } + res = suffix; + continue; + } + + AstNode * call = ast_parse_fn_call_arguments(pc); + if (call != nullptr) { + assert(call->type == NodeTypeFnCallExpr); + call->data.fn_call_expr.fn_ref_expr = res; + res = call; + continue; + } + + break; + } + + return res; + +} + +// PrimaryTypeExpr +// <- BUILTINIDENTIFIER FnCallArguments +// / CHAR_LITERAL +// / ContainerDecl +// / DOT IDENTIFIER +// / ErrorSetDecl +// / FLOAT +// / FnProto +// / GroupedExpr +// / LabeledTypeExpr +// / IDENTIFIER +// / IfTypeExpr +// / INTEGER +// / KEYWORD_comptime TypeExpr +// / KEYWORD_error DOT IDENTIFIER +// / KEYWORD_false +// / KEYWORD_null +// / KEYWORD_promise +// / KEYWORD_true +// / KEYWORD_undefined +// / KEYWORD_unreachable +// / STRINGLITERAL +// / SwitchExpr +static AstNode *ast_parse_primary_type_expr(ParseContext *pc) { + // TODO: This is not in line with the grammar. + // Because the prev stage 1 tokenizer does not parse + // @[a-zA-Z_][a-zA-Z0-9_] as one token, it has to do a + // hack, where it accepts '@' (IDENTIFIER / KEYWORD_export). + // I'd say that it's better if '@' is part of the builtin + // identifier token. + Token *at_sign = eat_token_if(pc, TokenIdAtSign); + if (at_sign != nullptr) { + Buf *name; + Token *token = eat_token_if(pc, TokenIdKeywordExport); + if (token == nullptr) { + token = expect_token(pc, TokenIdSymbol); + name = token_buf(token); + } else { + name = buf_create_from_str("export"); + } + + AstNode *res = ast_expect(pc, ast_parse_fn_call_arguments); + AstNode *name_sym = ast_create_node(pc, NodeTypeSymbol, token); + name_sym->data.symbol_expr.symbol = name; + + assert(res->type == NodeTypeFnCallExpr); + res->line = at_sign->start_line; + res->column = at_sign->start_column; + res->data.fn_call_expr.fn_ref_expr = name_sym; + res->data.fn_call_expr.modifier = CallModifierBuiltin; + return res; + } + + Token *char_lit = eat_token_if(pc, TokenIdCharLiteral); + if (char_lit != nullptr) { + AstNode *res = ast_create_node(pc, NodeTypeCharLiteral, char_lit); + res->data.char_literal.value = char_lit->data.char_lit.c; + return res; + } + + AstNode *container_decl = ast_parse_container_decl(pc); + if (container_decl != nullptr) + return container_decl; + + AstNode *anon_lit = ast_parse_anon_lit(pc); + if (anon_lit != nullptr) + return anon_lit; + + AstNode *error_set_decl = ast_parse_error_set_decl(pc); + if (error_set_decl != nullptr) + return error_set_decl; + + Token *float_lit = eat_token_if(pc, TokenIdFloatLiteral); + if (float_lit != nullptr) { + AstNode *res = ast_create_node(pc, NodeTypeFloatLiteral, float_lit); + res->data.float_literal.bigfloat = &float_lit->data.float_lit.bigfloat; + res->data.float_literal.overflow = float_lit->data.float_lit.overflow; + return res; + } + + AstNode *fn_proto = ast_parse_fn_proto(pc); + if (fn_proto != nullptr) + return fn_proto; + + AstNode *grouped_expr = ast_parse_grouped_expr(pc); + if (grouped_expr != nullptr) + return grouped_expr; + + AstNode *labeled_type_expr = ast_parse_labeled_type_expr(pc); + if (labeled_type_expr != nullptr) + return labeled_type_expr; + + Token *identifier = eat_token_if(pc, TokenIdSymbol); + if (identifier != nullptr) + return token_symbol(pc, identifier); + + AstNode *if_type_expr = ast_parse_if_type_expr(pc); + if (if_type_expr != nullptr) + return if_type_expr; + + Token *int_lit = eat_token_if(pc, TokenIdIntLiteral); + if (int_lit != nullptr) { + AstNode *res = ast_create_node(pc, NodeTypeIntLiteral, int_lit); + res->data.int_literal.bigint = &int_lit->data.int_lit.bigint; + return res; + } + + Token *comptime = eat_token_if(pc, TokenIdKeywordCompTime); + if (comptime != nullptr) { + AstNode *expr = ast_expect(pc, ast_parse_type_expr); + AstNode *res = ast_create_node(pc, NodeTypeCompTime, comptime); + res->data.comptime_expr.expr = expr; + return res; + } + + Token *error = eat_token_if(pc, TokenIdKeywordError); + if (error != nullptr) { + Token *dot = expect_token(pc, TokenIdDot); + Token *name = expect_token(pc, TokenIdSymbol); + AstNode *left = ast_create_node(pc, NodeTypeErrorType, error); + AstNode *res = ast_create_node(pc, NodeTypeFieldAccessExpr, dot); + res->data.field_access_expr.struct_expr = left; + res->data.field_access_expr.field_name = token_buf(name); + return res; + } + + Token *false_token = eat_token_if(pc, TokenIdKeywordFalse); + if (false_token != nullptr) { + AstNode *res = ast_create_node(pc, NodeTypeBoolLiteral, false_token); + res->data.bool_literal.value = false; + return res; + } + + Token *null = eat_token_if(pc, TokenIdKeywordNull); + if (null != nullptr) + return ast_create_node(pc, NodeTypeNullLiteral, null); + + Token *anyframe = eat_token_if(pc, TokenIdKeywordAnyFrame); + if (anyframe != nullptr) + return ast_create_node(pc, NodeTypeAnyFrameType, anyframe); + + Token *true_token = eat_token_if(pc, TokenIdKeywordTrue); + if (true_token != nullptr) { + AstNode *res = ast_create_node(pc, NodeTypeBoolLiteral, true_token); + res->data.bool_literal.value = true; + return res; + } + + Token *undefined = eat_token_if(pc, TokenIdKeywordUndefined); + if (undefined != nullptr) + return ast_create_node(pc, NodeTypeUndefinedLiteral, undefined); + + Token *unreachable = eat_token_if(pc, TokenIdKeywordUnreachable); + if (unreachable != nullptr) + return ast_create_node(pc, NodeTypeUnreachable, unreachable); + + Token *string_lit = eat_token_if(pc, TokenIdStringLiteral); + if (string_lit == nullptr) + string_lit = eat_token_if(pc, TokenIdMultilineStringLiteral); + if (string_lit != nullptr) { + AstNode *res = ast_create_node(pc, NodeTypeStringLiteral, string_lit); + res->data.string_literal.buf = token_buf(string_lit); + return res; + } + + AstNode *switch_expr = ast_parse_switch_expr(pc); + if (switch_expr != nullptr) + return switch_expr; + + return nullptr; +} + +// ContainerDecl <- (KEYWORD_extern / KEYWORD_packed)? ContainerDeclAuto +static AstNode *ast_parse_container_decl(ParseContext *pc) { + Token *layout_token = eat_token_if(pc, TokenIdKeywordExtern); + if (layout_token == nullptr) + layout_token = eat_token_if(pc, TokenIdKeywordPacked); + + AstNode *res = ast_parse_container_decl_auto(pc); + if (res == nullptr) { + if (layout_token != nullptr) + put_back_token(pc); + return nullptr; + } + + assert(res->type == NodeTypeContainerDecl); + if (layout_token != nullptr) { + res->line = layout_token->start_line; + res->column = layout_token->start_column; + res->data.container_decl.layout = layout_token->id == TokenIdKeywordExtern + ? ContainerLayoutExtern + : ContainerLayoutPacked; + } + return res; +} + +// ErrorSetDecl <- KEYWORD_error LBRACE IdentifierList RBRACE +static AstNode *ast_parse_error_set_decl(ParseContext *pc) { + Token *first = eat_token_if(pc, TokenIdKeywordError); + if (first == nullptr) + return nullptr; + if (eat_token_if(pc, TokenIdLBrace) == nullptr) { + put_back_token(pc); + return nullptr; + } + + ZigList decls = ast_parse_list(pc, TokenIdComma, [](ParseContext *context) { + Buf doc_comment_buf = BUF_INIT; + Token *doc_token = ast_parse_doc_comments(context, &doc_comment_buf); + Token *ident = eat_token_if(context, TokenIdSymbol); + if (ident == nullptr) + return (AstNode*)nullptr; + + AstNode *symbol_node = token_symbol(context, ident); + if (doc_token == nullptr) + return symbol_node; + + AstNode *field_node = ast_create_node(context, NodeTypeErrorSetField, doc_token); + field_node->data.err_set_field.field_name = symbol_node; + field_node->data.err_set_field.doc_comments = doc_comment_buf; + return field_node; + }); + expect_token(pc, TokenIdRBrace); + + AstNode *res = ast_create_node(pc, NodeTypeErrorSetDecl, first); + res->data.err_set_decl.decls = decls; + return res; +} + +// GroupedExpr <- LPAREN Expr RPAREN +static AstNode *ast_parse_grouped_expr(ParseContext *pc) { + Token *lparen = eat_token_if(pc, TokenIdLParen); + if (lparen == nullptr) + return nullptr; + + AstNode *expr = ast_expect(pc, ast_parse_expr); + expect_token(pc, TokenIdRParen); + + AstNode *res = ast_create_node(pc, NodeTypeGroupedExpr, lparen); + res->data.grouped_expr = expr; + return res; +} + +// IfTypeExpr <- IfPrefix TypeExpr (KEYWORD_else Payload? TypeExpr)? +static AstNode *ast_parse_if_type_expr(ParseContext *pc) { + return ast_parse_if_expr_helper(pc, ast_parse_type_expr); +} + +// LabeledTypeExpr +// <- BlockLabel Block +// / BlockLabel? LoopTypeExpr +static AstNode *ast_parse_labeled_type_expr(ParseContext *pc) { + Token *label = ast_parse_block_label(pc); + if (label != nullptr) { + AstNode *block = ast_parse_block(pc); + if (block != nullptr) { + assert(block->type == NodeTypeBlock); + block->data.block.name = token_buf(label); + return block; + } + } + + AstNode *loop = ast_parse_loop_type_expr(pc); + if (loop != nullptr) { + switch (loop->type) { + case NodeTypeForExpr: + loop->data.for_expr.name = token_buf(label); + break; + case NodeTypeWhileExpr: + loop->data.while_expr.name = token_buf(label); + break; + default: + zig_unreachable(); + } + return loop; + } + + if (label != nullptr) { + put_back_token(pc); + put_back_token(pc); + } + return nullptr; +} + +// LoopTypeExpr <- KEYWORD_inline? (ForTypeExpr / WhileTypeExpr) +static AstNode *ast_parse_loop_type_expr(ParseContext *pc) { + return ast_parse_loop_expr_helper( + pc, + ast_parse_for_type_expr, + ast_parse_while_type_expr + ); +} + +// ForTypeExpr <- ForPrefix TypeExpr (KEYWORD_else TypeExpr)? +static AstNode *ast_parse_for_type_expr(ParseContext *pc) { + return ast_parse_for_expr_helper(pc, ast_parse_type_expr); +} + +// WhileTypeExpr <- WhilePrefix TypeExpr (KEYWORD_else Payload? TypeExpr)? +static AstNode *ast_parse_while_type_expr(ParseContext *pc) { + return ast_parse_while_expr_helper(pc, ast_parse_type_expr); +} + +// SwitchExpr <- KEYWORD_switch LPAREN Expr RPAREN LBRACE SwitchProngList RBRACE +static AstNode *ast_parse_switch_expr(ParseContext *pc) { + Token *switch_token = eat_token_if(pc, TokenIdKeywordSwitch); + if (switch_token == nullptr) + return nullptr; + + expect_token(pc, TokenIdLParen); + AstNode *expr = ast_expect(pc, ast_parse_expr); + expect_token(pc, TokenIdRParen); + expect_token(pc, TokenIdLBrace); + ZigList prongs = ast_parse_list(pc, TokenIdComma, ast_parse_switch_prong); + expect_token(pc, TokenIdRBrace); + + AstNode *res = ast_create_node(pc, NodeTypeSwitchExpr, switch_token); + res->data.switch_expr.expr = expr; + res->data.switch_expr.prongs = prongs; + return res; +} + +// AsmExpr <- KEYWORD_asm KEYWORD_volatile? LPAREN STRINGLITERAL AsmOutput? RPAREN +static AstNode *ast_parse_asm_expr(ParseContext *pc) { + Token *asm_token = eat_token_if(pc, TokenIdKeywordAsm); + if (asm_token == nullptr) + return nullptr; + + Token *volatile_token = eat_token_if(pc, TokenIdKeywordVolatile); + expect_token(pc, TokenIdLParen); + AstNode *asm_template = ast_expect(pc, ast_parse_expr); + AstNode *res = ast_parse_asm_output(pc); + if (res == nullptr) + res = ast_create_node_no_line_info(pc, NodeTypeAsmExpr); + expect_token(pc, TokenIdRParen); + + res->line = asm_token->start_line; + res->column = asm_token->start_column; + res->data.asm_expr.volatile_token = volatile_token; + res->data.asm_expr.asm_template = asm_template; + return res; +} + +static AstNode *ast_parse_anon_lit(ParseContext *pc) { + Token *period = eat_token_if(pc, TokenIdDot); + if (period == nullptr) + return nullptr; + + // anon enum literal + Token *identifier = eat_token_if(pc, TokenIdSymbol); + if (identifier != nullptr) { + AstNode *res = ast_create_node(pc, NodeTypeEnumLiteral, period); + res->data.enum_literal.period = period; + res->data.enum_literal.identifier = identifier; + return res; + } + + // anon container literal + AstNode *res = ast_parse_init_list(pc); + if (res != nullptr) + return res; + put_back_token(pc); + return nullptr; +} + +// AsmOutput <- COLON AsmOutputList AsmInput? +static AstNode *ast_parse_asm_output(ParseContext *pc) { + if (eat_token_if(pc, TokenIdColon) == nullptr) + return nullptr; + + ZigList output_list = ast_parse_list(pc, TokenIdComma, ast_parse_asm_output_item); + AstNode *res = ast_parse_asm_input(pc); + if (res == nullptr) + res = ast_create_node_no_line_info(pc, NodeTypeAsmExpr); + + res->data.asm_expr.output_list = output_list; + return res; +} + +// AsmOutputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN (MINUSRARROW TypeExpr / IDENTIFIER) RPAREN +static AsmOutput *ast_parse_asm_output_item(ParseContext *pc) { + if (eat_token_if(pc, TokenIdLBracket) == nullptr) + return nullptr; + + Token *sym_name = expect_token(pc, TokenIdSymbol); + expect_token(pc, TokenIdRBracket); + + Token *str = eat_token_if(pc, TokenIdMultilineStringLiteral); + if (str == nullptr) + str = expect_token(pc, TokenIdStringLiteral); + expect_token(pc, TokenIdLParen); + + Token *var_name = eat_token_if(pc, TokenIdSymbol); + AstNode *return_type = nullptr; + if (var_name == nullptr) { + expect_token(pc, TokenIdArrow); + return_type = ast_expect(pc, ast_parse_type_expr); + } + + expect_token(pc, TokenIdRParen); + + AsmOutput *res = heap::c_allocator.create(); + res->asm_symbolic_name = token_buf(sym_name); + res->constraint = token_buf(str); + res->variable_name = token_buf(var_name); + res->return_type = return_type; + return res; +} + +// AsmInput <- COLON AsmInputList AsmClobbers? +static AstNode *ast_parse_asm_input(ParseContext *pc) { + if (eat_token_if(pc, TokenIdColon) == nullptr) + return nullptr; + + ZigList input_list = ast_parse_list(pc, TokenIdComma, ast_parse_asm_input_item); + AstNode *res = ast_parse_asm_clobbers(pc); + if (res == nullptr) + res = ast_create_node_no_line_info(pc, NodeTypeAsmExpr); + + res->data.asm_expr.input_list = input_list; + return res; +} + +// AsmInputItem <- LBRACKET IDENTIFIER RBRACKET STRINGLITERAL LPAREN Expr RPAREN +static AsmInput *ast_parse_asm_input_item(ParseContext *pc) { + if (eat_token_if(pc, TokenIdLBracket) == nullptr) + return nullptr; + + Token *sym_name = expect_token(pc, TokenIdSymbol); + expect_token(pc, TokenIdRBracket); + + Token *constraint = eat_token_if(pc, TokenIdMultilineStringLiteral); + if (constraint == nullptr) + constraint = expect_token(pc, TokenIdStringLiteral); + expect_token(pc, TokenIdLParen); + AstNode *expr = ast_expect(pc, ast_parse_expr); + expect_token(pc, TokenIdRParen); + + AsmInput *res = heap::c_allocator.create(); + res->asm_symbolic_name = token_buf(sym_name); + res->constraint = token_buf(constraint); + res->expr = expr; + return res; +} + +// AsmClobbers <- COLON StringList +static AstNode *ast_parse_asm_clobbers(ParseContext *pc) { + if (eat_token_if(pc, TokenIdColon) == nullptr) + return nullptr; + + ZigList clobber_list = ast_parse_list(pc, TokenIdComma, [](ParseContext *context) { + Token *str = eat_token_if(context, TokenIdStringLiteral); + if (str == nullptr) + str = eat_token_if(context, TokenIdMultilineStringLiteral); + if (str != nullptr) + return token_buf(str); + return (Buf*)nullptr; + }); + + AstNode *res = ast_create_node_no_line_info(pc, NodeTypeAsmExpr); + res->data.asm_expr.clobber_list = clobber_list; + return res; +} + +// BreakLabel <- COLON IDENTIFIER +static Token *ast_parse_break_label(ParseContext *pc) { + if (eat_token_if(pc, TokenIdColon) == nullptr) + return nullptr; + + return expect_token(pc, TokenIdSymbol); +} + +// BlockLabel <- IDENTIFIER COLON +static Token *ast_parse_block_label(ParseContext *pc) { + Token *ident = eat_token_if(pc, TokenIdSymbol); + if (ident == nullptr) + return nullptr; + + // We do 2 token lookahead here, as we don't want to error when + // parsing identifiers. + if (eat_token_if(pc, TokenIdColon) == nullptr) { + put_back_token(pc); + return nullptr; + } + + return ident; +} + +// FieldInit <- DOT IDENTIFIER EQUAL Expr +static AstNode *ast_parse_field_init(ParseContext *pc) { + Token *first = eat_token_if(pc, TokenIdDot); + if (first == nullptr) + return nullptr; + + Token *name = eat_token_if(pc, TokenIdSymbol); + if (name == nullptr) { + // Because of anon literals ".{" is also valid. + put_back_token(pc); + return nullptr; + } + if (eat_token_if(pc, TokenIdEq) == nullptr) { + // Because ".Name" can also be intepreted as an enum literal, we should put back + // those two tokens again so that the parser can try to parse them as the enum + // literal later. + put_back_token(pc); + put_back_token(pc); + return nullptr; + } + AstNode *expr = ast_expect(pc, ast_parse_expr); + + AstNode *res = ast_create_node(pc, NodeTypeStructValueField, first); + res->data.struct_val_field.name = token_buf(name); + res->data.struct_val_field.expr = expr; + return res; +} + +// WhileContinueExpr <- COLON LPAREN AssignExpr RPAREN +static AstNode *ast_parse_while_continue_expr(ParseContext *pc) { + Token *first = eat_token_if(pc, TokenIdColon); + if (first == nullptr) + return nullptr; + + expect_token(pc, TokenIdLParen); + AstNode *expr = ast_expect(pc, ast_parse_assign_expr); + expect_token(pc, TokenIdRParen); + return expr; +} + +// LinkSection <- KEYWORD_linksection LPAREN Expr RPAREN +static AstNode *ast_parse_link_section(ParseContext *pc) { + Token *first = eat_token_if(pc, TokenIdKeywordLinkSection); + if (first == nullptr) + return nullptr; + + expect_token(pc, TokenIdLParen); + AstNode *res = ast_expect(pc, ast_parse_expr); + expect_token(pc, TokenIdRParen); + return res; +} + +// CallConv <- KEYWORD_callconv LPAREN Expr RPAREN +static AstNode *ast_parse_callconv(ParseContext *pc) { + Token *first = eat_token_if(pc, TokenIdKeywordCallconv); + if (first == nullptr) + return nullptr; + + expect_token(pc, TokenIdLParen); + AstNode *res = ast_expect(pc, ast_parse_expr); + expect_token(pc, TokenIdRParen); + return res; +} + +// ParamDecl <- (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType +static AstNode *ast_parse_param_decl(ParseContext *pc) { + Buf doc_comments = BUF_INIT; + ast_parse_doc_comments(pc, &doc_comments); + + Token *first = eat_token_if(pc, TokenIdKeywordNoAlias); + if (first == nullptr) + first = eat_token_if(pc, TokenIdKeywordCompTime); + + Token *name = eat_token_if(pc, TokenIdSymbol); + if (name != nullptr) { + if (eat_token_if(pc, TokenIdColon) != nullptr) { + if (first == nullptr) + first = name; + } else { + // We put back the ident, so it can be parsed as a ParamType + // later. + put_back_token(pc); + name = nullptr; + } + } + + AstNode *res; + if (first == nullptr) { + first = peek_token(pc); + res = ast_parse_param_type(pc); + } else { + res = ast_expect(pc, ast_parse_param_type); + } + + if (res == nullptr) + return nullptr; + + assert(res->type == NodeTypeParamDecl); + res->line = first->start_line; + res->column = first->start_column; + res->data.param_decl.name = token_buf(name); + res->data.param_decl.doc_comments = doc_comments; + res->data.param_decl.is_noalias = first->id == TokenIdKeywordNoAlias; + res->data.param_decl.is_comptime = first->id == TokenIdKeywordCompTime; + return res; +} + +// ParamType +// <- KEYWORD_anytype +// / DOT3 +// / TypeExpr +static AstNode *ast_parse_param_type(ParseContext *pc) { + Token *anytype_token = eat_token_if(pc, TokenIdKeywordAnyType); + if (anytype_token != nullptr) { + AstNode *res = ast_create_node(pc, NodeTypeParamDecl, anytype_token); + res->data.param_decl.anytype_token = anytype_token; + return res; + } + + Token *dots = eat_token_if(pc, TokenIdEllipsis3); + if (dots != nullptr) { + AstNode *res = ast_create_node(pc, NodeTypeParamDecl, dots); + res->data.param_decl.is_var_args = true; + return res; + } + + AstNode *type_expr = ast_parse_type_expr(pc); + if (type_expr != nullptr) { + AstNode *res = ast_create_node_copy_line_info(pc, NodeTypeParamDecl, type_expr); + res->data.param_decl.type = type_expr; + return res; + } + + return nullptr; +} + +// IfPrefix <- KEYWORD_if LPAREN Expr RPAREN PtrPayload? +static AstNode *ast_parse_if_prefix(ParseContext *pc) { + Token *first = eat_token_if(pc, TokenIdKeywordIf); + if (first == nullptr) + return nullptr; + + expect_token(pc, TokenIdLParen); + AstNode *condition = ast_expect(pc, ast_parse_expr); + expect_token(pc, TokenIdRParen); + Optional opt_payload = ast_parse_ptr_payload(pc); + + PtrPayload payload; + AstNode *res = ast_create_node(pc, NodeTypeIfOptional, first); + res->data.test_expr.target_node = condition; + if (opt_payload.unwrap(&payload)) { + res->data.test_expr.var_symbol = token_buf(payload.payload); + res->data.test_expr.var_is_ptr = payload.asterisk != nullptr; + } + return res; +} + +// WhilePrefix <- KEYWORD_while LPAREN Expr RPAREN PtrPayload? WhileContinueExpr? +static AstNode *ast_parse_while_prefix(ParseContext *pc) { + Token *while_token = eat_token_if(pc, TokenIdKeywordWhile); + if (while_token == nullptr) + return nullptr; + + expect_token(pc, TokenIdLParen); + AstNode *condition = ast_expect(pc, ast_parse_expr); + expect_token(pc, TokenIdRParen); + Optional opt_payload = ast_parse_ptr_payload(pc); + AstNode *continue_expr = ast_parse_while_continue_expr(pc); + + PtrPayload payload; + AstNode *res = ast_create_node(pc, NodeTypeWhileExpr, while_token); + res->data.while_expr.condition = condition; + res->data.while_expr.continue_expr = continue_expr; + if (opt_payload.unwrap(&payload)) { + res->data.while_expr.var_symbol = token_buf(payload.payload); + res->data.while_expr.var_is_ptr = payload.asterisk != nullptr; + } + + return res; +} + +// ForPrefix <- KEYWORD_for LPAREN Expr RPAREN PtrIndexPayload +static AstNode *ast_parse_for_prefix(ParseContext *pc) { + Token *for_token = eat_token_if(pc, TokenIdKeywordFor); + if (for_token == nullptr) + return nullptr; + + expect_token(pc, TokenIdLParen); + AstNode *array_expr = ast_expect(pc, ast_parse_expr); + expect_token(pc, TokenIdRParen); + PtrIndexPayload payload; + if (!ast_parse_ptr_index_payload(pc).unwrap(&payload)) + ast_invalid_token_error(pc, peek_token(pc)); + + AstNode *res = ast_create_node(pc, NodeTypeForExpr, for_token); + res->data.for_expr.array_expr = array_expr; + res->data.for_expr.elem_node = token_symbol(pc, payload.payload); + res->data.for_expr.elem_is_ptr = payload.asterisk != nullptr; + if (payload.index != nullptr) + res->data.for_expr.index_node = token_symbol(pc, payload.index); + + return res; +} + +// Payload <- PIPE IDENTIFIER PIPE +static Token *ast_parse_payload(ParseContext *pc) { + if (eat_token_if(pc, TokenIdBinOr) == nullptr) + return nullptr; + + Token *res = expect_token(pc, TokenIdSymbol); + expect_token(pc, TokenIdBinOr); + return res; +} + +// PtrPayload <- PIPE ASTERISK? IDENTIFIER PIPE +static Optional ast_parse_ptr_payload(ParseContext *pc) { + if (eat_token_if(pc, TokenIdBinOr) == nullptr) + return Optional::none(); + + Token *asterisk = eat_token_if(pc, TokenIdStar); + Token *payload = expect_token(pc, TokenIdSymbol); + expect_token(pc, TokenIdBinOr); + + PtrPayload res; + res.asterisk = asterisk; + res.payload = payload; + return Optional::some(res); +} + +// PtrIndexPayload <- PIPE ASTERISK? IDENTIFIER (COMMA IDENTIFIER)? PIPE +static Optional ast_parse_ptr_index_payload(ParseContext *pc) { + if (eat_token_if(pc, TokenIdBinOr) == nullptr) + return Optional::none(); + + Token *asterisk = eat_token_if(pc, TokenIdStar); + Token *payload = expect_token(pc, TokenIdSymbol); + Token *index = nullptr; + if (eat_token_if(pc, TokenIdComma) != nullptr) + index = expect_token(pc, TokenIdSymbol); + expect_token(pc, TokenIdBinOr); + + PtrIndexPayload res; + res.asterisk = asterisk; + res.payload = payload; + res.index = index; + return Optional::some(res); +} + +// SwitchProng <- SwitchCase EQUALRARROW PtrPayload? AssignExpr +static AstNode *ast_parse_switch_prong(ParseContext *pc) { + AstNode *res = ast_parse_switch_case(pc); + if (res == nullptr) + return nullptr; + + expect_token(pc, TokenIdFatArrow); + Optional opt_payload = ast_parse_ptr_payload(pc); + AstNode *expr = ast_expect(pc, ast_parse_assign_expr); + + PtrPayload payload; + assert(res->type == NodeTypeSwitchProng); + res->data.switch_prong.expr = expr; + if (opt_payload.unwrap(&payload)) { + res->data.switch_prong.var_symbol = token_symbol(pc, payload.payload); + res->data.switch_prong.var_is_ptr = payload.asterisk != nullptr; + } + + return res; +} + +// SwitchCase +// <- SwitchItem (COMMA SwitchItem)* COMMA? +// / KEYWORD_else +static AstNode *ast_parse_switch_case(ParseContext *pc) { + AstNode *first = ast_parse_switch_item(pc); + if (first != nullptr) { + AstNode *res = ast_create_node_copy_line_info(pc, NodeTypeSwitchProng, first); + res->data.switch_prong.items.append(first); + res->data.switch_prong.any_items_are_range = first->type == NodeTypeSwitchRange; + + while (eat_token_if(pc, TokenIdComma) != nullptr) { + AstNode *item = ast_parse_switch_item(pc); + if (item == nullptr) + break; + + res->data.switch_prong.items.append(item); + res->data.switch_prong.any_items_are_range |= item->type == NodeTypeSwitchRange; + } + + return res; + } + + Token *else_token = eat_token_if(pc, TokenIdKeywordElse); + if (else_token != nullptr) + return ast_create_node(pc, NodeTypeSwitchProng, else_token); + + return nullptr; +} + +// SwitchItem <- Expr (DOT3 Expr)? +static AstNode *ast_parse_switch_item(ParseContext *pc) { + AstNode *expr = ast_parse_expr(pc); + if (expr == nullptr) + return nullptr; + + Token *dots = eat_token_if(pc, TokenIdEllipsis3); + if (dots != nullptr) { + AstNode *expr2 = ast_expect(pc, ast_parse_expr); + AstNode *res = ast_create_node(pc, NodeTypeSwitchRange, dots); + res->data.switch_range.start = expr; + res->data.switch_range.end = expr2; + return res; + } + + return expr; +} + +// AssignOp +// <- ASTERISKEQUAL +// / SLASHEQUAL +// / PERCENTEQUAL +// / PLUSEQUAL +// / MINUSEQUAL +// / LARROW2EQUAL +// / RARROW2EQUAL +// / AMPERSANDEQUAL +// / CARETEQUAL +// / PIPEEQUAL +// / ASTERISKPERCENTEQUAL +// / PLUSPERCENTEQUAL +// / MINUSPERCENTEQUAL +// / EQUAL +static AstNode *ast_parse_assign_op(ParseContext *pc) { + // In C, we have `T arr[N] = {[i] = T{}};` but it doesn't + // seem to work in C++... + BinOpType table[TokenIdCount] = {}; + table[TokenIdBarBarEq] = BinOpTypeAssignMergeErrorSets; + table[TokenIdBitAndEq] = BinOpTypeAssignBitAnd; + table[TokenIdBitOrEq] = BinOpTypeAssignBitOr; + table[TokenIdBitShiftLeftEq] = BinOpTypeAssignBitShiftLeft; + table[TokenIdBitShiftRightEq] = BinOpTypeAssignBitShiftRight; + table[TokenIdBitXorEq] = BinOpTypeAssignBitXor; + table[TokenIdDivEq] = BinOpTypeAssignDiv; + table[TokenIdEq] = BinOpTypeAssign; + table[TokenIdMinusEq] = BinOpTypeAssignMinus; + table[TokenIdMinusPercentEq] = BinOpTypeAssignMinusWrap; + table[TokenIdModEq] = BinOpTypeAssignMod; + table[TokenIdPlusEq] = BinOpTypeAssignPlus; + table[TokenIdPlusPercentEq] = BinOpTypeAssignPlusWrap; + table[TokenIdTimesEq] = BinOpTypeAssignTimes; + table[TokenIdTimesPercentEq] = BinOpTypeAssignTimesWrap; + + BinOpType op = table[peek_token(pc)->id]; + if (op != BinOpTypeInvalid) { + Token *op_token = eat_token(pc); + AstNode *res = ast_create_node(pc, NodeTypeBinOpExpr, op_token); + res->data.bin_op_expr.bin_op = op; + return res; + } + + return nullptr; + +} + +// CompareOp +// <- EQUALEQUAL +// / EXCLAMATIONMARKEQUAL +// / LARROW +// / RARROW +// / LARROWEQUAL +// / RARROWEQUAL +static AstNode *ast_parse_compare_op(ParseContext *pc) { + BinOpType table[TokenIdCount] = {}; + table[TokenIdCmpEq] = BinOpTypeCmpEq; + table[TokenIdCmpNotEq] = BinOpTypeCmpNotEq; + table[TokenIdCmpLessThan] = BinOpTypeCmpLessThan; + table[TokenIdCmpGreaterThan] = BinOpTypeCmpGreaterThan; + table[TokenIdCmpLessOrEq] = BinOpTypeCmpLessOrEq; + table[TokenIdCmpGreaterOrEq] = BinOpTypeCmpGreaterOrEq; + + BinOpType op = table[peek_token(pc)->id]; + if (op != BinOpTypeInvalid) { + Token *op_token = eat_token(pc); + AstNode *res = ast_create_node(pc, NodeTypeBinOpExpr, op_token); + res->data.bin_op_expr.bin_op = op; + return res; + } + + return nullptr; +} + +// BitwiseOp +// <- AMPERSAND +// / CARET +// / PIPE +// / KEYWORD_orelse +// / KEYWORD_catch Payload? +static AstNode *ast_parse_bitwise_op(ParseContext *pc) { + BinOpType table[TokenIdCount] = {}; + table[TokenIdAmpersand] = BinOpTypeBinAnd; + table[TokenIdBinXor] = BinOpTypeBinXor; + table[TokenIdBinOr] = BinOpTypeBinOr; + table[TokenIdKeywordOrElse] = BinOpTypeUnwrapOptional; + + BinOpType op = table[peek_token(pc)->id]; + if (op != BinOpTypeInvalid) { + Token *op_token = eat_token(pc); + AstNode *res = ast_create_node(pc, NodeTypeBinOpExpr, op_token); + res->data.bin_op_expr.bin_op = op; + return res; + } + + Token *catch_token = eat_token_if(pc, TokenIdKeywordCatch); + if (catch_token != nullptr) { + Token *payload = ast_parse_payload(pc); + AstNode *res = ast_create_node(pc, NodeTypeCatchExpr, catch_token); + if (payload != nullptr) + res->data.unwrap_err_expr.symbol = token_symbol(pc, payload); + + return res; + } + + return nullptr; +} + +// BitShiftOp +// <- LARROW2 +// / RARROW2 +static AstNode *ast_parse_bit_shift_op(ParseContext *pc) { + BinOpType table[TokenIdCount] = {}; + table[TokenIdBitShiftLeft] = BinOpTypeBitShiftLeft; + table[TokenIdBitShiftRight] = BinOpTypeBitShiftRight; + + BinOpType op = table[peek_token(pc)->id]; + if (op != BinOpTypeInvalid) { + Token *op_token = eat_token(pc); + AstNode *res = ast_create_node(pc, NodeTypeBinOpExpr, op_token); + res->data.bin_op_expr.bin_op = op; + return res; + } + + return nullptr; +} + +// AdditionOp +// <- PLUS +// / MINUS +// / PLUS2 +// / PLUSPERCENT +// / MINUSPERCENT +static AstNode *ast_parse_addition_op(ParseContext *pc) { + BinOpType table[TokenIdCount] = {}; + table[TokenIdPlus] = BinOpTypeAdd; + table[TokenIdDash] = BinOpTypeSub; + table[TokenIdPlusPlus] = BinOpTypeArrayCat; + table[TokenIdPlusPercent] = BinOpTypeAddWrap; + table[TokenIdMinusPercent] = BinOpTypeSubWrap; + + BinOpType op = table[peek_token(pc)->id]; + if (op != BinOpTypeInvalid) { + Token *op_token = eat_token(pc); + AstNode *res = ast_create_node(pc, NodeTypeBinOpExpr, op_token); + res->data.bin_op_expr.bin_op = op; + return res; + } + + return nullptr; +} + +// MultiplyOp +// <- PIPE2 +// / ASTERISK +// / SLASH +// / PERCENT +// / ASTERISK2 +// / ASTERISKPERCENT +static AstNode *ast_parse_multiply_op(ParseContext *pc) { + BinOpType table[TokenIdCount] = {}; + table[TokenIdBarBar] = BinOpTypeMergeErrorSets; + table[TokenIdStar] = BinOpTypeMult; + table[TokenIdSlash] = BinOpTypeDiv; + table[TokenIdPercent] = BinOpTypeMod; + table[TokenIdStarStar] = BinOpTypeArrayMult; + table[TokenIdTimesPercent] = BinOpTypeMultWrap; + + BinOpType op = table[peek_token(pc)->id]; + if (op != BinOpTypeInvalid) { + Token *op_token = eat_token(pc); + AstNode *res = ast_create_node(pc, NodeTypeBinOpExpr, op_token); + res->data.bin_op_expr.bin_op = op; + return res; + } + + return nullptr; +} + +// PrefixOp +// <- EXCLAMATIONMARK +// / MINUS +// / TILDE +// / MINUSPERCENT +// / AMPERSAND +// / KEYWORD_try +// / KEYWORD_await +static AstNode *ast_parse_prefix_op(ParseContext *pc) { + PrefixOp table[TokenIdCount] = {}; + table[TokenIdBang] = PrefixOpBoolNot; + table[TokenIdDash] = PrefixOpNegation; + table[TokenIdTilde] = PrefixOpBinNot; + table[TokenIdMinusPercent] = PrefixOpNegationWrap; + table[TokenIdAmpersand] = PrefixOpAddrOf; + + PrefixOp op = table[peek_token(pc)->id]; + if (op != PrefixOpInvalid) { + Token *op_token = eat_token(pc); + AstNode *res = ast_create_node(pc, NodeTypePrefixOpExpr, op_token); + res->data.prefix_op_expr.prefix_op = op; + return res; + } + + Token *try_token = eat_token_if(pc, TokenIdKeywordTry); + if (try_token != nullptr) { + AstNode *res = ast_create_node(pc, NodeTypeReturnExpr, try_token); + res->data.return_expr.kind = ReturnKindError; + return res; + } + + Token *await = eat_token_if(pc, TokenIdKeywordAwait); + if (await != nullptr) { + AstNode *res = ast_create_node(pc, NodeTypeAwaitExpr, await); + return res; + } + + return nullptr; +} + +// PrefixTypeOp +// <- QUESTIONMARK +// / KEYWORD_anyframe MINUSRARROW +// / ArrayTypeStart (ByteAlign / KEYWORD_const / KEYWORD_volatile)* +// / PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile)* +static AstNode *ast_parse_prefix_type_op(ParseContext *pc) { + Token *questionmark = eat_token_if(pc, TokenIdQuestion); + if (questionmark != nullptr) { + AstNode *res = ast_create_node(pc, NodeTypePrefixOpExpr, questionmark); + res->data.prefix_op_expr.prefix_op = PrefixOpOptional; + return res; + } + + Token *anyframe = eat_token_if(pc, TokenIdKeywordAnyFrame); + if (anyframe != nullptr) { + if (eat_token_if(pc, TokenIdArrow) != nullptr) { + AstNode *res = ast_create_node(pc, NodeTypeAnyFrameType, anyframe); + return res; + } + + put_back_token(pc); + } + + Token *arr_init_lbracket = eat_token_if(pc, TokenIdLBracket); + if (arr_init_lbracket != nullptr) { + Token *underscore = eat_token_if(pc, TokenIdSymbol); + if (underscore == nullptr) { + put_back_token(pc); + } else if (!buf_eql_str(token_buf(underscore), "_")) { + put_back_token(pc); + put_back_token(pc); + } else { + AstNode *sentinel = nullptr; + Token *colon = eat_token_if(pc, TokenIdColon); + if (colon != nullptr) { + sentinel = ast_expect(pc, ast_parse_expr); + } + expect_token(pc, TokenIdRBracket); + AstNode *node = ast_create_node(pc, NodeTypeInferredArrayType, arr_init_lbracket); + node->data.inferred_array_type.sentinel = sentinel; + return node; + } + } + + + AstNode *ptr = ast_parse_ptr_type_start(pc); + if (ptr != nullptr) { + assert(ptr->type == NodeTypePointerType); + // We might get two pointers from *_ptr_type_start + AstNode *child = ptr->data.pointer_type.op_expr; + if (child == nullptr) + child = ptr; + while (true) { + Token *allowzero_token = eat_token_if(pc, TokenIdKeywordAllowZero); + if (allowzero_token != nullptr) { + child->data.pointer_type.allow_zero_token = allowzero_token; + continue; + } + + if (eat_token_if(pc, TokenIdKeywordAlign) != nullptr) { + expect_token(pc, TokenIdLParen); + AstNode *align_expr = ast_expect(pc, ast_parse_expr); + child->data.pointer_type.align_expr = align_expr; + if (eat_token_if(pc, TokenIdColon) != nullptr) { + Token *bit_offset_start = expect_token(pc, TokenIdIntLiteral); + expect_token(pc, TokenIdColon); + Token *host_int_bytes = expect_token(pc, TokenIdIntLiteral); + child->data.pointer_type.bit_offset_start = token_bigint(bit_offset_start); + child->data.pointer_type.host_int_bytes = token_bigint(host_int_bytes); + } + expect_token(pc, TokenIdRParen); + continue; + } + + if (eat_token_if(pc, TokenIdKeywordConst) != nullptr) { + child->data.pointer_type.is_const = true; + continue; + } + + if (eat_token_if(pc, TokenIdKeywordVolatile) != nullptr) { + child->data.pointer_type.is_volatile = true; + continue; + } + + break; + } + + return ptr; + } + + AstNode *array = ast_parse_array_type_start(pc); + if (array != nullptr) { + assert(array->type == NodeTypeArrayType); + while (true) { + Token *allowzero_token = eat_token_if(pc, TokenIdKeywordAllowZero); + if (allowzero_token != nullptr) { + array->data.array_type.allow_zero_token = allowzero_token; + continue; + } + + AstNode *align_expr = ast_parse_byte_align(pc); + if (align_expr != nullptr) { + array->data.array_type.align_expr = align_expr; + continue; + } + + if (eat_token_if(pc, TokenIdKeywordConst) != nullptr) { + array->data.array_type.is_const = true; + continue; + } + + if (eat_token_if(pc, TokenIdKeywordVolatile) != nullptr) { + array->data.array_type.is_volatile = true; + continue; + } + break; + } + + return array; + } + + + return nullptr; +} + +// SuffixOp +// <- LBRACKET Expr (DOT2 (Expr (COLON Expr)?)?)? RBRACKET +// / DOT IDENTIFIER +// / DOTASTERISK +// / DOTQUESTIONMARK +static AstNode *ast_parse_suffix_op(ParseContext *pc) { + Token *lbracket = eat_token_if(pc, TokenIdLBracket); + if (lbracket != nullptr) { + AstNode *start = ast_expect(pc, ast_parse_expr); + AstNode *end = nullptr; + if (eat_token_if(pc, TokenIdEllipsis2) != nullptr) { + AstNode *sentinel = nullptr; + end = ast_parse_expr(pc); + if (eat_token_if(pc, TokenIdColon) != nullptr) { + sentinel = ast_parse_expr(pc); + } + expect_token(pc, TokenIdRBracket); + + AstNode *res = ast_create_node(pc, NodeTypeSliceExpr, lbracket); + res->data.slice_expr.start = start; + res->data.slice_expr.end = end; + res->data.slice_expr.sentinel = sentinel; + return res; + } + + expect_token(pc, TokenIdRBracket); + + AstNode *res = ast_create_node(pc, NodeTypeArrayAccessExpr, lbracket); + res->data.array_access_expr.subscript = start; + return res; + } + + Token *dot_asterisk = eat_token_if(pc, TokenIdDotStar); + if (dot_asterisk != nullptr) + return ast_create_node(pc, NodeTypePtrDeref, dot_asterisk); + + Token *dot = eat_token_if(pc, TokenIdDot); + if (dot != nullptr) { + if (eat_token_if(pc, TokenIdQuestion) != nullptr) + return ast_create_node(pc, NodeTypeUnwrapOptional, dot); + + Token *ident = expect_token(pc, TokenIdSymbol); + AstNode *res = ast_create_node(pc, NodeTypeFieldAccessExpr, dot); + res->data.field_access_expr.field_name = token_buf(ident); + return res; + } + + return nullptr; +} + +// FnCallArguments <- LPAREN ExprList RPAREN +static AstNode *ast_parse_fn_call_arguments(ParseContext *pc) { + Token *paren = eat_token_if(pc, TokenIdLParen); + if (paren == nullptr) + return nullptr; + + ZigList params = ast_parse_list(pc, TokenIdComma, ast_parse_expr); + expect_token(pc, TokenIdRParen); + + AstNode *res = ast_create_node(pc, NodeTypeFnCallExpr, paren); + res->data.fn_call_expr.params = params; + res->data.fn_call_expr.seen = false; + return res; +} + +// ArrayTypeStart <- LBRACKET Expr? RBRACKET +static AstNode *ast_parse_array_type_start(ParseContext *pc) { + Token *lbracket = eat_token_if(pc, TokenIdLBracket); + if (lbracket == nullptr) + return nullptr; + + AstNode *size = ast_parse_expr(pc); + AstNode *sentinel = nullptr; + Token *colon = eat_token_if(pc, TokenIdColon); + if (colon != nullptr) { + sentinel = ast_expect(pc, ast_parse_expr); + } + expect_token(pc, TokenIdRBracket); + AstNode *res = ast_create_node(pc, NodeTypeArrayType, lbracket); + res->data.array_type.size = size; + res->data.array_type.sentinel = sentinel; + return res; +} + +// PtrTypeStart +// <- ASTERISK +// / ASTERISK2 +// / PTRUNKNOWN +// / PTRC +static AstNode *ast_parse_ptr_type_start(ParseContext *pc) { + AstNode *sentinel = nullptr; + + Token *asterisk = eat_token_if(pc, TokenIdStar); + if (asterisk != nullptr) { + Token *colon = eat_token_if(pc, TokenIdColon); + if (colon != nullptr) { + sentinel = ast_expect(pc, ast_parse_expr); + } + AstNode *res = ast_create_node(pc, NodeTypePointerType, asterisk); + res->data.pointer_type.star_token = asterisk; + res->data.pointer_type.sentinel = sentinel; + return res; + } + + Token *asterisk2 = eat_token_if(pc, TokenIdStarStar); + if (asterisk2 != nullptr) { + Token *colon = eat_token_if(pc, TokenIdColon); + if (colon != nullptr) { + sentinel = ast_expect(pc, ast_parse_expr); + } + AstNode *res = ast_create_node(pc, NodeTypePointerType, asterisk2); + AstNode *res2 = ast_create_node(pc, NodeTypePointerType, asterisk2); + res->data.pointer_type.star_token = asterisk2; + res2->data.pointer_type.star_token = asterisk2; + res2->data.pointer_type.sentinel = sentinel; + res->data.pointer_type.op_expr = res2; + return res; + } + + Token *lbracket = eat_token_if(pc, TokenIdLBracket); + if (lbracket != nullptr) { + Token *star = eat_token_if(pc, TokenIdStar); + if (star == nullptr) { + put_back_token(pc); + } else { + Token *c_tok = eat_token_if(pc, TokenIdSymbol); + if (c_tok != nullptr) { + if (!buf_eql_str(token_buf(c_tok), "c")) { + put_back_token(pc); // c symbol + } else { + expect_token(pc, TokenIdRBracket); + AstNode *res = ast_create_node(pc, NodeTypePointerType, lbracket); + res->data.pointer_type.star_token = c_tok; + return res; + } + } + + Token *colon = eat_token_if(pc, TokenIdColon); + if (colon != nullptr) { + sentinel = ast_expect(pc, ast_parse_expr); + } + expect_token(pc, TokenIdRBracket); + AstNode *res = ast_create_node(pc, NodeTypePointerType, lbracket); + res->data.pointer_type.star_token = lbracket; + res->data.pointer_type.sentinel = sentinel; + return res; + } + } + + return nullptr; +} + +// ContainerDeclAuto <- ContainerDeclType LBRACE ContainerMembers RBRACE +static AstNode *ast_parse_container_decl_auto(ParseContext *pc) { + AstNode *res = ast_parse_container_decl_type(pc); + if (res == nullptr) + return nullptr; + + expect_token(pc, TokenIdLBrace); + AstNodeContainerDecl members = ast_parse_container_members(pc); + expect_token(pc, TokenIdRBrace); + + res->data.container_decl.fields = members.fields; + res->data.container_decl.decls = members.decls; + if (buf_len(&members.doc_comments) != 0) { + res->data.container_decl.doc_comments = members.doc_comments; + } + return res; +} + +// ContainerDeclType +// <- KEYWORD_struct +// / KEYWORD_enum (LPAREN Expr RPAREN)? +// / KEYWORD_union (LPAREN (KEYWORD_enum (LPAREN Expr RPAREN)? / Expr) RPAREN)? +static AstNode *ast_parse_container_decl_type(ParseContext *pc) { + Token *first = eat_token_if(pc, TokenIdKeywordStruct); + if (first != nullptr) { + AstNode *res = ast_create_node(pc, NodeTypeContainerDecl, first); + res->data.container_decl.init_arg_expr = nullptr; + res->data.container_decl.kind = ContainerKindStruct; + return res; + } + + first = eat_token_if(pc, TokenIdKeywordEnum); + if (first != nullptr) { + AstNode *init_arg_expr = nullptr; + if (eat_token_if(pc, TokenIdLParen) != nullptr) { + init_arg_expr = ast_expect(pc, ast_parse_expr); + expect_token(pc, TokenIdRParen); + } + AstNode *res = ast_create_node(pc, NodeTypeContainerDecl, first); + res->data.container_decl.init_arg_expr = init_arg_expr; + res->data.container_decl.kind = ContainerKindEnum; + return res; + } + + first = eat_token_if(pc, TokenIdKeywordUnion); + if (first != nullptr) { + AstNode *init_arg_expr = nullptr; + bool auto_enum = false; + if (eat_token_if(pc, TokenIdLParen) != nullptr) { + if (eat_token_if(pc, TokenIdKeywordEnum) != nullptr) { + auto_enum = true; + if (eat_token_if(pc, TokenIdLParen) != nullptr) { + init_arg_expr = ast_expect(pc, ast_parse_expr); + expect_token(pc, TokenIdRParen); + } + } else { + init_arg_expr = ast_expect(pc, ast_parse_expr); + } + + expect_token(pc, TokenIdRParen); + } + + AstNode *res = ast_create_node(pc, NodeTypeContainerDecl, first); + res->data.container_decl.init_arg_expr = init_arg_expr; + res->data.container_decl.auto_enum = auto_enum; + res->data.container_decl.kind = ContainerKindUnion; + return res; + } + + return nullptr; +} + +// ByteAlign <- KEYWORD_align LPAREN Expr RPAREN +static AstNode *ast_parse_byte_align(ParseContext *pc) { + if (eat_token_if(pc, TokenIdKeywordAlign) == nullptr) + return nullptr; + + expect_token(pc, TokenIdLParen); + AstNode *res = ast_expect(pc, ast_parse_expr); + expect_token(pc, TokenIdRParen); + return res; +} + +static void visit_field(AstNode **node, void (*visit)(AstNode **, void *context), void *context) { + if (*node) { + visit(node, context); + } +} + +static void visit_node_list(ZigList *list, void (*visit)(AstNode **, void *context), void *context) { + if (list) { + for (size_t i = 0; i < list->length; i += 1) { + visit(&list->at(i), context); + } + } +} + +void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *context), void *context) { + switch (node->type) { + case NodeTypeFnProto: + visit_field(&node->data.fn_proto.return_type, visit, context); + visit_node_list(&node->data.fn_proto.params, visit, context); + visit_field(&node->data.fn_proto.align_expr, visit, context); + visit_field(&node->data.fn_proto.section_expr, visit, context); + break; + case NodeTypeFnDef: + visit_field(&node->data.fn_def.fn_proto, visit, context); + visit_field(&node->data.fn_def.body, visit, context); + break; + case NodeTypeParamDecl: + visit_field(&node->data.param_decl.type, visit, context); + break; + case NodeTypeBlock: + visit_node_list(&node->data.block.statements, visit, context); + break; + case NodeTypeGroupedExpr: + visit_field(&node->data.grouped_expr, visit, context); + break; + case NodeTypeReturnExpr: + visit_field(&node->data.return_expr.expr, visit, context); + break; + case NodeTypeDefer: + visit_field(&node->data.defer.expr, visit, context); + visit_field(&node->data.defer.err_payload, visit, context); + break; + case NodeTypeVariableDeclaration: + visit_field(&node->data.variable_declaration.type, visit, context); + visit_field(&node->data.variable_declaration.expr, visit, context); + visit_field(&node->data.variable_declaration.align_expr, visit, context); + visit_field(&node->data.variable_declaration.section_expr, visit, context); + break; + case NodeTypeTestDecl: + visit_field(&node->data.test_decl.body, visit, context); + break; + case NodeTypeBinOpExpr: + visit_field(&node->data.bin_op_expr.op1, visit, context); + visit_field(&node->data.bin_op_expr.op2, visit, context); + break; + case NodeTypeCatchExpr: + visit_field(&node->data.unwrap_err_expr.op1, visit, context); + visit_field(&node->data.unwrap_err_expr.symbol, visit, context); + visit_field(&node->data.unwrap_err_expr.op2, visit, context); + break; + case NodeTypeIntLiteral: + // none + break; + case NodeTypeFloatLiteral: + // none + break; + case NodeTypeStringLiteral: + // none + break; + case NodeTypeCharLiteral: + // none + break; + case NodeTypeSymbol: + // none + break; + case NodeTypePrefixOpExpr: + visit_field(&node->data.prefix_op_expr.primary_expr, visit, context); + break; + case NodeTypeFnCallExpr: + visit_field(&node->data.fn_call_expr.fn_ref_expr, visit, context); + visit_node_list(&node->data.fn_call_expr.params, visit, context); + break; + case NodeTypeArrayAccessExpr: + visit_field(&node->data.array_access_expr.array_ref_expr, visit, context); + visit_field(&node->data.array_access_expr.subscript, visit, context); + break; + case NodeTypeSliceExpr: + visit_field(&node->data.slice_expr.array_ref_expr, visit, context); + visit_field(&node->data.slice_expr.start, visit, context); + visit_field(&node->data.slice_expr.end, visit, context); + visit_field(&node->data.slice_expr.sentinel, visit, context); + break; + case NodeTypeFieldAccessExpr: + visit_field(&node->data.field_access_expr.struct_expr, visit, context); + break; + case NodeTypePtrDeref: + visit_field(&node->data.ptr_deref_expr.target, visit, context); + break; + case NodeTypeUnwrapOptional: + visit_field(&node->data.unwrap_optional.expr, visit, context); + break; + case NodeTypeUsingNamespace: + visit_field(&node->data.using_namespace.expr, visit, context); + break; + case NodeTypeBoolLiteral: + // none + break; + case NodeTypeNullLiteral: + // none + break; + case NodeTypeUndefinedLiteral: + // none + break; + case NodeTypeIfBoolExpr: + visit_field(&node->data.if_bool_expr.condition, visit, context); + visit_field(&node->data.if_bool_expr.then_block, visit, context); + visit_field(&node->data.if_bool_expr.else_node, visit, context); + break; + case NodeTypeIfErrorExpr: + visit_field(&node->data.if_err_expr.target_node, visit, context); + visit_field(&node->data.if_err_expr.then_node, visit, context); + visit_field(&node->data.if_err_expr.else_node, visit, context); + break; + case NodeTypeIfOptional: + visit_field(&node->data.test_expr.target_node, visit, context); + visit_field(&node->data.test_expr.then_node, visit, context); + visit_field(&node->data.test_expr.else_node, visit, context); + break; + case NodeTypeWhileExpr: + visit_field(&node->data.while_expr.condition, visit, context); + visit_field(&node->data.while_expr.body, visit, context); + break; + case NodeTypeForExpr: + visit_field(&node->data.for_expr.elem_node, visit, context); + visit_field(&node->data.for_expr.array_expr, visit, context); + visit_field(&node->data.for_expr.index_node, visit, context); + visit_field(&node->data.for_expr.body, visit, context); + break; + case NodeTypeSwitchExpr: + visit_field(&node->data.switch_expr.expr, visit, context); + visit_node_list(&node->data.switch_expr.prongs, visit, context); + break; + case NodeTypeSwitchProng: + visit_node_list(&node->data.switch_prong.items, visit, context); + visit_field(&node->data.switch_prong.var_symbol, visit, context); + visit_field(&node->data.switch_prong.expr, visit, context); + break; + case NodeTypeSwitchRange: + visit_field(&node->data.switch_range.start, visit, context); + visit_field(&node->data.switch_range.end, visit, context); + break; + case NodeTypeCompTime: + visit_field(&node->data.comptime_expr.expr, visit, context); + break; + case NodeTypeNoSuspend: + visit_field(&node->data.comptime_expr.expr, visit, context); + break; + case NodeTypeBreak: + // none + break; + case NodeTypeContinue: + // none + break; + case NodeTypeUnreachable: + // none + break; + case NodeTypeAsmExpr: + for (size_t i = 0; i < node->data.asm_expr.input_list.length; i += 1) { + AsmInput *asm_input = node->data.asm_expr.input_list.at(i); + visit_field(&asm_input->expr, visit, context); + } + for (size_t i = 0; i < node->data.asm_expr.output_list.length; i += 1) { + AsmOutput *asm_output = node->data.asm_expr.output_list.at(i); + visit_field(&asm_output->return_type, visit, context); + } + break; + case NodeTypeContainerDecl: + visit_node_list(&node->data.container_decl.fields, visit, context); + visit_node_list(&node->data.container_decl.decls, visit, context); + visit_field(&node->data.container_decl.init_arg_expr, visit, context); + break; + case NodeTypeStructField: + visit_field(&node->data.struct_field.type, visit, context); + visit_field(&node->data.struct_field.value, visit, context); + break; + case NodeTypeContainerInitExpr: + visit_field(&node->data.container_init_expr.type, visit, context); + visit_node_list(&node->data.container_init_expr.entries, visit, context); + break; + case NodeTypeStructValueField: + visit_field(&node->data.struct_val_field.expr, visit, context); + break; + case NodeTypeArrayType: + visit_field(&node->data.array_type.size, visit, context); + visit_field(&node->data.array_type.sentinel, visit, context); + visit_field(&node->data.array_type.child_type, visit, context); + visit_field(&node->data.array_type.align_expr, visit, context); + break; + case NodeTypeInferredArrayType: + visit_field(&node->data.array_type.sentinel, visit, context); + visit_field(&node->data.array_type.child_type, visit, context); + break; + case NodeTypeAnyFrameType: + visit_field(&node->data.anyframe_type.payload_type, visit, context); + break; + case NodeTypeErrorType: + // none + break; + case NodeTypePointerType: + visit_field(&node->data.pointer_type.sentinel, visit, context); + visit_field(&node->data.pointer_type.align_expr, visit, context); + visit_field(&node->data.pointer_type.op_expr, visit, context); + break; + case NodeTypeErrorSetDecl: + visit_node_list(&node->data.err_set_decl.decls, visit, context); + break; + case NodeTypeErrorSetField: + visit_field(&node->data.err_set_field.field_name, visit, context); + break; + case NodeTypeResume: + visit_field(&node->data.resume_expr.expr, visit, context); + break; + case NodeTypeAwaitExpr: + visit_field(&node->data.await_expr.expr, visit, context); + break; + case NodeTypeSuspend: + visit_field(&node->data.suspend.block, visit, context); + break; + case NodeTypeEnumLiteral: + case NodeTypeAnyTypeField: + break; + } +} diff --git a/src/stage1/parser.hpp b/src/stage1/parser.hpp new file mode 100644 index 0000000000000000000000000000000000000000..73950993f39421b0d80a5a3afaac7d38a91e07c2 --- /dev/null +++ b/src/stage1/parser.hpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2015 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef ZIG_PARSER_HPP +#define ZIG_PARSER_HPP + +#include "all_types.hpp" +#include "tokenizer.hpp" +#include "errmsg.hpp" + +ATTRIBUTE_PRINTF(2, 3) +void ast_token_error(Token *token, const char *format, ...); + + +AstNode * ast_parse(Buf *buf, ZigList *tokens, ZigType *owner, ErrColor err_color); + +void ast_print(AstNode *node, int indent); + +void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *context), void *context); + +#endif diff --git a/src/stage1/range_set.cpp b/src/stage1/range_set.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9e621d2f1305fd0b9b37dd149ceb0c6542fc1830 --- /dev/null +++ b/src/stage1/range_set.cpp @@ -0,0 +1,74 @@ +#include "range_set.hpp" + +AstNode *rangeset_add_range(RangeSet *rs, BigInt *first, BigInt *last, AstNode *source_node) { + for (size_t i = 0; i < rs->src_range_list.length; i += 1) { + RangeWithSrc *range_with_src = &rs->src_range_list.at(i); + Range *range = &range_with_src->range; + if ((bigint_cmp(first, &range->first) == CmpLT && bigint_cmp(last, &range->first) == CmpLT) || + (bigint_cmp(first, &range->last) == CmpGT && bigint_cmp(last, &range->last) == CmpGT)) + { + // first...last is completely before/after `range` + } + else + { + return range_with_src->source_node; + } + } + rs->src_range_list.append({{*first, *last}, source_node}); + + return nullptr; + +} + +static int compare_rangeset(const void *a, const void *b) { + const Range *r1 = &static_cast(a)->range; + const Range *r2 = &static_cast(b)->range; + // Assume no two ranges overlap + switch (bigint_cmp(&r1->first, &r2->first)) { + case CmpLT: return -1; + case CmpGT: return 1; + case CmpEQ: return 0; + } + zig_unreachable(); +} + +void rangeset_sort(RangeSet *rs) { + if (rs->src_range_list.length > 1) { + qsort(rs->src_range_list.items, rs->src_range_list.length, + sizeof(RangeWithSrc), compare_rangeset); + } +} + +bool rangeset_spans(RangeSet *rs, BigInt *first, BigInt *last) { + if (rs->src_range_list.length == 0) + return false; + + rangeset_sort(rs); + + const Range *first_range = &rs->src_range_list.at(0).range; + if (bigint_cmp(&first_range->first, first) != CmpEQ) + return false; + + const Range *last_range = &rs->src_range_list.last().range; + if (bigint_cmp(&last_range->last, last) != CmpEQ) + return false; + + BigInt one; + bigint_init_unsigned(&one, 1); + + // Make sure there are no holes in the first...last range + for (size_t i = 1; i < rs->src_range_list.length; i++) { + const Range *range = &rs->src_range_list.at(i).range; + const Range *prev_range = &rs->src_range_list.at(i - 1).range; + + assert(bigint_cmp(&prev_range->last, &range->first) == CmpLT); + + BigInt last_plus_one; + bigint_add(&last_plus_one, &prev_range->last, &one); + + if (bigint_cmp(&last_plus_one, &range->first) != CmpEQ) + return false; + } + + return true; +} diff --git a/src/stage1/range_set.hpp b/src/stage1/range_set.hpp new file mode 100644 index 0000000000000000000000000000000000000000..9164a8b5c0a50fbc22feb602771ce84ad7071cf1 --- /dev/null +++ b/src/stage1/range_set.hpp @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2017 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef ZIG_RANGE_SET_HPP +#define ZIG_RANGE_SET_HPP + +#include "all_types.hpp" + +struct Range { + BigInt first; + BigInt last; +}; + +struct RangeWithSrc { + Range range; + AstNode *source_node; +}; + +struct RangeSet { + ZigList src_range_list; +}; + +AstNode *rangeset_add_range(RangeSet *rs, BigInt *first, BigInt *last, AstNode *source_node); +bool rangeset_spans(RangeSet *rs, BigInt *first, BigInt *last); + +#endif diff --git a/src/stage1/softfloat.hpp b/src/stage1/softfloat.hpp new file mode 100644 index 0000000000000000000000000000000000000000..a1173690b549623b974397f00856483907e4f2a1 --- /dev/null +++ b/src/stage1/softfloat.hpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2017 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef ZIG_SOFTFLOAT_HPP +#define ZIG_SOFTFLOAT_HPP + +extern "C" { +#include "softfloat.h" +} + +static inline float16_t zig_double_to_f16(double x) { + float64_t y; + static_assert(sizeof(x) == sizeof(y), ""); + memcpy(&y, &x, sizeof(x)); + return f64_to_f16(y); +} + + +// Return value is safe to coerce to float even when |x| is NaN or Infinity. +static inline double zig_f16_to_double(float16_t x) { + float64_t y = f16_to_f64(x); + double z; + static_assert(sizeof(y) == sizeof(z), ""); + memcpy(&z, &y, sizeof(y)); + return z; +} + +#endif diff --git a/src/stage1/softfloat_ext.cpp b/src/stage1/softfloat_ext.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8408a1511682fdc6384870a8cef7ee6501ce5118 --- /dev/null +++ b/src/stage1/softfloat_ext.cpp @@ -0,0 +1,25 @@ +#include "softfloat_ext.hpp" + +extern "C" { + #include "softfloat.h" +} + +void f128M_abs(const float128_t *aPtr, float128_t *zPtr) { + float128_t zero_float; + ui32_to_f128M(0, &zero_float); + if (f128M_lt(aPtr, &zero_float)) { + f128M_sub(&zero_float, aPtr, zPtr); + } else { + *zPtr = *aPtr; + } +} + +void f128M_trunc(const float128_t *aPtr, float128_t *zPtr) { + float128_t zero_float; + ui32_to_f128M(0, &zero_float); + if (f128M_lt(aPtr, &zero_float)) { + f128M_roundToInt(aPtr, softfloat_round_max, false, zPtr); + } else { + f128M_roundToInt(aPtr, softfloat_round_min, false, zPtr); + } +} \ No newline at end of file diff --git a/src/stage1/softfloat_ext.hpp b/src/stage1/softfloat_ext.hpp new file mode 100644 index 0000000000000000000000000000000000000000..0a1f9589334ac5d1af151affd3765bc366598c26 --- /dev/null +++ b/src/stage1/softfloat_ext.hpp @@ -0,0 +1,9 @@ +#ifndef ZIG_SOFTFLOAT_EXT_HPP +#define ZIG_SOFTFLOAT_EXT_HPP + +#include "softfloat_types.h" + +void f128M_abs(const float128_t *aPtr, float128_t *zPtr); +void f128M_trunc(const float128_t *aPtr, float128_t *zPtr); + +#endif \ No newline at end of file diff --git a/src/stage1/stage1.cpp b/src/stage1/stage1.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1034f9ff88f261642f6ca1c76007e50c74ed5054 --- /dev/null +++ b/src/stage1/stage1.cpp @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2020 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#include "stage1.h" +#include "os.hpp" +#include "all_types.hpp" +#include "codegen.hpp" + +void zig_stage1_os_init(void) { + os_init(); + mem::init(); + init_all_targets(); +} + +struct ZigStage1 *zig_stage1_create(BuildMode optimize_mode, + const char *main_pkg_path_ptr, size_t main_pkg_path_len, + const char *root_src_path_ptr, size_t root_src_path_len, + const char *zig_lib_dir_ptr, size_t zig_lib_dir_len, + const ZigTarget *target, bool is_test_build) +{ + Buf *main_pkg_path = (main_pkg_path_len == 0) ? + nullptr : buf_create_from_mem(main_pkg_path_ptr, main_pkg_path_len); + Buf *root_src_path = buf_create_from_mem(root_src_path_ptr, root_src_path_len); + Buf *zig_lib_dir = buf_create_from_mem(zig_lib_dir_ptr, zig_lib_dir_len); + CodeGen *g = codegen_create(main_pkg_path, root_src_path, target, optimize_mode, + zig_lib_dir, is_test_build); + return &g->stage1; +} + +void zig_stage1_destroy(struct ZigStage1 *stage1) { + CodeGen *codegen = reinterpret_cast(stage1); + codegen_destroy(codegen); +} + +static void add_package(CodeGen *g, ZigStage1Pkg *stage1_pkg, ZigPackage *pkg) { + for (size_t i = 0; i < stage1_pkg->children_len; i += 1) { + ZigStage1Pkg *child_cli_pkg = stage1_pkg->children_ptr[i]; + + Buf *dirname = buf_alloc(); + Buf *basename = buf_alloc(); + os_path_split(buf_create_from_mem(child_cli_pkg->path_ptr, child_cli_pkg->path_len), dirname, basename); + + ZigPackage *child_pkg = codegen_create_package(g, buf_ptr(dirname), buf_ptr(basename), + buf_ptr(buf_sprintf("%s.%.*s", buf_ptr(&pkg->pkg_path), + (int)child_cli_pkg->name_len, child_cli_pkg->name_ptr))); + auto entry = pkg->package_table.put_unique( + buf_create_from_mem(child_cli_pkg->name_ptr, child_cli_pkg->name_len), + child_pkg); + if (entry) { + ZigPackage *existing_pkg = entry->value; + Buf *full_path = buf_alloc(); + os_path_join(&existing_pkg->root_src_dir, &existing_pkg->root_src_path, full_path); + fprintf(stderr, "Unable to add package '%.*s'->'%.*s': already exists as '%s'\n", + (int)child_cli_pkg->name_len, child_cli_pkg->name_ptr, + (int)child_cli_pkg->path_len, child_cli_pkg->path_ptr, + buf_ptr(full_path)); + exit(EXIT_FAILURE); + } + + add_package(g, child_cli_pkg, child_pkg); + } +} + +void zig_stage1_build_object(struct ZigStage1 *stage1) { + CodeGen *g = reinterpret_cast(stage1); + + g->root_out_name = buf_create_from_mem(stage1->root_name_ptr, stage1->root_name_len); + buf_init_from_mem(&g->o_file_output_path, stage1->emit_o_ptr, stage1->emit_o_len); + buf_init_from_mem(&g->h_file_output_path, stage1->emit_h_ptr, stage1->emit_h_len); + buf_init_from_mem(&g->asm_file_output_path, stage1->emit_asm_ptr, stage1->emit_asm_len); + buf_init_from_mem(&g->llvm_ir_file_output_path, stage1->emit_llvm_ir_ptr, stage1->emit_llvm_ir_len); + buf_init_from_mem(&g->analysis_json_output_path, stage1->emit_analysis_json_ptr, stage1->emit_analysis_json_len); + buf_init_from_mem(&g->docs_output_path, stage1->emit_docs_ptr, stage1->emit_docs_len); + + if (stage1->builtin_zig_path_len != 0) { + g->builtin_zig_path = buf_create_from_mem(stage1->builtin_zig_path_ptr, stage1->builtin_zig_path_len); + } + if (stage1->test_filter_len != 0) { + g->test_filter = buf_create_from_mem(stage1->test_filter_ptr, stage1->test_filter_len); + } + if (stage1->test_name_prefix_len != 0) { + g->test_name_prefix = buf_create_from_mem(stage1->test_name_prefix_ptr, stage1->test_name_prefix_len); + } + + g->link_mode_dynamic = stage1->link_mode_dynamic; + g->dll_export_fns = stage1->dll_export_fns; + g->have_pic = stage1->pic; + g->have_stack_probing = stage1->enable_stack_probing; + g->is_single_threaded = stage1->is_single_threaded; + g->valgrind_enabled = stage1->valgrind_enabled; + g->link_libc = stage1->link_libc; + g->link_libcpp = stage1->link_libcpp; + g->function_sections = stage1->function_sections; + + g->subsystem = stage1->subsystem; + + g->enable_time_report = stage1->enable_time_report; + g->enable_stack_report = stage1->enable_stack_report; + g->test_is_evented = stage1->test_is_evented; + + g->verbose_tokenize = stage1->verbose_tokenize; + g->verbose_ast = stage1->verbose_ast; + g->verbose_ir = stage1->verbose_ir; + g->verbose_llvm_ir = stage1->verbose_llvm_ir; + g->verbose_cimport = stage1->verbose_cimport; + g->verbose_llvm_cpu_features = stage1->verbose_llvm_cpu_features; + + g->err_color = stage1->err_color; + g->code_model = stage1->code_model; + + { + g->strip_debug_symbols = stage1->strip; + if (!target_has_debug_info(g->zig_target)) { + g->strip_debug_symbols = true; + } + } + + g->main_progress_node = stage1->main_progress_node; + + add_package(g, stage1->root_pkg, g->main_pkg); + + codegen_build_object(g); +} diff --git a/src/stage1/stage1.h b/src/stage1/stage1.h new file mode 100644 index 0000000000000000000000000000000000000000..412118a6fa1c271cc073b1875b1906b0b045b694 --- /dev/null +++ b/src/stage1/stage1.h @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2020 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +// This file deals with exposing stage1 C++ code to stage2 Zig code. + +#ifndef ZIG_STAGE1_H +#define ZIG_STAGE1_H + +#include "zig_llvm.h" + +#include + +#ifdef __cplusplus +#define ZIG_EXTERN_C extern "C" +#else +#define ZIG_EXTERN_C +#endif + +// ABI warning +enum ErrColor { + ErrColorAuto, + ErrColorOff, + ErrColorOn, +}; + +// ABI warning +enum CodeModel { + CodeModelDefault, + CodeModelTiny, + CodeModelSmall, + CodeModelKernel, + CodeModelMedium, + CodeModelLarge, +}; + +// ABI warning +enum TargetSubsystem { + TargetSubsystemConsole, + TargetSubsystemWindows, + TargetSubsystemPosix, + TargetSubsystemNative, + TargetSubsystemEfiApplication, + TargetSubsystemEfiBootServiceDriver, + TargetSubsystemEfiRom, + TargetSubsystemEfiRuntimeDriver, + + // This means Zig should infer the subsystem. + // It's last so that the indexes of other items can line up + // with the enum in builtin.zig. + TargetSubsystemAuto +}; + + +// ABI warning +// Synchronize with target.cpp::os_list +enum Os { + OsFreestanding, + OsAnanas, + OsCloudABI, + OsDragonFly, + OsFreeBSD, + OsFuchsia, + OsIOS, + OsKFreeBSD, + OsLinux, + OsLv2, // PS3 + OsMacOSX, + OsNetBSD, + OsOpenBSD, + OsSolaris, + OsWindows, + OsHaiku, + OsMinix, + OsRTEMS, + OsNaCl, // Native Client + OsCNK, // BG/P Compute-Node Kernel + OsAIX, + OsCUDA, // NVIDIA CUDA + OsNVCL, // NVIDIA OpenCL + OsAMDHSA, // AMD HSA Runtime + OsPS4, + OsELFIAMCU, + OsTvOS, // Apple tvOS + OsWatchOS, // Apple watchOS + OsMesa3D, + OsContiki, + OsAMDPAL, + OsHermitCore, + OsHurd, + OsWASI, + OsEmscripten, + OsUefi, + OsOther, +}; + +// ABI warning +struct ZigTarget { + enum ZigLLVM_ArchType arch; + enum Os os; + enum ZigLLVM_EnvironmentType abi; + + bool is_native_os; + bool is_native_cpu; + + const char *llvm_cpu_name; + const char *llvm_cpu_features; +}; + +// ABI warning +struct Stage2Progress; +// ABI warning +struct Stage2ProgressNode; + +enum BuildMode { + BuildModeDebug, + BuildModeSafeRelease, + BuildModeFastRelease, + BuildModeSmallRelease, +}; + + +struct ZigStage1Pkg { + const char *name_ptr; + size_t name_len; + + const char *path_ptr; + size_t path_len; + + struct ZigStage1Pkg **children_ptr; + size_t children_len; + + struct ZigStage1Pkg *parent; +}; + +// This struct is used by both main.cpp and stage1.zig. +struct ZigStage1 { + const char *root_name_ptr; + size_t root_name_len; + + const char *emit_o_ptr; + size_t emit_o_len; + + const char *emit_h_ptr; + size_t emit_h_len; + + const char *emit_asm_ptr; + size_t emit_asm_len; + + const char *emit_llvm_ir_ptr; + size_t emit_llvm_ir_len; + + const char *emit_analysis_json_ptr; + size_t emit_analysis_json_len; + + const char *emit_docs_ptr; + size_t emit_docs_len; + + const char *builtin_zig_path_ptr; + size_t builtin_zig_path_len; + + const char *test_filter_ptr; + size_t test_filter_len; + + const char *test_name_prefix_ptr; + size_t test_name_prefix_len; + + void *userdata; + struct ZigStage1Pkg *root_pkg; + struct Stage2ProgressNode *main_progress_node; + + enum CodeModel code_model; + enum TargetSubsystem subsystem; + enum ErrColor err_color; + + bool pic; + bool link_libc; + bool link_libcpp; + bool strip; + bool is_single_threaded; + bool dll_export_fns; + bool link_mode_dynamic; + bool valgrind_enabled; + bool function_sections; + bool enable_stack_probing; + bool enable_time_report; + bool enable_stack_report; + bool test_is_evented; + bool verbose_tokenize; + bool verbose_ast; + bool verbose_ir; + bool verbose_llvm_ir; + bool verbose_cimport; + bool verbose_llvm_cpu_features; + + // Set by stage1 + bool have_c_main; + bool have_winmain; + bool have_wwinmain; + bool have_winmain_crt_startup; + bool have_wwinmain_crt_startup; + bool have_dllmain_crt_startup; +}; + +ZIG_EXTERN_C void zig_stage1_os_init(void); + +ZIG_EXTERN_C struct ZigStage1 *zig_stage1_create(enum BuildMode optimize_mode, + const char *main_pkg_path_ptr, size_t main_pkg_path_len, + const char *root_src_path_ptr, size_t root_src_path_len, + const char *zig_lib_dir_ptr, size_t zig_lib_dir_len, + const struct ZigTarget *target, bool is_test_build); + +ZIG_EXTERN_C void zig_stage1_build_object(struct ZigStage1 *); + +ZIG_EXTERN_C void zig_stage1_destroy(struct ZigStage1 *); + +#endif diff --git a/src/stage1/stage2.h b/src/stage1/stage2.h new file mode 100644 index 0000000000000000000000000000000000000000..886a4c26603779a3e954679b1b7e751d712a58b6 --- /dev/null +++ b/src/stage1/stage2.h @@ -0,0 +1,177 @@ +/* + * Copyright (c) 2019 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +// This file deals with exposing stage2 Zig code to stage1 C++ code. + +#ifndef ZIG_STAGE2_H +#define ZIG_STAGE2_H + +#include +#include +#include + +#include "stage1.h" + +#ifdef __cplusplus +#define ZIG_EXTERN_C extern "C" +#else +#define ZIG_EXTERN_C +#endif + +#if defined(_MSC_VER) +#define ZIG_ATTRIBUTE_NORETURN __declspec(noreturn) +#else +#define ZIG_ATTRIBUTE_NORETURN __attribute__((noreturn)) +#endif + +// ABI warning: the types and declarations in this file must match both those in +// stage2.cpp and src/stage1.zig. + +// ABI warning +enum Error { + ErrorNone, + ErrorNoMem, + ErrorInvalidFormat, + ErrorSemanticAnalyzeFail, + ErrorAccess, + ErrorInterrupted, + ErrorSystemResources, + ErrorFileNotFound, + ErrorFileSystem, + ErrorFileTooBig, + ErrorDivByZero, + ErrorOverflow, + ErrorPathAlreadyExists, + ErrorUnexpected, + ErrorExactDivRemainder, + ErrorNegativeDenominator, + ErrorShiftedOutOneBits, + ErrorCCompileErrors, + ErrorEndOfFile, + ErrorIsDir, + ErrorNotDir, + ErrorUnsupportedOperatingSystem, + ErrorSharingViolation, + ErrorPipeBusy, + ErrorPrimitiveTypeNotFound, + ErrorCacheUnavailable, + ErrorPathTooLong, + ErrorCCompilerCannotFindFile, + ErrorNoCCompilerInstalled, + ErrorReadingDepFile, + ErrorInvalidDepFile, + ErrorMissingArchitecture, + ErrorMissingOperatingSystem, + ErrorUnknownArchitecture, + ErrorUnknownOperatingSystem, + ErrorUnknownABI, + ErrorInvalidFilename, + ErrorDiskQuota, + ErrorDiskSpace, + ErrorUnexpectedWriteFailure, + ErrorUnexpectedSeekFailure, + ErrorUnexpectedFileTruncationFailure, + ErrorUnimplemented, + ErrorOperationAborted, + ErrorBrokenPipe, + ErrorNoSpaceLeft, + ErrorNotLazy, + ErrorIsAsync, + ErrorImportOutsidePkgPath, + ErrorUnknownCpu, + ErrorUnknownCpuFeature, + ErrorInvalidCpuFeatures, + ErrorInvalidLlvmCpuFeaturesFormat, + ErrorUnknownApplicationBinaryInterface, + ErrorASTUnitFailure, + ErrorBadPathName, + ErrorSymLinkLoop, + ErrorProcessFdQuotaExceeded, + ErrorSystemFdQuotaExceeded, + ErrorNoDevice, + ErrorDeviceBusy, + ErrorUnableToSpawnCCompiler, + ErrorCCompilerExitCode, + ErrorCCompilerCrashed, + ErrorCCompilerCannotFindHeaders, + ErrorLibCRuntimeNotFound, + ErrorLibCStdLibHeaderNotFound, + ErrorLibCKernel32LibNotFound, + ErrorUnsupportedArchitecture, + ErrorWindowsSdkNotFound, + ErrorUnknownDynamicLinkerPath, + ErrorTargetHasNoDynamicLinker, + ErrorInvalidAbiVersion, + ErrorInvalidOperatingSystemVersion, + ErrorUnknownClangOption, + ErrorNestedResponseFile, + ErrorZigIsTheCCompiler, + ErrorFileBusy, + ErrorLocked, +}; + +// ABI warning +struct Stage2ErrorMsg { + const char *filename_ptr; // can be null + size_t filename_len; + const char *msg_ptr; + size_t msg_len; + const char *source; // valid until the ASTUnit is freed. can be null + unsigned line; // 0 based + unsigned column; // 0 based + unsigned offset; // byte offset into source +}; + +// ABI warning +ZIG_EXTERN_C ZIG_ATTRIBUTE_NORETURN void stage2_panic(const char *ptr, size_t len); + +// ABI warning +ZIG_EXTERN_C struct Stage2Progress *stage2_progress_create(void); +// ABI warning +ZIG_EXTERN_C void stage2_progress_disable_tty(struct Stage2Progress *progress); +// ABI warning +ZIG_EXTERN_C void stage2_progress_destroy(struct Stage2Progress *progress); +// ABI warning +ZIG_EXTERN_C struct Stage2ProgressNode *stage2_progress_start_root(struct Stage2Progress *progress, + const char *name_ptr, size_t name_len, size_t estimated_total_items); +// ABI warning +ZIG_EXTERN_C struct Stage2ProgressNode *stage2_progress_start(struct Stage2ProgressNode *node, + const char *name_ptr, size_t name_len, size_t estimated_total_items); +// ABI warning +ZIG_EXTERN_C void stage2_progress_end(struct Stage2ProgressNode *node); +// ABI warning +ZIG_EXTERN_C void stage2_progress_complete_one(struct Stage2ProgressNode *node); +// ABI warning +ZIG_EXTERN_C void stage2_progress_update_node(struct Stage2ProgressNode *node, + size_t completed_count, size_t estimated_total_items); + +// ABI warning +struct Stage2SemVer { + uint32_t major; + uint32_t minor; + uint32_t patch; +}; + +// ABI warning +ZIG_EXTERN_C enum Error stage2_target_parse(struct ZigTarget *target, const char *zig_triple, const char *mcpu, + const char *dynamic_linker); + +// ABI warning +ZIG_EXTERN_C const char *stage2_fetch_file(struct ZigStage1 *stage1, const char *path_ptr, size_t path_len, + size_t *result_len); + +// ABI warning +ZIG_EXTERN_C Error stage2_cimport(struct ZigStage1 *stage1, const char *c_src_ptr, size_t c_src_len, + const char **out_zig_path_ptr, size_t *out_zig_path_len, + struct Stage2ErrorMsg **out_errors_ptr, size_t *out_errors_len); + +// ABI warning +ZIG_EXTERN_C const char *stage2_add_link_lib(struct ZigStage1 *stage1, + const char *lib_name_ptr, size_t lib_name_len, + const char *symbol_name_ptr, size_t symbol_name_len); + +#endif diff --git a/src/stage1/target.cpp b/src/stage1/target.cpp new file mode 100644 index 0000000000000000000000000000000000000000..433c988a01eb47231c05386f1122d59ea45d500d --- /dev/null +++ b/src/stage1/target.cpp @@ -0,0 +1,1211 @@ +/* + * Copyright (c) 2016 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#include "buffer.hpp" +#include "error.hpp" +#include "target.hpp" +#include "util.hpp" +#include "os.hpp" + +#include + +static const ZigLLVM_ArchType arch_list[] = { + ZigLLVM_arm, // ARM (little endian): arm, armv.*, xscale + ZigLLVM_armeb, // ARM (big endian): armeb + ZigLLVM_aarch64, // AArch64 (little endian): aarch64 + ZigLLVM_aarch64_be, // AArch64 (big endian): aarch64_be + ZigLLVM_aarch64_32, // AArch64 (little endian) ILP32: aarch64_32 + ZigLLVM_arc, // ARC: Synopsys ARC + ZigLLVM_avr, // AVR: Atmel AVR microcontroller + ZigLLVM_bpfel, // eBPF or extended BPF or 64-bit BPF (little endian) + ZigLLVM_bpfeb, // eBPF or extended BPF or 64-bit BPF (big endian) + ZigLLVM_hexagon, // Hexagon: hexagon + ZigLLVM_mips, // MIPS: mips, mipsallegrex, mipsr6 + ZigLLVM_mipsel, // MIPSEL: mipsel, mipsallegrexe, mipsr6el + ZigLLVM_mips64, // MIPS64: mips64, mips64r6, mipsn32, mipsn32r6 + ZigLLVM_mips64el, // MIPS64EL: mips64el, mips64r6el, mipsn32el, mipsn32r6el + ZigLLVM_msp430, // MSP430: msp430 + ZigLLVM_ppc, // PPC: powerpc + ZigLLVM_ppc64, // PPC64: powerpc64, ppu + ZigLLVM_ppc64le, // PPC64LE: powerpc64le + ZigLLVM_r600, // R600: AMD GPUs HD2XXX - HD6XXX + ZigLLVM_amdgcn, // AMDGCN: AMD GCN GPUs + ZigLLVM_riscv32, // RISC-V (32-bit): riscv32 + ZigLLVM_riscv64, // RISC-V (64-bit): riscv64 + ZigLLVM_sparc, // Sparc: sparc + ZigLLVM_sparcv9, // Sparcv9: Sparcv9 + ZigLLVM_sparcel, // Sparc: (endianness = little). NB: 'Sparcle' is a CPU variant + ZigLLVM_systemz, // SystemZ: s390x + ZigLLVM_tce, // TCE (http://tce.cs.tut.fi/): tce + ZigLLVM_tcele, // TCE little endian (http://tce.cs.tut.fi/): tcele + ZigLLVM_thumb, // Thumb (little endian): thumb, thumbv.* + ZigLLVM_thumbeb, // Thumb (big endian): thumbeb + ZigLLVM_x86, // X86: i[3-9]86 + ZigLLVM_x86_64, // X86-64: amd64, x86_64 + ZigLLVM_xcore, // XCore: xcore + ZigLLVM_nvptx, // NVPTX: 32-bit + ZigLLVM_nvptx64, // NVPTX: 64-bit + ZigLLVM_le32, // le32: generic little-endian 32-bit CPU (PNaCl) + ZigLLVM_le64, // le64: generic little-endian 64-bit CPU (PNaCl) + ZigLLVM_amdil, // AMDIL + ZigLLVM_amdil64, // AMDIL with 64-bit pointers + ZigLLVM_hsail, // AMD HSAIL + ZigLLVM_hsail64, // AMD HSAIL with 64-bit pointers + ZigLLVM_spir, // SPIR: standard portable IR for OpenCL 32-bit version + ZigLLVM_spir64, // SPIR: standard portable IR for OpenCL 64-bit version + ZigLLVM_kalimba, // Kalimba: generic kalimba + ZigLLVM_shave, // SHAVE: Movidius vector VLIW processors + ZigLLVM_lanai, // Lanai: Lanai 32-bit + ZigLLVM_wasm32, // WebAssembly with 32-bit pointers + ZigLLVM_wasm64, // WebAssembly with 64-bit pointers + ZigLLVM_renderscript32, // 32-bit RenderScript + ZigLLVM_renderscript64, // 64-bit RenderScript + ZigLLVM_ve, // NEC SX-Aurora Vector Engine +}; + +static const ZigLLVM_VendorType vendor_list[] = { + ZigLLVM_Apple, + ZigLLVM_PC, + ZigLLVM_SCEI, + ZigLLVM_BGP, + ZigLLVM_BGQ, + ZigLLVM_Freescale, + ZigLLVM_IBM, + ZigLLVM_ImaginationTechnologies, + ZigLLVM_MipsTechnologies, + ZigLLVM_NVIDIA, + ZigLLVM_CSR, + ZigLLVM_Myriad, + ZigLLVM_AMD, + ZigLLVM_Mesa, + ZigLLVM_SUSE, +}; + +static const Os os_list[] = { + OsFreestanding, + OsAnanas, + OsCloudABI, + OsDragonFly, + OsFreeBSD, + OsFuchsia, + OsIOS, + OsKFreeBSD, + OsLinux, + OsLv2, // PS3 + OsMacOSX, + OsNetBSD, + OsOpenBSD, + OsSolaris, + OsWindows, + OsHaiku, + OsMinix, + OsRTEMS, + OsNaCl, // Native Client + OsCNK, // BG/P Compute-Node Kernel + OsAIX, + OsCUDA, // NVIDIA CUDA + OsNVCL, // NVIDIA OpenCL + OsAMDHSA, // AMD HSA Runtime + OsPS4, + OsELFIAMCU, + OsTvOS, // Apple tvOS + OsWatchOS, // Apple watchOS + OsMesa3D, + OsContiki, + OsAMDPAL, + OsHermitCore, + OsHurd, + OsWASI, + OsEmscripten, + OsUefi, + OsOther, +}; + +// Coordinate with zig_llvm.h +static const ZigLLVM_EnvironmentType abi_list[] = { + ZigLLVM_UnknownEnvironment, + + ZigLLVM_GNU, + ZigLLVM_GNUABIN32, + ZigLLVM_GNUABI64, + ZigLLVM_GNUEABI, + ZigLLVM_GNUEABIHF, + ZigLLVM_GNUX32, + ZigLLVM_CODE16, + ZigLLVM_EABI, + ZigLLVM_EABIHF, + ZigLLVM_Android, + ZigLLVM_Musl, + ZigLLVM_MuslEABI, + ZigLLVM_MuslEABIHF, + + ZigLLVM_MSVC, + ZigLLVM_Itanium, + ZigLLVM_Cygnus, + ZigLLVM_CoreCLR, + ZigLLVM_Simulator, + ZigLLVM_MacABI, +}; + +static const ZigLLVM_ObjectFormatType oformat_list[] = { + ZigLLVM_UnknownObjectFormat, + ZigLLVM_COFF, + ZigLLVM_ELF, + ZigLLVM_MachO, + ZigLLVM_Wasm, +}; + +size_t target_oformat_count(void) { + return array_length(oformat_list); +} + +ZigLLVM_ObjectFormatType target_oformat_enum(size_t index) { + assert(index < array_length(oformat_list)); + return oformat_list[index]; +} + +const char *target_oformat_name(ZigLLVM_ObjectFormatType oformat) { + switch (oformat) { + case ZigLLVM_UnknownObjectFormat: return "unknown"; + case ZigLLVM_COFF: return "coff"; + case ZigLLVM_ELF: return "elf"; + case ZigLLVM_MachO: return "macho"; + case ZigLLVM_Wasm: return "wasm"; + case ZigLLVM_XCOFF: return "xcoff"; + } + zig_unreachable(); +} + +size_t target_arch_count(void) { + return array_length(arch_list); +} + +ZigLLVM_ArchType target_arch_enum(size_t index) { + assert(index < array_length(arch_list)); + return arch_list[index]; +} + +size_t target_vendor_count(void) { + return array_length(vendor_list); +} + +ZigLLVM_VendorType target_vendor_enum(size_t index) { + assert(index < array_length(vendor_list)); + return vendor_list[index]; +} + +size_t target_os_count(void) { + return array_length(os_list); +} +Os target_os_enum(size_t index) { + assert(index < array_length(os_list)); + return os_list[index]; +} + +ZigLLVM_OSType get_llvm_os_type(Os os_type) { + switch (os_type) { + case OsFreestanding: + case OsOther: + return ZigLLVM_UnknownOS; + case OsAnanas: + return ZigLLVM_Ananas; + case OsCloudABI: + return ZigLLVM_CloudABI; + case OsDragonFly: + return ZigLLVM_DragonFly; + case OsFreeBSD: + return ZigLLVM_FreeBSD; + case OsFuchsia: + return ZigLLVM_Fuchsia; + case OsIOS: + return ZigLLVM_IOS; + case OsKFreeBSD: + return ZigLLVM_KFreeBSD; + case OsLinux: + return ZigLLVM_Linux; + case OsLv2: + return ZigLLVM_Lv2; + case OsMacOSX: + return ZigLLVM_MacOSX; + case OsNetBSD: + return ZigLLVM_NetBSD; + case OsOpenBSD: + return ZigLLVM_OpenBSD; + case OsSolaris: + return ZigLLVM_Solaris; + case OsWindows: + case OsUefi: + return ZigLLVM_Win32; + case OsHaiku: + return ZigLLVM_Haiku; + case OsMinix: + return ZigLLVM_Minix; + case OsRTEMS: + return ZigLLVM_RTEMS; + case OsNaCl: + return ZigLLVM_NaCl; + case OsCNK: + return ZigLLVM_CNK; + case OsAIX: + return ZigLLVM_AIX; + case OsCUDA: + return ZigLLVM_CUDA; + case OsNVCL: + return ZigLLVM_NVCL; + case OsAMDHSA: + return ZigLLVM_AMDHSA; + case OsPS4: + return ZigLLVM_PS4; + case OsELFIAMCU: + return ZigLLVM_ELFIAMCU; + case OsTvOS: + return ZigLLVM_TvOS; + case OsWatchOS: + return ZigLLVM_WatchOS; + case OsMesa3D: + return ZigLLVM_Mesa3D; + case OsContiki: + return ZigLLVM_Contiki; + case OsAMDPAL: + return ZigLLVM_AMDPAL; + case OsHermitCore: + return ZigLLVM_HermitCore; + case OsHurd: + return ZigLLVM_Hurd; + case OsWASI: + return ZigLLVM_WASI; + case OsEmscripten: + return ZigLLVM_Emscripten; + } + zig_unreachable(); +} + +const char *target_os_name(Os os_type) { + switch (os_type) { + case OsFreestanding: + return "freestanding"; + case OsUefi: + return "uefi"; + case OsOther: + return "other"; + case OsAnanas: + case OsCloudABI: + case OsDragonFly: + case OsFreeBSD: + case OsFuchsia: + case OsIOS: + case OsKFreeBSD: + case OsLinux: + case OsLv2: // PS3 + case OsMacOSX: + case OsNetBSD: + case OsOpenBSD: + case OsSolaris: + case OsWindows: + case OsHaiku: + case OsMinix: + case OsRTEMS: + case OsNaCl: // Native Client + case OsCNK: // BG/P Compute-Node Kernel + case OsAIX: + case OsCUDA: // NVIDIA CUDA + case OsNVCL: // NVIDIA OpenCL + case OsAMDHSA: // AMD HSA Runtime + case OsPS4: + case OsELFIAMCU: + case OsTvOS: // Apple tvOS + case OsWatchOS: // Apple watchOS + case OsMesa3D: + case OsContiki: + case OsAMDPAL: + case OsHermitCore: + case OsHurd: + case OsWASI: + case OsEmscripten: + return ZigLLVMGetOSTypeName(get_llvm_os_type(os_type)); + } + zig_unreachable(); +} + +size_t target_abi_count(void) { + return array_length(abi_list); +} +ZigLLVM_EnvironmentType target_abi_enum(size_t index) { + assert(index < array_length(abi_list)); + return abi_list[index]; +} +const char *target_abi_name(ZigLLVM_EnvironmentType abi) { + if (abi == ZigLLVM_UnknownEnvironment) + return "none"; + return ZigLLVMGetEnvironmentTypeName(abi); +} + +Error target_parse_arch(ZigLLVM_ArchType *out_arch, const char *arch_ptr, size_t arch_len) { + *out_arch = ZigLLVM_UnknownArch; + for (size_t arch_i = 0; arch_i < array_length(arch_list); arch_i += 1) { + ZigLLVM_ArchType arch = arch_list[arch_i]; + if (mem_eql_str(arch_ptr, arch_len, target_arch_name(arch))) { + *out_arch = arch; + return ErrorNone; + } + } + return ErrorUnknownArchitecture; +} + +Error target_parse_os(Os *out_os, const char *os_ptr, size_t os_len) { + for (size_t i = 0; i < array_length(os_list); i += 1) { + Os os = os_list[i]; + const char *os_name = target_os_name(os); + if (mem_eql_str(os_ptr, os_len, os_name)) { + *out_os = os; + return ErrorNone; + } + } + return ErrorUnknownOperatingSystem; +} + +Error target_parse_abi(ZigLLVM_EnvironmentType *out_abi, const char *abi_ptr, size_t abi_len) { + for (size_t i = 0; i < array_length(abi_list); i += 1) { + ZigLLVM_EnvironmentType abi = abi_list[i]; + const char *abi_name = target_abi_name(abi); + if (mem_eql_str(abi_ptr, abi_len, abi_name)) { + *out_abi = abi; + return ErrorNone; + } + } + return ErrorUnknownABI; +} + +const char *target_arch_name(ZigLLVM_ArchType arch) { + return ZigLLVMGetArchTypeName(arch); +} + +void init_all_targets(void) { + LLVMInitializeAllTargets(); + LLVMInitializeAllTargetInfos(); + LLVMInitializeAllTargetMCs(); + LLVMInitializeAllAsmPrinters(); + LLVMInitializeAllAsmParsers(); +} + +void target_triple_zig(Buf *triple, const ZigTarget *target) { + buf_resize(triple, 0); + buf_appendf(triple, "%s-%s-%s", + target_arch_name(target->arch), + target_os_name(target->os), + target_abi_name(target->abi)); +} + +void target_triple_llvm(Buf *triple, const ZigTarget *target) { + buf_resize(triple, 0); + buf_appendf(triple, "%s-%s-%s-%s", + ZigLLVMGetArchTypeName(target->arch), + ZigLLVMGetVendorTypeName(ZigLLVM_UnknownVendor), + ZigLLVMGetOSTypeName(get_llvm_os_type(target->os)), + ZigLLVMGetEnvironmentTypeName(target->abi)); +} + +bool target_os_is_darwin(Os os) { + switch (os) { + case OsMacOSX: + case OsIOS: + case OsWatchOS: + case OsTvOS: + return true; + default: + return false; + } +} + +ZigLLVM_ObjectFormatType target_object_format(const ZigTarget *target) { + if (target->os == OsUefi || target->os == OsWindows) { + return ZigLLVM_COFF; + } else if (target_os_is_darwin(target->os)) { + return ZigLLVM_MachO; + } + if (target->arch == ZigLLVM_wasm32 || + target->arch == ZigLLVM_wasm64) + { + return ZigLLVM_Wasm; + } + return ZigLLVM_ELF; +} + +// See lib/Support/Triple.cpp in LLVM for the source of this data. +// getArchPointerBitWidth +uint32_t target_arch_pointer_bit_width(ZigLLVM_ArchType arch) { + switch (arch) { + case ZigLLVM_UnknownArch: + return 0; + + case ZigLLVM_avr: + case ZigLLVM_msp430: + return 16; + + case ZigLLVM_arc: + case ZigLLVM_arm: + case ZigLLVM_armeb: + case ZigLLVM_hexagon: + case ZigLLVM_le32: + case ZigLLVM_mips: + case ZigLLVM_mipsel: + case ZigLLVM_nvptx: + case ZigLLVM_ppc: + case ZigLLVM_r600: + case ZigLLVM_riscv32: + case ZigLLVM_sparc: + case ZigLLVM_sparcel: + case ZigLLVM_tce: + case ZigLLVM_tcele: + case ZigLLVM_thumb: + case ZigLLVM_thumbeb: + case ZigLLVM_x86: + case ZigLLVM_xcore: + case ZigLLVM_amdil: + case ZigLLVM_hsail: + case ZigLLVM_spir: + case ZigLLVM_kalimba: + case ZigLLVM_lanai: + case ZigLLVM_shave: + case ZigLLVM_wasm32: + case ZigLLVM_renderscript32: + case ZigLLVM_aarch64_32: + return 32; + + case ZigLLVM_aarch64: + case ZigLLVM_aarch64_be: + case ZigLLVM_amdgcn: + case ZigLLVM_bpfel: + case ZigLLVM_bpfeb: + case ZigLLVM_le64: + case ZigLLVM_mips64: + case ZigLLVM_mips64el: + case ZigLLVM_nvptx64: + case ZigLLVM_ppc64: + case ZigLLVM_ppc64le: + case ZigLLVM_riscv64: + case ZigLLVM_sparcv9: + case ZigLLVM_systemz: + case ZigLLVM_x86_64: + case ZigLLVM_amdil64: + case ZigLLVM_hsail64: + case ZigLLVM_spir64: + case ZigLLVM_wasm64: + case ZigLLVM_renderscript64: + case ZigLLVM_ve: + return 64; + } + zig_unreachable(); +} + +uint32_t target_arch_largest_atomic_bits(ZigLLVM_ArchType arch) { + switch (arch) { + case ZigLLVM_UnknownArch: + zig_unreachable(); + + case ZigLLVM_avr: + case ZigLLVM_msp430: + return 16; + + case ZigLLVM_arc: + case ZigLLVM_arm: + case ZigLLVM_armeb: + case ZigLLVM_hexagon: + case ZigLLVM_le32: + case ZigLLVM_mips: + case ZigLLVM_mipsel: + case ZigLLVM_nvptx: + case ZigLLVM_ppc: + case ZigLLVM_r600: + case ZigLLVM_riscv32: + case ZigLLVM_sparc: + case ZigLLVM_sparcel: + case ZigLLVM_tce: + case ZigLLVM_tcele: + case ZigLLVM_thumb: + case ZigLLVM_thumbeb: + case ZigLLVM_x86: + case ZigLLVM_xcore: + case ZigLLVM_amdil: + case ZigLLVM_hsail: + case ZigLLVM_spir: + case ZigLLVM_kalimba: + case ZigLLVM_lanai: + case ZigLLVM_shave: + case ZigLLVM_wasm32: + case ZigLLVM_renderscript32: + return 32; + + case ZigLLVM_aarch64: + case ZigLLVM_aarch64_be: + case ZigLLVM_aarch64_32: + case ZigLLVM_amdgcn: + case ZigLLVM_bpfel: + case ZigLLVM_bpfeb: + case ZigLLVM_le64: + case ZigLLVM_mips64: + case ZigLLVM_mips64el: + case ZigLLVM_nvptx64: + case ZigLLVM_ppc64: + case ZigLLVM_ppc64le: + case ZigLLVM_riscv64: + case ZigLLVM_sparcv9: + case ZigLLVM_systemz: + case ZigLLVM_amdil64: + case ZigLLVM_hsail64: + case ZigLLVM_spir64: + case ZigLLVM_wasm64: + case ZigLLVM_renderscript64: + case ZigLLVM_ve: + return 64; + + case ZigLLVM_x86_64: + return 128; + } + zig_unreachable(); +} + +uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id) { + switch (target->os) { + case OsFreestanding: + case OsOther: + switch (target->arch) { + case ZigLLVM_msp430: + switch (id) { + case CIntTypeShort: + case CIntTypeUShort: + return 16; + case CIntTypeInt: + case CIntTypeUInt: + return 16; + case CIntTypeLong: + case CIntTypeULong: + return 32; + case CIntTypeLongLong: + case CIntTypeULongLong: + return 64; + case CIntTypeCount: + zig_unreachable(); + } + zig_unreachable(); + default: + switch (id) { + case CIntTypeShort: + case CIntTypeUShort: + return 16; + case CIntTypeInt: + case CIntTypeUInt: + return 32; + case CIntTypeLong: + case CIntTypeULong: + return target_arch_pointer_bit_width(target->arch); + case CIntTypeLongLong: + case CIntTypeULongLong: + return 64; + case CIntTypeCount: + zig_unreachable(); + } + } + zig_unreachable(); + case OsLinux: + case OsMacOSX: + case OsFreeBSD: + case OsNetBSD: + case OsDragonFly: + case OsOpenBSD: + case OsWASI: + case OsEmscripten: + switch (id) { + case CIntTypeShort: + case CIntTypeUShort: + return 16; + case CIntTypeInt: + case CIntTypeUInt: + return 32; + case CIntTypeLong: + case CIntTypeULong: + return target_arch_pointer_bit_width(target->arch); + case CIntTypeLongLong: + case CIntTypeULongLong: + return 64; + case CIntTypeCount: + zig_unreachable(); + } + zig_unreachable(); + case OsUefi: + case OsWindows: + switch (id) { + case CIntTypeShort: + case CIntTypeUShort: + return 16; + case CIntTypeInt: + case CIntTypeUInt: + case CIntTypeLong: + case CIntTypeULong: + return 32; + case CIntTypeLongLong: + case CIntTypeULongLong: + return 64; + case CIntTypeCount: + zig_unreachable(); + } + zig_unreachable(); + case OsIOS: + switch (id) { + case CIntTypeShort: + case CIntTypeUShort: + return 16; + case CIntTypeInt: + case CIntTypeUInt: + return 32; + case CIntTypeLong: + case CIntTypeULong: + case CIntTypeLongLong: + case CIntTypeULongLong: + return 64; + case CIntTypeCount: + zig_unreachable(); + } + zig_unreachable(); + case OsAnanas: + case OsCloudABI: + case OsKFreeBSD: + case OsLv2: + case OsSolaris: + case OsHaiku: + case OsMinix: + case OsRTEMS: + case OsNaCl: + case OsCNK: + case OsAIX: + case OsCUDA: + case OsNVCL: + case OsAMDHSA: + case OsPS4: + case OsELFIAMCU: + case OsTvOS: + case OsWatchOS: + case OsMesa3D: + case OsFuchsia: + case OsContiki: + case OsAMDPAL: + case OsHermitCore: + case OsHurd: + zig_panic("TODO c type size in bits for this target"); + } + zig_unreachable(); +} + +bool target_allows_addr_zero(const ZigTarget *target) { + return target->os == OsFreestanding || target->os == OsUefi; +} + +const char *target_o_file_ext(const ZigTarget *target) { + if (target->abi == ZigLLVM_MSVC || + (target->os == OsWindows && !target_abi_is_gnu(target->abi)) || + target->os == OsUefi) + { + return ".obj"; + } else { + return ".o"; + } +} + +const char *target_asm_file_ext(const ZigTarget *target) { + return ".s"; +} + +const char *target_llvm_ir_file_ext(const ZigTarget *target) { + return ".ll"; +} + +bool target_is_android(const ZigTarget *target) { + return target->abi == ZigLLVM_Android; +} + +bool target_can_exec(const ZigTarget *host_target, const ZigTarget *guest_target) { + assert(host_target != nullptr); + + if (guest_target == nullptr) { + // null guest target means that the guest target is native + return true; + } + + if (guest_target->os == host_target->os && guest_target->arch == host_target->arch) { + // OS and arch match + return true; + } + + if (guest_target->os == OsWindows && host_target->os == OsWindows && + host_target->arch == ZigLLVM_x86_64 && guest_target->arch == ZigLLVM_x86) + { + // 64-bit windows can run 32-bit programs + return true; + } + + return false; +} + +const char *arch_stack_pointer_register_name(ZigLLVM_ArchType arch) { + switch (arch) { + case ZigLLVM_UnknownArch: + zig_unreachable(); + case ZigLLVM_x86: + return "esp"; + case ZigLLVM_x86_64: + return "rsp"; + case ZigLLVM_arm: + case ZigLLVM_armeb: + case ZigLLVM_thumb: + case ZigLLVM_thumbeb: + case ZigLLVM_aarch64: + case ZigLLVM_aarch64_be: + case ZigLLVM_aarch64_32: + case ZigLLVM_riscv32: + case ZigLLVM_riscv64: + case ZigLLVM_mipsel: + case ZigLLVM_ppc: + case ZigLLVM_ppc64: + case ZigLLVM_ppc64le: + return "sp"; + + case ZigLLVM_wasm32: + case ZigLLVM_wasm64: + return nullptr; // known to be not available + + case ZigLLVM_amdgcn: + case ZigLLVM_amdil: + case ZigLLVM_amdil64: + case ZigLLVM_arc: + case ZigLLVM_avr: + case ZigLLVM_bpfeb: + case ZigLLVM_bpfel: + case ZigLLVM_hexagon: + case ZigLLVM_lanai: + case ZigLLVM_hsail: + case ZigLLVM_hsail64: + case ZigLLVM_kalimba: + case ZigLLVM_le32: + case ZigLLVM_le64: + case ZigLLVM_mips: + case ZigLLVM_mips64: + case ZigLLVM_mips64el: + case ZigLLVM_msp430: + case ZigLLVM_nvptx: + case ZigLLVM_nvptx64: + case ZigLLVM_r600: + case ZigLLVM_renderscript32: + case ZigLLVM_renderscript64: + case ZigLLVM_shave: + case ZigLLVM_sparc: + case ZigLLVM_sparcel: + case ZigLLVM_sparcv9: + case ZigLLVM_spir: + case ZigLLVM_spir64: + case ZigLLVM_systemz: + case ZigLLVM_tce: + case ZigLLVM_tcele: + case ZigLLVM_xcore: + case ZigLLVM_ve: + zig_panic("TODO populate this table with stack pointer register name for this CPU architecture"); + } + zig_unreachable(); +} + +bool target_is_arm(const ZigTarget *target) { + switch (target->arch) { + case ZigLLVM_UnknownArch: + zig_unreachable(); + case ZigLLVM_aarch64: + case ZigLLVM_aarch64_be: + case ZigLLVM_aarch64_32: + case ZigLLVM_arm: + case ZigLLVM_armeb: + case ZigLLVM_thumb: + case ZigLLVM_thumbeb: + return true; + + case ZigLLVM_x86: + case ZigLLVM_x86_64: + case ZigLLVM_amdgcn: + case ZigLLVM_amdil: + case ZigLLVM_amdil64: + case ZigLLVM_arc: + case ZigLLVM_avr: + case ZigLLVM_bpfeb: + case ZigLLVM_bpfel: + case ZigLLVM_hexagon: + case ZigLLVM_lanai: + case ZigLLVM_hsail: + case ZigLLVM_hsail64: + case ZigLLVM_kalimba: + case ZigLLVM_le32: + case ZigLLVM_le64: + case ZigLLVM_mips: + case ZigLLVM_mips64: + case ZigLLVM_mips64el: + case ZigLLVM_mipsel: + case ZigLLVM_msp430: + case ZigLLVM_nvptx: + case ZigLLVM_nvptx64: + case ZigLLVM_ppc64le: + case ZigLLVM_r600: + case ZigLLVM_renderscript32: + case ZigLLVM_renderscript64: + case ZigLLVM_riscv32: + case ZigLLVM_riscv64: + case ZigLLVM_shave: + case ZigLLVM_sparc: + case ZigLLVM_sparcel: + case ZigLLVM_sparcv9: + case ZigLLVM_spir: + case ZigLLVM_spir64: + case ZigLLVM_systemz: + case ZigLLVM_tce: + case ZigLLVM_tcele: + case ZigLLVM_wasm32: + case ZigLLVM_wasm64: + case ZigLLVM_xcore: + case ZigLLVM_ppc: + case ZigLLVM_ppc64: + case ZigLLVM_ve: + return false; + } + zig_unreachable(); +} + +// Valgrind supports more, but Zig does not support them yet. +bool target_has_valgrind_support(const ZigTarget *target) { + switch (target->arch) { + case ZigLLVM_UnknownArch: + zig_unreachable(); + case ZigLLVM_x86_64: + return (target->os == OsLinux || target_os_is_darwin(target->os) || target->os == OsSolaris || + (target->os == OsWindows && target->abi != ZigLLVM_MSVC)); + default: + return false; + } + zig_unreachable(); +} + +bool target_os_requires_libc(Os os) { + // On Darwin, we always link libSystem which contains libc. + // Similarly on FreeBSD and NetBSD we always link system libc + // since this is the stable syscall interface. + return (target_os_is_darwin(os) || os == OsFreeBSD || os == OsNetBSD || os == OsDragonFly); +} + +bool target_is_glibc(const ZigTarget *target) { + return target->os == OsLinux && target_abi_is_gnu(target->abi); +} + +bool target_is_musl(const ZigTarget *target) { + return target->os == OsLinux && target_abi_is_musl(target->abi); +} + +bool target_is_wasm(const ZigTarget *target) { + return target->arch == ZigLLVM_wasm32 || target->arch == ZigLLVM_wasm64; +} + +ZigLLVM_EnvironmentType target_default_abi(ZigLLVM_ArchType arch, Os os) { + if (arch == ZigLLVM_wasm32 || arch == ZigLLVM_wasm64) { + return ZigLLVM_Musl; + } + switch (os) { + case OsFreestanding: + case OsAnanas: + case OsCloudABI: + case OsLv2: + case OsSolaris: + case OsHaiku: + case OsMinix: + case OsRTEMS: + case OsNaCl: + case OsCNK: + case OsAIX: + case OsCUDA: + case OsNVCL: + case OsAMDHSA: + case OsPS4: + case OsELFIAMCU: + case OsMesa3D: + case OsContiki: + case OsAMDPAL: + case OsHermitCore: + case OsOther: + return ZigLLVM_EABI; + case OsOpenBSD: + case OsMacOSX: + case OsFreeBSD: + case OsIOS: + case OsTvOS: + case OsWatchOS: + case OsFuchsia: + case OsKFreeBSD: + case OsNetBSD: + case OsDragonFly: + case OsHurd: + return ZigLLVM_GNU; + case OsUefi: + case OsWindows: + return ZigLLVM_MSVC; + case OsLinux: + case OsWASI: + case OsEmscripten: + return ZigLLVM_Musl; + } + zig_unreachable(); +} + +bool target_abi_is_gnu(ZigLLVM_EnvironmentType abi) { + switch (abi) { + case ZigLLVM_GNU: + case ZigLLVM_GNUABIN32: + case ZigLLVM_GNUABI64: + case ZigLLVM_GNUEABI: + case ZigLLVM_GNUEABIHF: + case ZigLLVM_GNUX32: + return true; + default: + return false; + } +} + +bool target_abi_is_musl(ZigLLVM_EnvironmentType abi) { + switch (abi) { + case ZigLLVM_Musl: + case ZigLLVM_MuslEABI: + case ZigLLVM_MuslEABIHF: + return true; + default: + return false; + } +} + +struct AvailableLibC { + ZigLLVM_ArchType arch; + Os os; + ZigLLVM_EnvironmentType abi; +}; + +static const AvailableLibC libcs_available[] = { + {ZigLLVM_aarch64_be, OsLinux, ZigLLVM_GNU}, + {ZigLLVM_aarch64_be, OsLinux, ZigLLVM_Musl}, + {ZigLLVM_aarch64_be, OsWindows, ZigLLVM_GNU}, + {ZigLLVM_aarch64, OsLinux, ZigLLVM_GNU}, + {ZigLLVM_aarch64, OsLinux, ZigLLVM_Musl}, + {ZigLLVM_aarch64, OsWindows, ZigLLVM_GNU}, + {ZigLLVM_armeb, OsLinux, ZigLLVM_GNUEABI}, + {ZigLLVM_armeb, OsLinux, ZigLLVM_GNUEABIHF}, + {ZigLLVM_armeb, OsLinux, ZigLLVM_MuslEABI}, + {ZigLLVM_armeb, OsLinux, ZigLLVM_MuslEABIHF}, + {ZigLLVM_armeb, OsWindows, ZigLLVM_GNU}, + {ZigLLVM_arm, OsLinux, ZigLLVM_GNUEABI}, + {ZigLLVM_arm, OsLinux, ZigLLVM_GNUEABIHF}, + {ZigLLVM_arm, OsLinux, ZigLLVM_MuslEABI}, + {ZigLLVM_arm, OsLinux, ZigLLVM_MuslEABIHF}, + {ZigLLVM_arm, OsWindows, ZigLLVM_GNU}, + {ZigLLVM_x86, OsLinux, ZigLLVM_GNU}, + {ZigLLVM_x86, OsLinux, ZigLLVM_Musl}, + {ZigLLVM_x86, OsWindows, ZigLLVM_GNU}, + {ZigLLVM_mips64el, OsLinux, ZigLLVM_GNUABI64}, + {ZigLLVM_mips64el, OsLinux, ZigLLVM_GNUABIN32}, + {ZigLLVM_mips64el, OsLinux, ZigLLVM_Musl}, + {ZigLLVM_mips64, OsLinux, ZigLLVM_GNUABI64}, + {ZigLLVM_mips64, OsLinux, ZigLLVM_GNUABIN32}, + {ZigLLVM_mips64, OsLinux, ZigLLVM_Musl}, + {ZigLLVM_mipsel, OsLinux, ZigLLVM_GNU}, + {ZigLLVM_mipsel, OsLinux, ZigLLVM_Musl}, + {ZigLLVM_mips, OsLinux, ZigLLVM_GNU}, + {ZigLLVM_mips, OsLinux, ZigLLVM_Musl}, + {ZigLLVM_ppc64le, OsLinux, ZigLLVM_GNU}, + {ZigLLVM_ppc64le, OsLinux, ZigLLVM_Musl}, + {ZigLLVM_ppc64, OsLinux, ZigLLVM_GNU}, + {ZigLLVM_ppc64, OsLinux, ZigLLVM_Musl}, + {ZigLLVM_ppc, OsLinux, ZigLLVM_GNU}, + {ZigLLVM_ppc, OsLinux, ZigLLVM_Musl}, + {ZigLLVM_riscv64, OsLinux, ZigLLVM_GNU}, + {ZigLLVM_riscv64, OsLinux, ZigLLVM_Musl}, + {ZigLLVM_systemz, OsLinux, ZigLLVM_GNU}, + {ZigLLVM_systemz, OsLinux, ZigLLVM_Musl}, + {ZigLLVM_sparc, OsLinux, ZigLLVM_GNU}, + {ZigLLVM_sparcv9, OsLinux, ZigLLVM_GNU}, + {ZigLLVM_wasm32, OsFreestanding, ZigLLVM_Musl}, + {ZigLLVM_x86_64, OsLinux, ZigLLVM_GNU}, + {ZigLLVM_x86_64, OsLinux, ZigLLVM_GNUX32}, + {ZigLLVM_x86_64, OsLinux, ZigLLVM_Musl}, + {ZigLLVM_x86_64, OsWindows, ZigLLVM_GNU}, +}; + +bool target_can_build_libc(const ZigTarget *target) { + for (size_t i = 0; i < array_length(libcs_available); i += 1) { + if (target->arch == libcs_available[i].arch && + target->os == libcs_available[i].os && + target->abi == libcs_available[i].abi) + { + return true; + } + } + return false; +} + +const char *target_libc_generic_name(const ZigTarget *target) { + if (target->os == OsWindows) { + return "mingw"; + } + switch (target->abi) { + case ZigLLVM_GNU: + case ZigLLVM_GNUABIN32: + case ZigLLVM_GNUABI64: + case ZigLLVM_GNUEABI: + case ZigLLVM_GNUEABIHF: + case ZigLLVM_GNUX32: + return "glibc"; + case ZigLLVM_Musl: + case ZigLLVM_MuslEABI: + case ZigLLVM_MuslEABIHF: + case ZigLLVM_UnknownEnvironment: + return "musl"; + case ZigLLVM_CODE16: + case ZigLLVM_EABI: + case ZigLLVM_EABIHF: + case ZigLLVM_Android: + case ZigLLVM_MSVC: + case ZigLLVM_Itanium: + case ZigLLVM_Cygnus: + case ZigLLVM_CoreCLR: + case ZigLLVM_Simulator: + case ZigLLVM_MacABI: + zig_unreachable(); + } + zig_unreachable(); +} + +bool target_is_libc_lib_name(const ZigTarget *target, const char *name) { + auto equal = str_eql_str; + if (target->os == OsMacOSX) + equal = str_eql_str_ignore_case; + + if (equal(name, "c")) + return true; + + if (target_abi_is_gnu(target->abi) && target->os == OsWindows) { + // mingw-w64 + + if (equal(name, "m")) + return true; + + return false; + } + + if (target_abi_is_gnu(target->abi) || target_abi_is_musl(target->abi) || target_os_is_darwin(target->os)) { + if (equal(name, "m")) + return true; + if (equal(name, "rt")) + return true; + if (equal(name, "pthread")) + return true; + if (equal(name, "crypt")) + return true; + if (equal(name, "util")) + return true; + if (equal(name, "xnet")) + return true; + if (equal(name, "resolv")) + return true; + if (equal(name, "dl")) + return true; + if (equal(name, "util")) + return true; + } + + if (target_os_is_darwin(target->os) && equal(name, "System")) + return true; + + return false; +} + +bool target_is_libcpp_lib_name(const ZigTarget *target, const char *name) { + if (strcmp(name, "c++") == 0 || strcmp(name, "c++abi") == 0) + return true; + + return false; +} + +size_t target_libc_count(void) { + return array_length(libcs_available); +} + +void target_libc_enum(size_t index, ZigTarget *out_target) { + assert(index < array_length(libcs_available)); + out_target->arch = libcs_available[index].arch; + out_target->os = libcs_available[index].os; + out_target->abi = libcs_available[index].abi; + out_target->is_native_os = false; + out_target->is_native_cpu = false; +} + +bool target_has_debug_info(const ZigTarget *target) { + return !target_is_wasm(target); +} + +const char *target_arch_musl_name(ZigLLVM_ArchType arch) { + switch (arch) { + case ZigLLVM_aarch64: + case ZigLLVM_aarch64_be: + return "aarch64"; + case ZigLLVM_arm: + case ZigLLVM_armeb: + return "arm"; + case ZigLLVM_mips: + case ZigLLVM_mipsel: + return "mips"; + case ZigLLVM_mips64el: + case ZigLLVM_mips64: + return "mips64"; + case ZigLLVM_ppc: + return "powerpc"; + case ZigLLVM_ppc64: + case ZigLLVM_ppc64le: + return "powerpc64"; + case ZigLLVM_systemz: + return "s390x"; + case ZigLLVM_x86: + return "i386"; + case ZigLLVM_x86_64: + return "x86_64"; + case ZigLLVM_riscv64: + return "riscv64"; + default: + zig_unreachable(); + } +} + +bool target_libc_needs_crti_crtn(const ZigTarget *target) { + if (target->arch == ZigLLVM_riscv32 || target->arch == ZigLLVM_riscv64 || target_is_android(target)) { + return false; + } + return true; +} + +bool target_is_riscv(const ZigTarget *target) { + return target->arch == ZigLLVM_riscv32 || target->arch == ZigLLVM_riscv64; +} + +bool target_is_mips(const ZigTarget *target) { + return target->arch == ZigLLVM_mips || target->arch == ZigLLVM_mipsel || + target->arch == ZigLLVM_mips64 || target->arch == ZigLLVM_mips64el; +} + +bool target_is_ppc(const ZigTarget *target) { + return target->arch == ZigLLVM_ppc || target->arch == ZigLLVM_ppc64 || + target->arch == ZigLLVM_ppc64le; +} + +unsigned target_fn_align(const ZigTarget *target) { + return 16; +} diff --git a/src/stage1/target.hpp b/src/stage1/target.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d34c2aeae50da1897383f6ca09e606e1c42a994c --- /dev/null +++ b/src/stage1/target.hpp @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2016 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef ZIG_TARGET_HPP +#define ZIG_TARGET_HPP + +#include "stage2.h" + +struct Buf; + +enum CIntType { + CIntTypeShort, + CIntTypeUShort, + CIntTypeInt, + CIntTypeUInt, + CIntTypeLong, + CIntTypeULong, + CIntTypeLongLong, + CIntTypeULongLong, + + CIntTypeCount, +}; + +Error target_parse_arch(ZigLLVM_ArchType *arch, const char *arch_ptr, size_t arch_len); +Error target_parse_os(Os *os, const char *os_ptr, size_t os_len); +Error target_parse_abi(ZigLLVM_EnvironmentType *abi, const char *abi_ptr, size_t abi_len); + +size_t target_arch_count(void); +ZigLLVM_ArchType target_arch_enum(size_t index); +const char *target_arch_name(ZigLLVM_ArchType arch); + +const char *arch_stack_pointer_register_name(ZigLLVM_ArchType arch); + +size_t target_vendor_count(void); +ZigLLVM_VendorType target_vendor_enum(size_t index); + +size_t target_os_count(void); +Os target_os_enum(size_t index); +const char *target_os_name(Os os_type); + +size_t target_abi_count(void); +ZigLLVM_EnvironmentType target_abi_enum(size_t index); +const char *target_abi_name(ZigLLVM_EnvironmentType abi); +ZigLLVM_EnvironmentType target_default_abi(ZigLLVM_ArchType arch, Os os); + + +size_t target_oformat_count(void); +ZigLLVM_ObjectFormatType target_oformat_enum(size_t index); +const char *target_oformat_name(ZigLLVM_ObjectFormatType oformat); +ZigLLVM_ObjectFormatType target_object_format(const ZigTarget *target); + +void target_triple_llvm(Buf *triple, const ZigTarget *target); +void target_triple_zig(Buf *triple, const ZigTarget *target); + +void init_all_targets(void); + +void resolve_target_object_format(ZigTarget *target); + +uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id); + +const char *target_o_file_ext(const ZigTarget *target); +const char *target_asm_file_ext(const ZigTarget *target); +const char *target_llvm_ir_file_ext(const ZigTarget *target); + +bool target_can_exec(const ZigTarget *host_target, const ZigTarget *guest_target); +ZigLLVM_OSType get_llvm_os_type(Os os_type); + +bool target_is_arm(const ZigTarget *target); +bool target_is_mips(const ZigTarget *target); +bool target_is_ppc(const ZigTarget *target); +bool target_allows_addr_zero(const ZigTarget *target); +bool target_has_valgrind_support(const ZigTarget *target); +bool target_os_is_darwin(Os os); +bool target_os_requires_libc(Os os); +bool target_can_build_libc(const ZigTarget *target); +const char *target_libc_generic_name(const ZigTarget *target); +bool target_is_libc_lib_name(const ZigTarget *target, const char *name); +bool target_is_libcpp_lib_name(const ZigTarget *target, const char *name); +bool target_abi_is_gnu(ZigLLVM_EnvironmentType abi); +bool target_abi_is_musl(ZigLLVM_EnvironmentType abi); +bool target_is_glibc(const ZigTarget *target); +bool target_is_musl(const ZigTarget *target); +bool target_is_wasm(const ZigTarget *target); +bool target_is_riscv(const ZigTarget *target); +bool target_is_android(const ZigTarget *target); +bool target_has_debug_info(const ZigTarget *target); +const char *target_arch_musl_name(ZigLLVM_ArchType arch); + +uint32_t target_arch_pointer_bit_width(ZigLLVM_ArchType arch); +uint32_t target_arch_largest_atomic_bits(ZigLLVM_ArchType arch); + +size_t target_libc_count(void); +void target_libc_enum(size_t index, ZigTarget *out_target); +bool target_libc_needs_crti_crtn(const ZigTarget *target); + +unsigned target_fn_align(const ZigTarget *target); + +#endif diff --git a/src/stage1/tokenizer.cpp b/src/stage1/tokenizer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fa14dd40fa3420406511719b75051fa8d13ece3d --- /dev/null +++ b/src/stage1/tokenizer.cpp @@ -0,0 +1,1671 @@ +/* + * Copyright (c) 2015 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#include "tokenizer.hpp" +#include "util.hpp" + +#include +#include +#include +#include +#include +#include + +#define WHITESPACE \ + ' ': \ + case '\r': \ + case '\n' + +#define DIGIT_NON_ZERO \ + '1': \ + case '2': \ + case '3': \ + case '4': \ + case '5': \ + case '6': \ + case '7': \ + case '8': \ + case '9' +#define DIGIT \ + '0': \ + case DIGIT_NON_ZERO + +#define ALPHA \ + 'a': \ + case 'b': \ + case 'c': \ + case 'd': \ + case 'e': \ + case 'f': \ + case 'g': \ + case 'h': \ + case 'i': \ + case 'j': \ + case 'k': \ + case 'l': \ + case 'm': \ + case 'n': \ + case 'o': \ + case 'p': \ + case 'q': \ + case 'r': \ + case 's': \ + case 't': \ + case 'u': \ + case 'v': \ + case 'w': \ + case 'x': \ + case 'y': \ + case 'z': \ + case 'A': \ + case 'B': \ + case 'C': \ + case 'D': \ + case 'E': \ + case 'F': \ + case 'G': \ + case 'H': \ + case 'I': \ + case 'J': \ + case 'K': \ + case 'L': \ + case 'M': \ + case 'N': \ + case 'O': \ + case 'P': \ + case 'Q': \ + case 'R': \ + case 'S': \ + case 'T': \ + case 'U': \ + case 'V': \ + case 'W': \ + case 'X': \ + case 'Y': \ + case 'Z' + +#define SYMBOL_CHAR \ + ALPHA: \ + case DIGIT: \ + case '_' + +#define SYMBOL_START \ + ALPHA: \ + case '_' + +struct ZigKeyword { + const char *text; + TokenId token_id; +}; + +static const struct ZigKeyword zig_keywords[] = { + {"align", TokenIdKeywordAlign}, + {"allowzero", TokenIdKeywordAllowZero}, + {"and", TokenIdKeywordAnd}, + {"anyframe", TokenIdKeywordAnyFrame}, + {"anytype", TokenIdKeywordAnyType}, + {"asm", TokenIdKeywordAsm}, + {"async", TokenIdKeywordAsync}, + {"await", TokenIdKeywordAwait}, + {"break", TokenIdKeywordBreak}, + {"callconv", TokenIdKeywordCallconv}, + {"catch", TokenIdKeywordCatch}, + {"comptime", TokenIdKeywordCompTime}, + {"const", TokenIdKeywordConst}, + {"continue", TokenIdKeywordContinue}, + {"defer", TokenIdKeywordDefer}, + {"else", TokenIdKeywordElse}, + {"enum", TokenIdKeywordEnum}, + {"errdefer", TokenIdKeywordErrdefer}, + {"error", TokenIdKeywordError}, + {"export", TokenIdKeywordExport}, + {"extern", TokenIdKeywordExtern}, + {"false", TokenIdKeywordFalse}, + {"fn", TokenIdKeywordFn}, + {"for", TokenIdKeywordFor}, + {"if", TokenIdKeywordIf}, + {"inline", TokenIdKeywordInline}, + {"noalias", TokenIdKeywordNoAlias}, + {"noinline", TokenIdKeywordNoInline}, + {"nosuspend", TokenIdKeywordNoSuspend}, + {"null", TokenIdKeywordNull}, + {"or", TokenIdKeywordOr}, + {"orelse", TokenIdKeywordOrElse}, + {"packed", TokenIdKeywordPacked}, + {"pub", TokenIdKeywordPub}, + {"resume", TokenIdKeywordResume}, + {"return", TokenIdKeywordReturn}, + {"linksection", TokenIdKeywordLinkSection}, + {"struct", TokenIdKeywordStruct}, + {"suspend", TokenIdKeywordSuspend}, + {"switch", TokenIdKeywordSwitch}, + {"test", TokenIdKeywordTest}, + {"threadlocal", TokenIdKeywordThreadLocal}, + {"true", TokenIdKeywordTrue}, + {"try", TokenIdKeywordTry}, + {"undefined", TokenIdKeywordUndefined}, + {"union", TokenIdKeywordUnion}, + {"unreachable", TokenIdKeywordUnreachable}, + {"usingnamespace", TokenIdKeywordUsingNamespace}, + {"var", TokenIdKeywordVar}, + {"volatile", TokenIdKeywordVolatile}, + {"while", TokenIdKeywordWhile}, +}; + +bool is_zig_keyword(Buf *buf) { + for (size_t i = 0; i < array_length(zig_keywords); i += 1) { + if (buf_eql_str(buf, zig_keywords[i].text)) { + return true; + } + } + return false; +} + +static bool is_symbol_char(uint8_t c) { + switch (c) { + case SYMBOL_CHAR: + return true; + default: + return false; + } +} + +enum TokenizeState { + TokenizeStateStart, + TokenizeStateSymbol, + TokenizeStateZero, // "0", which might lead to "0x" + TokenizeStateNumber, // "123", "0x123" + TokenizeStateNumberNoUnderscore, // "12_", "0x12_" next char must be digit + TokenizeStateNumberDot, + TokenizeStateFloatFraction, // "123.456", "0x123.456" + TokenizeStateFloatFractionNoUnderscore, // "123.45_", "0x123.45_" + TokenizeStateFloatExponentUnsigned, // "123.456e", "123e", "0x123p" + TokenizeStateFloatExponentNumber, // "123.456e7", "123.456e+7", "123.456e-7" + TokenizeStateFloatExponentNumberNoUnderscore, // "123.456e7_", "123.456e+7_", "123.456e-7_" + TokenizeStateString, + TokenizeStateStringEscape, + TokenizeStateStringEscapeUnicodeStart, + TokenizeStateCharLiteral, + TokenizeStateCharLiteralEnd, + TokenizeStateCharLiteralUnicode, + TokenizeStateSawStar, + TokenizeStateSawStarPercent, + TokenizeStateSawSlash, + TokenizeStateSawSlash2, + TokenizeStateSawSlash3, + TokenizeStateSawSlashBang, + TokenizeStateSawBackslash, + TokenizeStateSawPercent, + TokenizeStateSawPlus, + TokenizeStateSawPlusPercent, + TokenizeStateSawDash, + TokenizeStateSawMinusPercent, + TokenizeStateSawAmpersand, + TokenizeStateSawCaret, + TokenizeStateSawBar, + TokenizeStateSawBarBar, + TokenizeStateDocComment, + TokenizeStateContainerDocComment, + TokenizeStateLineComment, + TokenizeStateLineString, + TokenizeStateLineStringEnd, + TokenizeStateLineStringContinue, + TokenizeStateSawEq, + TokenizeStateSawBang, + TokenizeStateSawLessThan, + TokenizeStateSawLessThanLessThan, + TokenizeStateSawGreaterThan, + TokenizeStateSawGreaterThanGreaterThan, + TokenizeStateSawDot, + TokenizeStateSawDotDot, + TokenizeStateSawAtSign, + TokenizeStateCharCode, + TokenizeStateError, +}; + + +struct Tokenize { + Buf *buf; + size_t pos; + TokenizeState state; + ZigList *tokens; + int line; + int column; + Token *cur_tok; + Tokenization *out; + uint32_t radix; + bool is_trailing_underscore; + size_t char_code_index; + bool unicode; + uint32_t char_code; + size_t remaining_code_units; +}; + +ATTRIBUTE_PRINTF(2, 3) +static void tokenize_error(Tokenize *t, const char *format, ...) { + t->state = TokenizeStateError; + + t->out->err_line = t->line; + t->out->err_column = t->column; + + va_list ap; + va_start(ap, format); + t->out->err = buf_vprintf(format, ap); + va_end(ap); +} + +static void set_token_id(Tokenize *t, Token *token, TokenId id) { + token->id = id; + + if (id == TokenIdIntLiteral) { + bigint_init_unsigned(&token->data.int_lit.bigint, 0); + } else if (id == TokenIdFloatLiteral) { + bigfloat_init_32(&token->data.float_lit.bigfloat, 0.0f); + token->data.float_lit.overflow = false; + } else if (id == TokenIdStringLiteral || id == TokenIdMultilineStringLiteral || id == TokenIdSymbol) { + memset(&token->data.str_lit.str, 0, sizeof(Buf)); + buf_resize(&token->data.str_lit.str, 0); + } +} + +static void begin_token(Tokenize *t, TokenId id) { + assert(!t->cur_tok); + t->tokens->add_one(); + Token *token = &t->tokens->last(); + token->start_line = t->line; + token->start_column = t->column; + token->start_pos = t->pos; + + set_token_id(t, token, id); + + t->cur_tok = token; +} + +static void cancel_token(Tokenize *t) { + t->tokens->pop(); + t->cur_tok = nullptr; +} + +static void end_float_token(Tokenize *t) { + uint8_t *ptr_buf = (uint8_t*)buf_ptr(t->buf) + t->cur_tok->start_pos; + size_t buf_len = t->cur_tok->end_pos - t->cur_tok->start_pos; + if (bigfloat_init_buf(&t->cur_tok->data.float_lit.bigfloat, ptr_buf, buf_len)) { + t->cur_tok->data.float_lit.overflow = true; + } +} + +static void end_token(Tokenize *t) { + assert(t->cur_tok); + t->cur_tok->end_pos = t->pos + 1; + + if (t->cur_tok->id == TokenIdFloatLiteral) { + end_float_token(t); + } else if (t->cur_tok->id == TokenIdSymbol) { + char *token_mem = buf_ptr(t->buf) + t->cur_tok->start_pos; + int token_len = (int)(t->cur_tok->end_pos - t->cur_tok->start_pos); + + for (size_t i = 0; i < array_length(zig_keywords); i += 1) { + if (mem_eql_str(token_mem, token_len, zig_keywords[i].text)) { + t->cur_tok->id = zig_keywords[i].token_id; + break; + } + } + } + + t->cur_tok = nullptr; +} + +static bool is_exponent_signifier(uint8_t c, int radix) { + if (radix == 16) { + return c == 'p' || c == 'P'; + } else { + return c == 'e' || c == 'E'; + } +} + +static uint32_t get_digit_value(uint8_t c) { + if ('0' <= c && c <= '9') { + return c - '0'; + } + if ('A' <= c && c <= 'Z') { + return c - 'A' + 10; + } + if ('a' <= c && c <= 'z') { + return c - 'a' + 10; + } + return UINT32_MAX; +} + +static void handle_string_escape(Tokenize *t, uint8_t c) { + if (t->cur_tok->id == TokenIdCharLiteral) { + t->cur_tok->data.char_lit.c = c; + t->state = TokenizeStateCharLiteralEnd; + } else if (t->cur_tok->id == TokenIdStringLiteral || t->cur_tok->id == TokenIdSymbol) { + buf_append_char(&t->cur_tok->data.str_lit.str, c); + t->state = TokenizeStateString; + } else { + zig_unreachable(); + } +} + +static const char* get_escape_shorthand(uint8_t c) { + switch (c) { + case '\0': + return "\\0"; + case '\a': + return "\\a"; + case '\b': + return "\\b"; + case '\t': + return "\\t"; + case '\n': + return "\\n"; + case '\v': + return "\\v"; + case '\f': + return "\\f"; + case '\r': + return "\\r"; + default: + return nullptr; + } +} + +static void invalid_char_error(Tokenize *t, uint8_t c) { + if (c == '\r') { + tokenize_error(t, "invalid carriage return, only '\\n' line endings are supported"); + return; + } + + const char *sh = get_escape_shorthand(c); + if (sh) { + tokenize_error(t, "invalid character: '%s'", sh); + return; + } + + if (isprint(c)) { + tokenize_error(t, "invalid character: '%c'", c); + return; + } + + tokenize_error(t, "invalid character: '\\x%02x'", c); +} + +void tokenize(Buf *buf, Tokenization *out) { + Tokenize t = {0}; + t.out = out; + t.tokens = out->tokens = heap::c_allocator.create>(); + t.buf = buf; + + out->line_offsets = heap::c_allocator.create>(); + out->line_offsets->append(0); + + // Skip the UTF-8 BOM if present + if (buf_starts_with_mem(buf, "\xEF\xBB\xBF", 3)) { + t.pos += 3; + } + + for (; t.pos < buf_len(t.buf); t.pos += 1) { + uint8_t c = buf_ptr(t.buf)[t.pos]; + switch (t.state) { + case TokenizeStateError: + break; + case TokenizeStateStart: + switch (c) { + case WHITESPACE: + break; + case ALPHA: + case '_': + t.state = TokenizeStateSymbol; + begin_token(&t, TokenIdSymbol); + buf_append_char(&t.cur_tok->data.str_lit.str, c); + break; + case '0': + t.state = TokenizeStateZero; + begin_token(&t, TokenIdIntLiteral); + t.is_trailing_underscore = false; + t.radix = 10; + bigint_init_unsigned(&t.cur_tok->data.int_lit.bigint, 0); + break; + case DIGIT_NON_ZERO: + t.state = TokenizeStateNumber; + begin_token(&t, TokenIdIntLiteral); + t.is_trailing_underscore = false; + t.radix = 10; + bigint_init_unsigned(&t.cur_tok->data.int_lit.bigint, get_digit_value(c)); + break; + case '"': + begin_token(&t, TokenIdStringLiteral); + t.state = TokenizeStateString; + break; + case '\'': + begin_token(&t, TokenIdCharLiteral); + t.state = TokenizeStateCharLiteral; + break; + case '(': + begin_token(&t, TokenIdLParen); + end_token(&t); + break; + case ')': + begin_token(&t, TokenIdRParen); + end_token(&t); + break; + case ',': + begin_token(&t, TokenIdComma); + end_token(&t); + break; + case '?': + begin_token(&t, TokenIdQuestion); + end_token(&t); + break; + case '{': + begin_token(&t, TokenIdLBrace); + end_token(&t); + break; + case '}': + begin_token(&t, TokenIdRBrace); + end_token(&t); + break; + case '[': + begin_token(&t, TokenIdLBracket); + end_token(&t); + break; + case ']': + begin_token(&t, TokenIdRBracket); + end_token(&t); + break; + case ';': + begin_token(&t, TokenIdSemicolon); + end_token(&t); + break; + case ':': + begin_token(&t, TokenIdColon); + end_token(&t); + break; + case '#': + begin_token(&t, TokenIdNumberSign); + end_token(&t); + break; + case '*': + begin_token(&t, TokenIdStar); + t.state = TokenizeStateSawStar; + break; + case '/': + begin_token(&t, TokenIdSlash); + t.state = TokenizeStateSawSlash; + break; + case '\\': + begin_token(&t, TokenIdMultilineStringLiteral); + t.state = TokenizeStateSawBackslash; + break; + case '%': + begin_token(&t, TokenIdPercent); + t.state = TokenizeStateSawPercent; + break; + case '+': + begin_token(&t, TokenIdPlus); + t.state = TokenizeStateSawPlus; + break; + case '~': + begin_token(&t, TokenIdTilde); + end_token(&t); + break; + case '@': + begin_token(&t, TokenIdAtSign); + t.state = TokenizeStateSawAtSign; + break; + case '-': + begin_token(&t, TokenIdDash); + t.state = TokenizeStateSawDash; + break; + case '&': + begin_token(&t, TokenIdAmpersand); + t.state = TokenizeStateSawAmpersand; + break; + case '^': + begin_token(&t, TokenIdBinXor); + t.state = TokenizeStateSawCaret; + break; + case '|': + begin_token(&t, TokenIdBinOr); + t.state = TokenizeStateSawBar; + break; + case '=': + begin_token(&t, TokenIdEq); + t.state = TokenizeStateSawEq; + break; + case '!': + begin_token(&t, TokenIdBang); + t.state = TokenizeStateSawBang; + break; + case '<': + begin_token(&t, TokenIdCmpLessThan); + t.state = TokenizeStateSawLessThan; + break; + case '>': + begin_token(&t, TokenIdCmpGreaterThan); + t.state = TokenizeStateSawGreaterThan; + break; + case '.': + begin_token(&t, TokenIdDot); + t.state = TokenizeStateSawDot; + break; + default: + invalid_char_error(&t, c); + } + break; + case TokenizeStateSawDot: + switch (c) { + case '.': + t.state = TokenizeStateSawDotDot; + set_token_id(&t, t.cur_tok, TokenIdEllipsis2); + break; + case '*': + t.state = TokenizeStateStart; + set_token_id(&t, t.cur_tok, TokenIdDotStar); + end_token(&t); + break; + default: + t.pos -= 1; + end_token(&t); + t.state = TokenizeStateStart; + continue; + } + break; + case TokenizeStateSawDotDot: + switch (c) { + case '.': + t.state = TokenizeStateStart; + set_token_id(&t, t.cur_tok, TokenIdEllipsis3); + end_token(&t); + break; + default: + t.pos -= 1; + end_token(&t); + t.state = TokenizeStateStart; + continue; + } + break; + case TokenizeStateSawGreaterThan: + switch (c) { + case '=': + set_token_id(&t, t.cur_tok, TokenIdCmpGreaterOrEq); + end_token(&t); + t.state = TokenizeStateStart; + break; + case '>': + set_token_id(&t, t.cur_tok, TokenIdBitShiftRight); + t.state = TokenizeStateSawGreaterThanGreaterThan; + break; + default: + t.pos -= 1; + end_token(&t); + t.state = TokenizeStateStart; + continue; + } + break; + case TokenizeStateSawGreaterThanGreaterThan: + switch (c) { + case '=': + set_token_id(&t, t.cur_tok, TokenIdBitShiftRightEq); + end_token(&t); + t.state = TokenizeStateStart; + break; + default: + t.pos -= 1; + end_token(&t); + t.state = TokenizeStateStart; + continue; + } + break; + case TokenizeStateSawLessThan: + switch (c) { + case '=': + set_token_id(&t, t.cur_tok, TokenIdCmpLessOrEq); + end_token(&t); + t.state = TokenizeStateStart; + break; + case '<': + set_token_id(&t, t.cur_tok, TokenIdBitShiftLeft); + t.state = TokenizeStateSawLessThanLessThan; + break; + default: + t.pos -= 1; + end_token(&t); + t.state = TokenizeStateStart; + continue; + } + break; + case TokenizeStateSawLessThanLessThan: + switch (c) { + case '=': + set_token_id(&t, t.cur_tok, TokenIdBitShiftLeftEq); + end_token(&t); + t.state = TokenizeStateStart; + break; + default: + t.pos -= 1; + end_token(&t); + t.state = TokenizeStateStart; + continue; + } + break; + case TokenizeStateSawBang: + switch (c) { + case '=': + set_token_id(&t, t.cur_tok, TokenIdCmpNotEq); + end_token(&t); + t.state = TokenizeStateStart; + break; + default: + t.pos -= 1; + end_token(&t); + t.state = TokenizeStateStart; + continue; + } + break; + case TokenizeStateSawEq: + switch (c) { + case '=': + set_token_id(&t, t.cur_tok, TokenIdCmpEq); + end_token(&t); + t.state = TokenizeStateStart; + break; + case '>': + set_token_id(&t, t.cur_tok, TokenIdFatArrow); + end_token(&t); + t.state = TokenizeStateStart; + break; + default: + t.pos -= 1; + end_token(&t); + t.state = TokenizeStateStart; + continue; + } + break; + case TokenizeStateSawStar: + switch (c) { + case '=': + set_token_id(&t, t.cur_tok, TokenIdTimesEq); + end_token(&t); + t.state = TokenizeStateStart; + break; + case '*': + set_token_id(&t, t.cur_tok, TokenIdStarStar); + end_token(&t); + t.state = TokenizeStateStart; + break; + case '%': + set_token_id(&t, t.cur_tok, TokenIdTimesPercent); + t.state = TokenizeStateSawStarPercent; + break; + default: + t.pos -= 1; + end_token(&t); + t.state = TokenizeStateStart; + continue; + } + break; + case TokenizeStateSawStarPercent: + switch (c) { + case '=': + set_token_id(&t, t.cur_tok, TokenIdTimesPercentEq); + end_token(&t); + t.state = TokenizeStateStart; + break; + default: + t.pos -= 1; + end_token(&t); + t.state = TokenizeStateStart; + continue; + } + break; + case TokenizeStateSawPercent: + switch (c) { + case '=': + set_token_id(&t, t.cur_tok, TokenIdModEq); + end_token(&t); + t.state = TokenizeStateStart; + break; + case '.': + set_token_id(&t, t.cur_tok, TokenIdPercentDot); + end_token(&t); + t.state = TokenizeStateStart; + break; + default: + t.pos -= 1; + end_token(&t); + t.state = TokenizeStateStart; + continue; + } + break; + case TokenizeStateSawPlus: + switch (c) { + case '=': + set_token_id(&t, t.cur_tok, TokenIdPlusEq); + end_token(&t); + t.state = TokenizeStateStart; + break; + case '+': + set_token_id(&t, t.cur_tok, TokenIdPlusPlus); + end_token(&t); + t.state = TokenizeStateStart; + break; + case '%': + set_token_id(&t, t.cur_tok, TokenIdPlusPercent); + t.state = TokenizeStateSawPlusPercent; + break; + default: + t.pos -= 1; + end_token(&t); + t.state = TokenizeStateStart; + continue; + } + break; + case TokenizeStateSawPlusPercent: + switch (c) { + case '=': + set_token_id(&t, t.cur_tok, TokenIdPlusPercentEq); + end_token(&t); + t.state = TokenizeStateStart; + break; + default: + t.pos -= 1; + end_token(&t); + t.state = TokenizeStateStart; + continue; + } + break; + case TokenizeStateSawAmpersand: + switch (c) { + case '&': + tokenize_error(&t, "`&&` is invalid. Note that `and` is boolean AND"); + break; + case '=': + set_token_id(&t, t.cur_tok, TokenIdBitAndEq); + end_token(&t); + t.state = TokenizeStateStart; + break; + default: + t.pos -= 1; + end_token(&t); + t.state = TokenizeStateStart; + continue; + } + break; + case TokenizeStateSawCaret: + switch (c) { + case '=': + set_token_id(&t, t.cur_tok, TokenIdBitXorEq); + end_token(&t); + t.state = TokenizeStateStart; + break; + default: + t.pos -= 1; + end_token(&t); + t.state = TokenizeStateStart; + continue; + } + break; + case TokenizeStateSawBar: + switch (c) { + case '=': + set_token_id(&t, t.cur_tok, TokenIdBitOrEq); + end_token(&t); + t.state = TokenizeStateStart; + break; + case '|': + set_token_id(&t, t.cur_tok, TokenIdBarBar); + t.state = TokenizeStateSawBarBar; + break; + default: + t.pos -= 1; + end_token(&t); + t.state = TokenizeStateStart; + continue; + } + break; + case TokenizeStateSawBarBar: + switch (c) { + case '=': + set_token_id(&t, t.cur_tok, TokenIdBarBarEq); + end_token(&t); + t.state = TokenizeStateStart; + break; + default: + t.pos -= 1; + end_token(&t); + t.state = TokenizeStateStart; + continue; + } + break; + case TokenizeStateSawSlash: + switch (c) { + case '/': + t.state = TokenizeStateSawSlash2; + break; + case '=': + set_token_id(&t, t.cur_tok, TokenIdDivEq); + end_token(&t); + t.state = TokenizeStateStart; + break; + default: + t.pos -= 1; + end_token(&t); + t.state = TokenizeStateStart; + continue; + } + break; + case TokenizeStateSawSlash2: + switch (c) { + case '/': + t.state = TokenizeStateSawSlash3; + break; + case '!': + t.state = TokenizeStateSawSlashBang; + break; + case '\n': + cancel_token(&t); + t.state = TokenizeStateStart; + break; + default: + cancel_token(&t); + t.state = TokenizeStateLineComment; + break; + } + break; + case TokenizeStateSawSlash3: + switch (c) { + case '/': + cancel_token(&t); + t.state = TokenizeStateLineComment; + break; + case '\n': + set_token_id(&t, t.cur_tok, TokenIdDocComment); + end_token(&t); + t.state = TokenizeStateStart; + break; + default: + set_token_id(&t, t.cur_tok, TokenIdDocComment); + t.state = TokenizeStateDocComment; + break; + } + break; + case TokenizeStateSawSlashBang: + switch (c) { + case '\n': + set_token_id(&t, t.cur_tok, TokenIdContainerDocComment); + end_token(&t); + t.state = TokenizeStateStart; + break; + default: + set_token_id(&t, t.cur_tok, TokenIdContainerDocComment); + t.state = TokenizeStateContainerDocComment; + break; + } + break; + case TokenizeStateSawBackslash: + switch (c) { + case '\\': + t.state = TokenizeStateLineString; + break; + default: + invalid_char_error(&t, c); + break; + } + break; + case TokenizeStateLineString: + switch (c) { + case '\n': + t.state = TokenizeStateLineStringEnd; + break; + default: + buf_append_char(&t.cur_tok->data.str_lit.str, c); + break; + } + break; + case TokenizeStateLineStringEnd: + switch (c) { + case WHITESPACE: + break; + case '\\': + t.state = TokenizeStateLineStringContinue; + break; + default: + t.pos -= 1; + end_token(&t); + t.state = TokenizeStateStart; + continue; + } + break; + case TokenizeStateLineStringContinue: + switch (c) { + case '\\': + t.state = TokenizeStateLineString; + buf_append_char(&t.cur_tok->data.str_lit.str, '\n'); + break; + default: + invalid_char_error(&t, c); + break; + } + break; + case TokenizeStateLineComment: + switch (c) { + case '\n': + t.state = TokenizeStateStart; + break; + default: + // do nothing + break; + } + break; + case TokenizeStateDocComment: + switch (c) { + case '\n': + end_token(&t); + t.state = TokenizeStateStart; + break; + default: + // do nothing + break; + } + break; + case TokenizeStateContainerDocComment: + switch (c) { + case '\n': + end_token(&t); + t.state = TokenizeStateStart; + break; + default: + // do nothing + break; + } + break; + case TokenizeStateSawAtSign: + switch (c) { + case '"': + set_token_id(&t, t.cur_tok, TokenIdSymbol); + t.state = TokenizeStateString; + break; + default: + t.pos -= 1; + end_token(&t); + t.state = TokenizeStateStart; + continue; + } + break; + case TokenizeStateSymbol: + switch (c) { + case SYMBOL_CHAR: + buf_append_char(&t.cur_tok->data.str_lit.str, c); + break; + default: + t.pos -= 1; + end_token(&t); + t.state = TokenizeStateStart; + continue; + } + break; + case TokenizeStateString: + switch (c) { + case '"': + end_token(&t); + t.state = TokenizeStateStart; + break; + case '\n': + tokenize_error(&t, "newline not allowed in string literal"); + break; + case '\\': + t.state = TokenizeStateStringEscape; + break; + default: + buf_append_char(&t.cur_tok->data.str_lit.str, c); + break; + } + break; + case TokenizeStateStringEscape: + switch (c) { + case 'x': + t.state = TokenizeStateCharCode; + t.radix = 16; + t.char_code = 0; + t.char_code_index = 0; + t.unicode = false; + break; + case 'u': + t.state = TokenizeStateStringEscapeUnicodeStart; + break; + case 'n': + handle_string_escape(&t, '\n'); + break; + case 'r': + handle_string_escape(&t, '\r'); + break; + case '\\': + handle_string_escape(&t, '\\'); + break; + case 't': + handle_string_escape(&t, '\t'); + break; + case '\'': + handle_string_escape(&t, '\''); + break; + case '"': + handle_string_escape(&t, '\"'); + break; + default: + invalid_char_error(&t, c); + } + break; + case TokenizeStateStringEscapeUnicodeStart: + switch (c) { + case '{': + t.state = TokenizeStateCharCode; + t.radix = 16; + t.char_code = 0; + t.char_code_index = 0; + t.unicode = true; + break; + default: + invalid_char_error(&t, c); + } + break; + case TokenizeStateCharCode: + { + if (t.unicode && c == '}') { + if (t.char_code_index == 0) { + tokenize_error(&t, "empty unicode escape sequence"); + break; + } + if (t.char_code > 0x10ffff) { + tokenize_error(&t, "unicode value out of range: %x", t.char_code); + break; + } + if (t.cur_tok->id == TokenIdCharLiteral) { + t.cur_tok->data.char_lit.c = t.char_code; + t.state = TokenizeStateCharLiteralEnd; + } else if (t.char_code <= 0x7f) { + // 00000000 00000000 00000000 0xxxxxxx + handle_string_escape(&t, (uint8_t)t.char_code); + } else if (t.char_code <= 0x7ff) { + // 00000000 00000000 00000xxx xx000000 + handle_string_escape(&t, (uint8_t)(0xc0 | (t.char_code >> 6))); + // 00000000 00000000 00000000 00xxxxxx + handle_string_escape(&t, (uint8_t)(0x80 | (t.char_code & 0x3f))); + } else if (t.char_code <= 0xffff) { + // 00000000 00000000 xxxx0000 00000000 + handle_string_escape(&t, (uint8_t)(0xe0 | (t.char_code >> 12))); + // 00000000 00000000 0000xxxx xx000000 + handle_string_escape(&t, (uint8_t)(0x80 | ((t.char_code >> 6) & 0x3f))); + // 00000000 00000000 00000000 00xxxxxx + handle_string_escape(&t, (uint8_t)(0x80 | (t.char_code & 0x3f))); + } else if (t.char_code <= 0x10ffff) { + // 00000000 000xxx00 00000000 00000000 + handle_string_escape(&t, (uint8_t)(0xf0 | (t.char_code >> 18))); + // 00000000 000000xx xxxx0000 00000000 + handle_string_escape(&t, (uint8_t)(0x80 | ((t.char_code >> 12) & 0x3f))); + // 00000000 00000000 0000xxxx xx000000 + handle_string_escape(&t, (uint8_t)(0x80 | ((t.char_code >> 6) & 0x3f))); + // 00000000 00000000 00000000 00xxxxxx + handle_string_escape(&t, (uint8_t)(0x80 | (t.char_code & 0x3f))); + } else { + zig_unreachable(); + } + break; + } + + uint32_t digit_value = get_digit_value(c); + if (digit_value >= t.radix) { + tokenize_error(&t, "invalid digit: '%c'", c); + break; + } + t.char_code *= t.radix; + t.char_code += digit_value; + t.char_code_index += 1; + + if (!t.unicode && t.char_code_index >= 2) { + assert(t.char_code <= 255); + handle_string_escape(&t, (uint8_t)t.char_code); + } + } + break; + case TokenizeStateCharLiteral: + if (c == '\'') { + tokenize_error(&t, "expected character"); + } else if (c == '\\') { + t.state = TokenizeStateStringEscape; + } else if ((c >= 0x80 && c <= 0xbf) || c >= 0xf8) { + // 10xxxxxx + // 11111xxx + invalid_char_error(&t, c); + } else if (c >= 0xc0 && c <= 0xdf) { + // 110xxxxx + t.cur_tok->data.char_lit.c = c & 0x1f; + t.remaining_code_units = 1; + t.state = TokenizeStateCharLiteralUnicode; + } else if (c >= 0xe0 && c <= 0xef) { + // 1110xxxx + t.cur_tok->data.char_lit.c = c & 0x0f; + t.remaining_code_units = 2; + t.state = TokenizeStateCharLiteralUnicode; + } else if (c >= 0xf0 && c <= 0xf7) { + // 11110xxx + t.cur_tok->data.char_lit.c = c & 0x07; + t.remaining_code_units = 3; + t.state = TokenizeStateCharLiteralUnicode; + } else { + t.cur_tok->data.char_lit.c = c; + t.state = TokenizeStateCharLiteralEnd; + } + break; + case TokenizeStateCharLiteralEnd: + switch (c) { + case '\'': + end_token(&t); + t.state = TokenizeStateStart; + break; + default: + invalid_char_error(&t, c); + } + break; + case TokenizeStateCharLiteralUnicode: + if (c <= 0x7f || c >= 0xc0) { + invalid_char_error(&t, c); + } + t.cur_tok->data.char_lit.c <<= 6; + t.cur_tok->data.char_lit.c += c & 0x3f; + t.remaining_code_units--; + if (t.remaining_code_units == 0) { + t.state = TokenizeStateCharLiteralEnd; + } + break; + case TokenizeStateZero: + switch (c) { + case 'b': + t.radix = 2; + t.state = TokenizeStateNumberNoUnderscore; + break; + case 'o': + t.radix = 8; + t.state = TokenizeStateNumberNoUnderscore; + break; + case 'x': + t.radix = 16; + t.state = TokenizeStateNumberNoUnderscore; + break; + default: + // reinterpret as normal number + t.pos -= 1; + t.state = TokenizeStateNumber; + continue; + } + break; + case TokenizeStateNumberNoUnderscore: + if (c == '_') { + invalid_char_error(&t, c); + break; + } else if (get_digit_value(c) < t.radix) { + t.is_trailing_underscore = false; + t.state = TokenizeStateNumber; + } + ZIG_FALLTHROUGH; + case TokenizeStateNumber: + { + if (c == '_') { + t.is_trailing_underscore = true; + t.state = TokenizeStateNumberNoUnderscore; + break; + } + if (c == '.') { + if (t.is_trailing_underscore) { + invalid_char_error(&t, c); + break; + } + t.state = TokenizeStateNumberDot; + break; + } + if (is_exponent_signifier(c, t.radix)) { + if (t.is_trailing_underscore) { + invalid_char_error(&t, c); + break; + } + if (t.radix != 16 && t.radix != 10) { + invalid_char_error(&t, c); + } + t.state = TokenizeStateFloatExponentUnsigned; + t.radix = 10; // exponent is always base 10 + assert(t.cur_tok->id == TokenIdIntLiteral); + set_token_id(&t, t.cur_tok, TokenIdFloatLiteral); + break; + } + uint32_t digit_value = get_digit_value(c); + if (digit_value >= t.radix) { + if (t.is_trailing_underscore) { + invalid_char_error(&t, c); + break; + } + + if (is_symbol_char(c)) { + invalid_char_error(&t, c); + } + // not my char + t.pos -= 1; + end_token(&t); + t.state = TokenizeStateStart; + continue; + } + BigInt digit_value_bi; + bigint_init_unsigned(&digit_value_bi, digit_value); + + BigInt radix_bi; + bigint_init_unsigned(&radix_bi, t.radix); + + BigInt multiplied; + bigint_mul(&multiplied, &t.cur_tok->data.int_lit.bigint, &radix_bi); + + bigint_add(&t.cur_tok->data.int_lit.bigint, &multiplied, &digit_value_bi); + break; + } + case TokenizeStateNumberDot: + { + if (c == '.') { + t.pos -= 2; + end_token(&t); + t.state = TokenizeStateStart; + continue; + } + if (t.radix != 16 && t.radix != 10) { + invalid_char_error(&t, c); + } + t.pos -= 1; + t.state = TokenizeStateFloatFractionNoUnderscore; + assert(t.cur_tok->id == TokenIdIntLiteral); + set_token_id(&t, t.cur_tok, TokenIdFloatLiteral); + continue; + } + case TokenizeStateFloatFractionNoUnderscore: + if (c == '_') { + invalid_char_error(&t, c); + } else if (get_digit_value(c) < t.radix) { + t.is_trailing_underscore = false; + t.state = TokenizeStateFloatFraction; + } + ZIG_FALLTHROUGH; + case TokenizeStateFloatFraction: + { + if (c == '_') { + t.is_trailing_underscore = true; + t.state = TokenizeStateFloatFractionNoUnderscore; + break; + } + if (is_exponent_signifier(c, t.radix)) { + if (t.is_trailing_underscore) { + invalid_char_error(&t, c); + break; + } + t.state = TokenizeStateFloatExponentUnsigned; + t.radix = 10; // exponent is always base 10 + break; + } + uint32_t digit_value = get_digit_value(c); + if (digit_value >= t.radix) { + if (t.is_trailing_underscore) { + invalid_char_error(&t, c); + break; + } + if (is_symbol_char(c)) { + invalid_char_error(&t, c); + } + // not my char + t.pos -= 1; + end_token(&t); + t.state = TokenizeStateStart; + continue; + } + + // we use parse_f128 to generate the float literal, so just + // need to get to the end of the token + } + break; + case TokenizeStateFloatExponentUnsigned: + switch (c) { + case '+': + t.state = TokenizeStateFloatExponentNumberNoUnderscore; + break; + case '-': + t.state = TokenizeStateFloatExponentNumberNoUnderscore; + break; + default: + // reinterpret as normal exponent number + t.pos -= 1; + t.state = TokenizeStateFloatExponentNumberNoUnderscore; + continue; + } + break; + case TokenizeStateFloatExponentNumberNoUnderscore: + if (c == '_') { + invalid_char_error(&t, c); + } else if (get_digit_value(c) < t.radix) { + t.is_trailing_underscore = false; + t.state = TokenizeStateFloatExponentNumber; + } + ZIG_FALLTHROUGH; + case TokenizeStateFloatExponentNumber: + { + if (c == '_') { + t.is_trailing_underscore = true; + t.state = TokenizeStateFloatExponentNumberNoUnderscore; + break; + } + uint32_t digit_value = get_digit_value(c); + if (digit_value >= t.radix) { + if (t.is_trailing_underscore) { + invalid_char_error(&t, c); + break; + } + if (is_symbol_char(c)) { + invalid_char_error(&t, c); + } + // not my char + t.pos -= 1; + end_token(&t); + t.state = TokenizeStateStart; + continue; + } + + // we use parse_f128 to generate the float literal, so just + // need to get to the end of the token + } + break; + case TokenizeStateSawDash: + switch (c) { + case '>': + set_token_id(&t, t.cur_tok, TokenIdArrow); + end_token(&t); + t.state = TokenizeStateStart; + break; + case '=': + set_token_id(&t, t.cur_tok, TokenIdMinusEq); + end_token(&t); + t.state = TokenizeStateStart; + break; + case '%': + set_token_id(&t, t.cur_tok, TokenIdMinusPercent); + t.state = TokenizeStateSawMinusPercent; + break; + default: + t.pos -= 1; + end_token(&t); + t.state = TokenizeStateStart; + continue; + } + break; + case TokenizeStateSawMinusPercent: + switch (c) { + case '=': + set_token_id(&t, t.cur_tok, TokenIdMinusPercentEq); + end_token(&t); + t.state = TokenizeStateStart; + break; + default: + t.pos -= 1; + end_token(&t); + t.state = TokenizeStateStart; + continue; + } + break; + } + if (c == '\n') { + out->line_offsets->append(t.pos + 1); + t.line += 1; + t.column = 0; + } else { + t.column += 1; + } + } + // EOF + switch (t.state) { + case TokenizeStateStart: + case TokenizeStateError: + break; + case TokenizeStateNumberNoUnderscore: + case TokenizeStateFloatFractionNoUnderscore: + case TokenizeStateFloatExponentNumberNoUnderscore: + case TokenizeStateNumberDot: + tokenize_error(&t, "unterminated number literal"); + break; + case TokenizeStateString: + tokenize_error(&t, "unterminated string"); + break; + case TokenizeStateStringEscape: + case TokenizeStateStringEscapeUnicodeStart: + case TokenizeStateCharCode: + if (t.cur_tok->id == TokenIdStringLiteral) { + tokenize_error(&t, "unterminated string"); + break; + } else if (t.cur_tok->id == TokenIdCharLiteral) { + tokenize_error(&t, "unterminated character literal"); + break; + } else { + zig_unreachable(); + } + break; + case TokenizeStateCharLiteral: + case TokenizeStateCharLiteralEnd: + case TokenizeStateCharLiteralUnicode: + tokenize_error(&t, "unterminated character literal"); + break; + case TokenizeStateSymbol: + case TokenizeStateZero: + case TokenizeStateNumber: + case TokenizeStateFloatFraction: + case TokenizeStateFloatExponentUnsigned: + case TokenizeStateFloatExponentNumber: + case TokenizeStateSawStar: + case TokenizeStateSawSlash: + case TokenizeStateSawPercent: + case TokenizeStateSawPlus: + case TokenizeStateSawDash: + case TokenizeStateSawAmpersand: + case TokenizeStateSawCaret: + case TokenizeStateSawBar: + case TokenizeStateSawEq: + case TokenizeStateSawBang: + case TokenizeStateSawLessThan: + case TokenizeStateSawLessThanLessThan: + case TokenizeStateSawGreaterThan: + case TokenizeStateSawGreaterThanGreaterThan: + case TokenizeStateSawDot: + case TokenizeStateSawAtSign: + case TokenizeStateSawStarPercent: + case TokenizeStateSawPlusPercent: + case TokenizeStateSawMinusPercent: + case TokenizeStateLineString: + case TokenizeStateLineStringEnd: + case TokenizeStateSawBarBar: + case TokenizeStateDocComment: + case TokenizeStateContainerDocComment: + end_token(&t); + break; + case TokenizeStateSawDotDot: + case TokenizeStateSawBackslash: + case TokenizeStateLineStringContinue: + tokenize_error(&t, "unexpected EOF"); + break; + case TokenizeStateLineComment: + break; + case TokenizeStateSawSlash2: + cancel_token(&t); + break; + case TokenizeStateSawSlash3: + set_token_id(&t, t.cur_tok, TokenIdDocComment); + end_token(&t); + break; + case TokenizeStateSawSlashBang: + set_token_id(&t, t.cur_tok, TokenIdContainerDocComment); + end_token(&t); + break; + } + if (t.state != TokenizeStateError) { + if (t.tokens->length > 0) { + Token *last_token = &t.tokens->last(); + t.line = (int)last_token->start_line; + t.column = (int)last_token->start_column; + t.pos = last_token->start_pos; + } else { + t.pos = 0; + } + begin_token(&t, TokenIdEof); + end_token(&t); + assert(!t.cur_tok); + } +} + +const char * token_name(TokenId id) { + switch (id) { + case TokenIdAmpersand: return "&"; + case TokenIdArrow: return "->"; + case TokenIdAtSign: return "@"; + case TokenIdBang: return "!"; + case TokenIdBarBar: return "||"; + case TokenIdBinOr: return "|"; + case TokenIdBinXor: return "^"; + case TokenIdBitAndEq: return "&="; + case TokenIdBitOrEq: return "|="; + case TokenIdBitShiftLeft: return "<<"; + case TokenIdBitShiftLeftEq: return "<<="; + case TokenIdBitShiftRight: return ">>"; + case TokenIdBitShiftRightEq: return ">>="; + case TokenIdBitXorEq: return "^="; + case TokenIdCharLiteral: return "CharLiteral"; + case TokenIdCmpEq: return "=="; + case TokenIdCmpGreaterOrEq: return ">="; + case TokenIdCmpGreaterThan: return ">"; + case TokenIdCmpLessOrEq: return "<="; + case TokenIdCmpLessThan: return "<"; + case TokenIdCmpNotEq: return "!="; + case TokenIdColon: return ":"; + case TokenIdComma: return ","; + case TokenIdDash: return "-"; + case TokenIdDivEq: return "/="; + case TokenIdDocComment: return "DocComment"; + case TokenIdContainerDocComment: return "ContainerDocComment"; + case TokenIdDot: return "."; + case TokenIdDotStar: return ".*"; + case TokenIdEllipsis2: return ".."; + case TokenIdEllipsis3: return "..."; + case TokenIdEof: return "EOF"; + case TokenIdEq: return "="; + case TokenIdFatArrow: return "=>"; + case TokenIdFloatLiteral: return "FloatLiteral"; + case TokenIdIntLiteral: return "IntLiteral"; + case TokenIdKeywordAsync: return "async"; + case TokenIdKeywordAllowZero: return "allowzero"; + case TokenIdKeywordAwait: return "await"; + case TokenIdKeywordResume: return "resume"; + case TokenIdKeywordSuspend: return "suspend"; + case TokenIdKeywordAlign: return "align"; + case TokenIdKeywordAnd: return "and"; + case TokenIdKeywordAnyFrame: return "anyframe"; + case TokenIdKeywordAnyType: return "anytype"; + case TokenIdKeywordAsm: return "asm"; + case TokenIdKeywordBreak: return "break"; + case TokenIdKeywordCatch: return "catch"; + case TokenIdKeywordCallconv: return "callconv"; + case TokenIdKeywordCompTime: return "comptime"; + case TokenIdKeywordConst: return "const"; + case TokenIdKeywordContinue: return "continue"; + case TokenIdKeywordDefer: return "defer"; + case TokenIdKeywordElse: return "else"; + case TokenIdKeywordEnum: return "enum"; + case TokenIdKeywordErrdefer: return "errdefer"; + case TokenIdKeywordError: return "error"; + case TokenIdKeywordExport: return "export"; + case TokenIdKeywordExtern: return "extern"; + case TokenIdKeywordFalse: return "false"; + case TokenIdKeywordFn: return "fn"; + case TokenIdKeywordFor: return "for"; + case TokenIdKeywordIf: return "if"; + case TokenIdKeywordInline: return "inline"; + case TokenIdKeywordNoAlias: return "noalias"; + case TokenIdKeywordNoInline: return "noinline"; + case TokenIdKeywordNoSuspend: return "nosuspend"; + case TokenIdKeywordNull: return "null"; + case TokenIdKeywordOr: return "or"; + case TokenIdKeywordOrElse: return "orelse"; + case TokenIdKeywordPacked: return "packed"; + case TokenIdKeywordPub: return "pub"; + case TokenIdKeywordReturn: return "return"; + case TokenIdKeywordLinkSection: return "linksection"; + case TokenIdKeywordStruct: return "struct"; + case TokenIdKeywordSwitch: return "switch"; + case TokenIdKeywordTest: return "test"; + case TokenIdKeywordThreadLocal: return "threadlocal"; + case TokenIdKeywordTrue: return "true"; + case TokenIdKeywordTry: return "try"; + case TokenIdKeywordUndefined: return "undefined"; + case TokenIdKeywordUnion: return "union"; + case TokenIdKeywordUnreachable: return "unreachable"; + case TokenIdKeywordUsingNamespace: return "usingnamespace"; + case TokenIdKeywordVar: return "var"; + case TokenIdKeywordVolatile: return "volatile"; + case TokenIdKeywordWhile: return "while"; + case TokenIdLBrace: return "{"; + case TokenIdLBracket: return "["; + case TokenIdLParen: return "("; + case TokenIdQuestion: return "?"; + case TokenIdMinusEq: return "-="; + case TokenIdMinusPercent: return "-%"; + case TokenIdMinusPercentEq: return "-%="; + case TokenIdModEq: return "%="; + case TokenIdNumberSign: return "#"; + case TokenIdPercent: return "%"; + case TokenIdPercentDot: return "%."; + case TokenIdPlus: return "+"; + case TokenIdPlusEq: return "+="; + case TokenIdPlusPercent: return "+%"; + case TokenIdPlusPercentEq: return "+%="; + case TokenIdPlusPlus: return "++"; + case TokenIdRBrace: return "}"; + case TokenIdRBracket: return "]"; + case TokenIdRParen: return ")"; + case TokenIdSemicolon: return ";"; + case TokenIdSlash: return "/"; + case TokenIdStar: return "*"; + case TokenIdStarStar: return "**"; + case TokenIdStringLiteral: return "StringLiteral"; + case TokenIdMultilineStringLiteral: return "MultilineStringLiteral"; + case TokenIdSymbol: return "Symbol"; + case TokenIdTilde: return "~"; + case TokenIdTimesEq: return "*="; + case TokenIdTimesPercent: return "*%"; + case TokenIdTimesPercentEq: return "*%="; + case TokenIdBarBarEq: return "||="; + case TokenIdCount: + zig_unreachable(); + } + return "(invalid token)"; +} + +void print_tokens(Buf *buf, ZigList *tokens) { + for (size_t i = 0; i < tokens->length; i += 1) { + Token *token = &tokens->at(i); + fprintf(stderr, "%s ", token_name(token->id)); + if (token->start_pos != SIZE_MAX) { + fwrite(buf_ptr(buf) + token->start_pos, 1, token->end_pos - token->start_pos, stderr); + } + fprintf(stderr, "\n"); + } +} + +bool valid_symbol_starter(uint8_t c) { + switch (c) { + case SYMBOL_START: + return true; + } + return false; +} diff --git a/src/stage1/tokenizer.hpp b/src/stage1/tokenizer.hpp new file mode 100644 index 0000000000000000000000000000000000000000..d8af21ee006eb2070990680e8c8540f6acf42008 --- /dev/null +++ b/src/stage1/tokenizer.hpp @@ -0,0 +1,199 @@ +/* + * Copyright (c) 2015 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef ZIG_TOKENIZER_HPP +#define ZIG_TOKENIZER_HPP + +#include "buffer.hpp" +#include "bigint.hpp" +#include "bigfloat.hpp" + +enum TokenId { + TokenIdAmpersand, + TokenIdArrow, + TokenIdAtSign, + TokenIdBang, + TokenIdBarBar, + TokenIdBarBarEq, + TokenIdBinOr, + TokenIdBinXor, + TokenIdBitAndEq, + TokenIdBitOrEq, + TokenIdBitShiftLeft, + TokenIdBitShiftLeftEq, + TokenIdBitShiftRight, + TokenIdBitShiftRightEq, + TokenIdBitXorEq, + TokenIdCharLiteral, + TokenIdCmpEq, + TokenIdCmpGreaterOrEq, + TokenIdCmpGreaterThan, + TokenIdCmpLessOrEq, + TokenIdCmpLessThan, + TokenIdCmpNotEq, + TokenIdColon, + TokenIdComma, + TokenIdDash, + TokenIdDivEq, + TokenIdDocComment, + TokenIdContainerDocComment, + TokenIdDot, + TokenIdDotStar, + TokenIdEllipsis2, + TokenIdEllipsis3, + TokenIdEof, + TokenIdEq, + TokenIdFatArrow, + TokenIdFloatLiteral, + TokenIdIntLiteral, + TokenIdKeywordAlign, + TokenIdKeywordAllowZero, + TokenIdKeywordAnd, + TokenIdKeywordAnyFrame, + TokenIdKeywordAnyType, + TokenIdKeywordAsm, + TokenIdKeywordAsync, + TokenIdKeywordAwait, + TokenIdKeywordBreak, + TokenIdKeywordCatch, + TokenIdKeywordCallconv, + TokenIdKeywordCompTime, + TokenIdKeywordConst, + TokenIdKeywordContinue, + TokenIdKeywordDefer, + TokenIdKeywordElse, + TokenIdKeywordEnum, + TokenIdKeywordErrdefer, + TokenIdKeywordError, + TokenIdKeywordExport, + TokenIdKeywordExtern, + TokenIdKeywordFalse, + TokenIdKeywordFn, + TokenIdKeywordFor, + TokenIdKeywordIf, + TokenIdKeywordInline, + TokenIdKeywordNoInline, + TokenIdKeywordLinkSection, + TokenIdKeywordNoAlias, + TokenIdKeywordNoSuspend, + TokenIdKeywordNull, + TokenIdKeywordOr, + TokenIdKeywordOrElse, + TokenIdKeywordPacked, + TokenIdKeywordPub, + TokenIdKeywordResume, + TokenIdKeywordReturn, + TokenIdKeywordStruct, + TokenIdKeywordSuspend, + TokenIdKeywordSwitch, + TokenIdKeywordTest, + TokenIdKeywordThreadLocal, + TokenIdKeywordTrue, + TokenIdKeywordTry, + TokenIdKeywordUndefined, + TokenIdKeywordUnion, + TokenIdKeywordUnreachable, + TokenIdKeywordUsingNamespace, + TokenIdKeywordVar, + TokenIdKeywordVolatile, + TokenIdKeywordWhile, + TokenIdLBrace, + TokenIdLBracket, + TokenIdLParen, + TokenIdQuestion, + TokenIdMinusEq, + TokenIdMinusPercent, + TokenIdMinusPercentEq, + TokenIdModEq, + TokenIdNumberSign, + TokenIdPercent, + TokenIdPercentDot, + TokenIdPlus, + TokenIdPlusEq, + TokenIdPlusPercent, + TokenIdPlusPercentEq, + TokenIdPlusPlus, + TokenIdRBrace, + TokenIdRBracket, + TokenIdRParen, + TokenIdSemicolon, + TokenIdSlash, + TokenIdStar, + TokenIdStarStar, + TokenIdStringLiteral, + TokenIdMultilineStringLiteral, + TokenIdSymbol, + TokenIdTilde, + TokenIdTimesEq, + TokenIdTimesPercent, + TokenIdTimesPercentEq, + TokenIdCount, +}; + +struct TokenFloatLit { + BigFloat bigfloat; + // overflow is true if when parsing the number, we discovered it would not fit + // without losing data + bool overflow; +}; + +struct TokenIntLit { + BigInt bigint; +}; + +struct TokenStrLit { + Buf str; +}; + +struct TokenCharLit { + uint32_t c; +}; + +struct Token { + TokenId id; + size_t start_pos; + size_t end_pos; + size_t start_line; + size_t start_column; + + union { + // TokenIdIntLiteral + TokenIntLit int_lit; + + // TokenIdFloatLiteral + TokenFloatLit float_lit; + + // TokenIdStringLiteral, TokenIdMultilineStringLiteral or TokenIdSymbol + TokenStrLit str_lit; + + // TokenIdCharLiteral + TokenCharLit char_lit; + } data; +}; +// work around conflicting name Token which is also found in libclang +typedef Token ZigToken; + +struct Tokenization { + ZigList *tokens; + ZigList *line_offsets; + + // if an error occurred + Buf *err; + size_t err_line; + size_t err_column; +}; + +void tokenize(Buf *buf, Tokenization *out_tokenization); + +void print_tokens(Buf *buf, ZigList *tokens); + +const char * token_name(TokenId id); + +bool valid_symbol_starter(uint8_t c); +bool is_zig_keyword(Buf *buf); + +#endif diff --git a/src/stage1/util.cpp b/src/stage1/util.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2de09df8087be2d8a012328117e1898560f0dc0f --- /dev/null +++ b/src/stage1/util.cpp @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2015 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#include "util.hpp" +#include "stage2.h" + +#include +#include + +void zig_panic(const char *format, ...) { + va_list ap; + va_start(ap, format); + vfprintf(stderr, format, ap); + fflush(stderr); + va_end(ap); + stage2_panic("", 0); + abort(); +} + +uint32_t int_hash(int i) { + return (uint32_t)(i % UINT32_MAX); +} +bool int_eq(int a, int b) { + return a == b; +} + +uint32_t uint64_hash(uint64_t i) { + return (uint32_t)(i % UINT32_MAX); +} + +bool uint64_eq(uint64_t a, uint64_t b) { + return a == b; +} + +uint32_t ptr_hash(const void *ptr) { + return (uint32_t)(((uintptr_t)ptr) % UINT32_MAX); +} + +bool ptr_eq(const void *a, const void *b) { + return a == b; +} + +// Ported from std/mem.zig. +bool SplitIterator_isSplitByte(SplitIterator *self, uint8_t byte) { + for (size_t i = 0; i < self->split_bytes.len; i += 1) { + if (byte == self->split_bytes.ptr[i]) { + return true; + } + } + return false; +} + +// Ported from std/mem.zig. +Optional> SplitIterator_next(SplitIterator *self) { + // move to beginning of token + while (self->index < self->buffer.len && + SplitIterator_isSplitByte(self, self->buffer.ptr[self->index])) + { + self->index += 1; + } + size_t start = self->index; + if (start == self->buffer.len) { + return {}; + } + + // move to end of token + while (self->index < self->buffer.len && + !SplitIterator_isSplitByte(self, self->buffer.ptr[self->index])) + { + self->index += 1; + } + size_t end = self->index; + + return Optional>::some(self->buffer.slice(start, end)); +} + +// Ported from std/mem.zig. +// This one won't collapse multiple separators into one, so you could use it, for example, +// to parse Comma Separated Value format. +Optional> SplitIterator_next_separate(SplitIterator *self) { + // move to beginning of token + if (self->index < self->buffer.len && + SplitIterator_isSplitByte(self, self->buffer.ptr[self->index])) + { + self->index += 1; + } + size_t start = self->index; + if (start == self->buffer.len) { + return {}; + } + + // move to end of token + while (self->index < self->buffer.len && + !SplitIterator_isSplitByte(self, self->buffer.ptr[self->index])) + { + self->index += 1; + } + size_t end = self->index; + + return Optional>::some(self->buffer.slice(start, end)); +} + +// Ported from std/mem.zig +Slice SplitIterator_rest(SplitIterator *self) { + // move to beginning of token + size_t index = self->index; + while (index < self->buffer.len && SplitIterator_isSplitByte(self, self->buffer.ptr[index])) { + index += 1; + } + return self->buffer.sliceFrom(index); +} + +// Ported from std/mem.zig +SplitIterator memSplit(Slice buffer, Slice split_bytes) { + return SplitIterator{0, buffer, split_bytes}; +} + +void zig_pretty_print_bytes(FILE *f, double n) { + if (n > 1024.0 * 1024.0 * 1024.0) { + fprintf(f, "%.03f GiB", n / 1024.0 / 1024.0 / 1024.0); + return; + } + if (n > 1024.0 * 1024.0) { + fprintf(f, "%.03f MiB", n / 1024.0 / 1024.0); + return; + } + if (n > 1024.0) { + fprintf(f, "%.03f KiB", n / 1024.0); + return; + } + fprintf(f, "%.03f bytes", n ); + return; +} + diff --git a/src/stage1/util.hpp b/src/stage1/util.hpp new file mode 100644 index 0000000000000000000000000000000000000000..66efe6dfd1736cfdb7d22023ed14b8d7b3a97c00 --- /dev/null +++ b/src/stage1/util.hpp @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2015 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef ZIG_UTIL_HPP +#define ZIG_UTIL_HPP + +#include +#include +#include +#include + +#if defined(_MSC_VER) +#include +#endif + +#include "config.h" +#include "util_base.hpp" +#include "heap.hpp" +#include "mem.hpp" + +#if defined(_MSC_VER) +static inline int clzll(unsigned long long mask) { + unsigned long lz; +#if defined(_WIN64) + if (_BitScanReverse64(&lz, mask)) + return static_cast(63 - lz); + zig_unreachable(); +#else + if (_BitScanReverse(&lz, mask >> 32)) + lz += 32; + else + _BitScanReverse(&lz, mask & 0xffffffff); + return 63 - lz; +#endif +} +static inline int ctzll(unsigned long long mask) { + unsigned long result; +#if defined(_WIN64) + if (_BitScanForward64(&result, mask)) + return result; + zig_unreachable(); +#else + if (_BitScanForward(&result, mask & 0xffffffff)) + return result; + } + if (_BitScanForward(&result, mask >> 32)) + return 32 + result; + zig_unreachable(); +#endif +} +#else +#define clzll(x) __builtin_clzll(x) +#define ctzll(x) __builtin_ctzll(x) +#endif + +template +constexpr size_t array_length(const T (&)[n]) { + return n; +} + +template +static inline T max(T a, T b) { + return (a >= b) ? a : b; +} + +template +static inline T min(T a, T b) { + return (a <= b) ? a : b; +} + +template +static inline T clamp(T min_value, T value, T max_value) { + return max(min(value, max_value), min_value); +} + +static inline bool mem_eql_mem(const char *a_ptr, size_t a_len, const char *b_ptr, size_t b_len) { + if (a_len != b_len) + return false; + return memcmp(a_ptr, b_ptr, a_len) == 0; +} +static inline bool mem_eql_mem_ignore_case(const char *a_ptr, size_t a_len, const char *b_ptr, size_t b_len) { + if (a_len != b_len) + return false; + for (size_t i = 0; i < a_len; i += 1) { + if (tolower(a_ptr[i]) != tolower(b_ptr[i])) + return false; + } + return true; +} + +static inline bool mem_eql_str(const char *mem, size_t mem_len, const char *str) { + return mem_eql_mem(mem, mem_len, str, strlen(str)); +} + +static inline bool str_eql_str(const char *a, const char* b) { + return mem_eql_mem(a, strlen(a), b, strlen(b)); +} + +static inline bool str_eql_str_ignore_case(const char *a, const char* b) { + return mem_eql_mem_ignore_case(a, strlen(a), b, strlen(b)); +} + +static inline bool is_power_of_2(uint64_t x) { + return x != 0 && ((x & (~x + 1)) == x); +} + +static inline bool mem_ends_with_mem(const char *mem, size_t mem_len, const char *end, size_t end_len) { + if (mem_len < end_len) return false; + return memcmp(mem + mem_len - end_len, end, end_len) == 0; +} + +static inline bool mem_ends_with_str(const char *mem, size_t mem_len, const char *str) { + return mem_ends_with_mem(mem, mem_len, str, strlen(str)); +} + +static inline uint64_t round_to_next_power_of_2(uint64_t x) { + --x; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + x |= x >> 32; + return x + 1; +} + +uint32_t int_hash(int i); +bool int_eq(int a, int b); +uint32_t uint64_hash(uint64_t i); +bool uint64_eq(uint64_t a, uint64_t b); +uint32_t ptr_hash(const void *ptr); +bool ptr_eq(const void *a, const void *b); + +static inline uint8_t log2_u64(uint64_t x) { + return (63 - clzll(x)); +} + +void zig_pretty_print_bytes(FILE *f, double n); + +template +struct Optional { + T value; + bool is_some; + + static inline Optional some(T x) { + return {x, true}; + } + + static inline Optional none() { + return {{}, false}; + } + + inline bool unwrap(T *res) { + *res = value; + return is_some; + } +}; + +template +struct Slice { + T *ptr; + size_t len; + + inline T &at(size_t i) { + assert(i < len); + return ptr[i]; + } + + inline Slice slice(size_t start, size_t end) { + assert(end <= len); + assert(end >= start); + return { + ptr + start, + end - start, + }; + } + + inline Slice sliceFrom(size_t start) { + assert(start <= len); + return { + ptr + start, + len - start, + }; + } + + static inline Slice alloc(size_t n) { + return {heap::c_allocator.allocate_nonzero(n), n}; + } +}; + +template +struct Array { + static const size_t len = n; + T items[n]; + + inline Slice slice() { + return { + &items[0], + len, + }; + } +}; + +static inline Slice str(const char *literal) { + return {(uint8_t*)(literal), strlen(literal)}; +} + +// Ported from std/mem.zig +template +static inline bool memEql(Slice a, Slice b) { + if (a.len != b.len) + return false; + for (size_t i = 0; i < a.len; i += 1) { + if (a.ptr[i] != b.ptr[i]) + return false; + } + return true; +} + +// Ported from std/mem.zig +template +static inline bool memStartsWith(Slice haystack, Slice needle) { + if (needle.len > haystack.len) + return false; + return memEql(haystack.slice(0, needle.len), needle); +} + +// Ported from std/mem.zig +template +static inline void memCopy(Slice dest, Slice src) { + assert(dest.len >= src.len); + memcpy(dest.ptr, src.ptr, src.len * sizeof(T)); +} + +// Ported from std/mem.zig. +// Coordinate struct fields with memSplit function +struct SplitIterator { + size_t index; + Slice buffer; + Slice split_bytes; +}; + +bool SplitIterator_isSplitByte(SplitIterator *self, uint8_t byte); +Optional< Slice > SplitIterator_next(SplitIterator *self); +Optional< Slice > SplitIterator_next_separate(SplitIterator *self); +Slice SplitIterator_rest(SplitIterator *self); +SplitIterator memSplit(Slice buffer, Slice split_bytes); + +#endif diff --git a/src/stage1/util_base.hpp b/src/stage1/util_base.hpp new file mode 100644 index 0000000000000000000000000000000000000000..da1d3bf234deba7255089c19399e03a48d1ce737 --- /dev/null +++ b/src/stage1/util_base.hpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2015 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +#ifndef ZIG_UTIL_BASE_HPP +#define ZIG_UTIL_BASE_HPP + +#include + +#if defined(_MSC_VER) + +#define ATTRIBUTE_COLD __declspec(noinline) +#define ATTRIBUTE_PRINTF(a, b) +#define ATTRIBUTE_RETURNS_NOALIAS __declspec(restrict) +#define ATTRIBUTE_NORETURN __declspec(noreturn) +#define ATTRIBUTE_MUST_USE + +#define BREAKPOINT __debugbreak() + +#else + +#define ATTRIBUTE_COLD __attribute__((cold)) +#define ATTRIBUTE_PRINTF(a, b) __attribute__((format(printf, a, b))) +#define ATTRIBUTE_RETURNS_NOALIAS __attribute__((__malloc__)) +#define ATTRIBUTE_NORETURN __attribute__((noreturn)) +#define ATTRIBUTE_MUST_USE __attribute__((warn_unused_result)) + +#if defined(__MINGW32__) || defined(__MINGW64__) +#define BREAKPOINT __debugbreak() +#elif defined(__i386__) || defined(__x86_64__) +#define BREAKPOINT __asm__ volatile("int $0x03"); +#elif defined(__clang__) +#define BREAKPOINT __builtin_debugtrap() +#elif defined(__GNUC__) +#define BREAKPOINT __builtin_trap() +#else +#include +#define BREAKPOINT raise(SIGTRAP) +#endif + +#endif + +ATTRIBUTE_COLD +ATTRIBUTE_NORETURN +ATTRIBUTE_PRINTF(1, 2) +void zig_panic(const char *format, ...); + +static inline void zig_assert(bool ok, const char *file, int line, const char *func) { + if (!ok) { + zig_panic("Assertion failed at %s:%d in %s. This is a bug in the Zig compiler.", file, line, func); + } +} + +#ifdef _WIN32 +#define __func__ __FUNCTION__ +#endif + +#define zig_unreachable() zig_panic("Unreachable at %s:%d in %s. This is a bug in the Zig compiler.", __FILE__, __LINE__, __func__) + +// Assertions in stage1 are always on, and they call zig @panic. +#undef assert +#define assert(ok) zig_assert(ok, __FILE__, __LINE__, __func__) + +#if defined(_MSC_VER) +#define ZIG_FALLTHROUGH +#elif defined(__clang__) +#define ZIG_FALLTHROUGH [[clang::fallthrough]] +#elif defined(__GNUC__) && __GNUC__ >= 7 +#define ZIG_FALLTHROUGH __attribute__((fallthrough)) +#else +#define ZIG_FALLTHROUGH +#endif + +#endif diff --git a/src/stage1/zig0.cpp b/src/stage1/zig0.cpp new file mode 100644 index 0000000000000000000000000000000000000000..839ff0263f074ed1342872eeb5ce6344375b5d20 --- /dev/null +++ b/src/stage1/zig0.cpp @@ -0,0 +1,529 @@ +/* + * Copyright (c) 2015 Andrew Kelley + * + * This file is part of zig, which is MIT licensed. + * See http://opensource.org/licenses/MIT + */ + +// This file is the entry point for zig0, which is *only* used to build +// stage2, the self-hosted compiler, into an object file, which is then +// linked by the same build system (cmake) that linked this binary. + +#include "stage1.h" +#include "heap.hpp" +#include "stage2.h" +#include "target.hpp" +#include "error.hpp" +#include "util.hpp" +#include "buffer.hpp" +#include "os.hpp" + +#include +#include + +static int print_error_usage(const char *arg0) { + fprintf(stderr, "See `%s --help` for detailed usage information\n", arg0); + return EXIT_FAILURE; +} + +static int print_full_usage(const char *arg0, FILE *file, int return_code) { + fprintf(file, + "Usage: %s [options] builds an object file\n" + "\n" + "Options:\n" + " --color [auto|off|on] enable or disable colored error messages\n" + " --name [name] override output name\n" + " -femit-bin=[path] Output machine code\n" + " --pkg-begin [name] [path] make pkg available to import and push current pkg\n" + " --pkg-end pop current pkg\n" + " -ODebug build with optimizations on and safety off\n" + " -OReleaseFast build with optimizations on and safety off\n" + " -OReleaseSafe build with optimizations on and safety on\n" + " -OReleaseSmall build with size optimizations on and safety off\n" + " --single-threaded source may assume it is only used single-threaded\n" + " -dynamic create a shared library (.so; .dll; .dylib)\n" + " --strip exclude debug symbols\n" + " -target [name] -- see the targets command\n" + " -mcpu [cpu] specify target CPU and feature set\n" + " --verbose-tokenize enable compiler debug output for tokenization\n" + " --verbose-ast enable compiler debug output for AST parsing\n" + " --verbose-ir enable compiler debug output for Zig IR\n" + " --verbose-llvm-ir enable compiler debug output for LLVM IR\n" + " --verbose-cimport enable compiler debug output for C imports\n" + " --verbose-llvm-cpu-features enable compiler debug output for LLVM CPU features\n" + "\n" + , arg0); + return return_code; +} + +static Os get_zig_os_type(ZigLLVM_OSType os_type) { + switch (os_type) { + case ZigLLVM_UnknownOS: + return OsFreestanding; + case ZigLLVM_Ananas: + return OsAnanas; + case ZigLLVM_CloudABI: + return OsCloudABI; + case ZigLLVM_DragonFly: + return OsDragonFly; + case ZigLLVM_FreeBSD: + return OsFreeBSD; + case ZigLLVM_Fuchsia: + return OsFuchsia; + case ZigLLVM_IOS: + return OsIOS; + case ZigLLVM_KFreeBSD: + return OsKFreeBSD; + case ZigLLVM_Linux: + return OsLinux; + case ZigLLVM_Lv2: + return OsLv2; + case ZigLLVM_Darwin: + case ZigLLVM_MacOSX: + return OsMacOSX; + case ZigLLVM_NetBSD: + return OsNetBSD; + case ZigLLVM_OpenBSD: + return OsOpenBSD; + case ZigLLVM_Solaris: + return OsSolaris; + case ZigLLVM_Win32: + return OsWindows; + case ZigLLVM_Haiku: + return OsHaiku; + case ZigLLVM_Minix: + return OsMinix; + case ZigLLVM_RTEMS: + return OsRTEMS; + case ZigLLVM_NaCl: + return OsNaCl; + case ZigLLVM_CNK: + return OsCNK; + case ZigLLVM_AIX: + return OsAIX; + case ZigLLVM_CUDA: + return OsCUDA; + case ZigLLVM_NVCL: + return OsNVCL; + case ZigLLVM_AMDHSA: + return OsAMDHSA; + case ZigLLVM_PS4: + return OsPS4; + case ZigLLVM_ELFIAMCU: + return OsELFIAMCU; + case ZigLLVM_TvOS: + return OsTvOS; + case ZigLLVM_WatchOS: + return OsWatchOS; + case ZigLLVM_Mesa3D: + return OsMesa3D; + case ZigLLVM_Contiki: + return OsContiki; + case ZigLLVM_AMDPAL: + return OsAMDPAL; + case ZigLLVM_HermitCore: + return OsHermitCore; + case ZigLLVM_Hurd: + return OsHurd; + case ZigLLVM_WASI: + return OsWASI; + case ZigLLVM_Emscripten: + return OsEmscripten; + } + zig_unreachable(); +} + +static void get_native_target(ZigTarget *target) { + // first zero initialize + *target = {}; + + ZigLLVM_OSType os_type; + ZigLLVM_ObjectFormatType oformat; // ignored; based on arch/os + ZigLLVM_VendorType trash; + ZigLLVMGetNativeTarget( + &target->arch, + &trash, + &os_type, + &target->abi, + &oformat); + target->os = get_zig_os_type(os_type); + target->is_native_os = true; + target->is_native_cpu = true; + if (target->abi == ZigLLVM_UnknownEnvironment) { + target->abi = target_default_abi(target->arch, target->os); + } +} + +static Error target_parse_triple(struct ZigTarget *target, const char *zig_triple, const char *mcpu, + const char *dynamic_linker) +{ + Error err; + + if (zig_triple != nullptr && strcmp(zig_triple, "native") == 0) { + zig_triple = nullptr; + } + + if (zig_triple == nullptr) { + get_native_target(target); + + if (mcpu == nullptr) { + target->llvm_cpu_name = ZigLLVMGetHostCPUName(); + target->llvm_cpu_features = ZigLLVMGetNativeFeatures(); + } else if (strcmp(mcpu, "baseline") == 0) { + target->is_native_os = false; + target->is_native_cpu = false; + target->llvm_cpu_name = ""; + target->llvm_cpu_features = ""; + } else { + const char *msg = "stage0 can't handle CPU/features in the target"; + stage2_panic(msg, strlen(msg)); + } + } else { + // first initialize all to zero + *target = {}; + + SplitIterator it = memSplit(str(zig_triple), str("-")); + + Optional> opt_archsub = SplitIterator_next(&it); + Optional> opt_os = SplitIterator_next(&it); + Optional> opt_abi = SplitIterator_next(&it); + + if (!opt_archsub.is_some) + return ErrorMissingArchitecture; + + if ((err = target_parse_arch(&target->arch, (char*)opt_archsub.value.ptr, opt_archsub.value.len))) { + return err; + } + + if (!opt_os.is_some) + return ErrorMissingOperatingSystem; + + if ((err = target_parse_os(&target->os, (char*)opt_os.value.ptr, opt_os.value.len))) { + return err; + } + + if (opt_abi.is_some) { + if ((err = target_parse_abi(&target->abi, (char*)opt_abi.value.ptr, opt_abi.value.len))) { + return err; + } + } else { + target->abi = target_default_abi(target->arch, target->os); + } + + if (mcpu != nullptr && strcmp(mcpu, "baseline") != 0) { + const char *msg = "stage0 can't handle CPU/features in the target"; + stage2_panic(msg, strlen(msg)); + } + } + + return ErrorNone; +} + + +static bool str_starts_with(const char *s1, const char *s2) { + size_t s2_len = strlen(s2); + if (strlen(s1) < s2_len) { + return false; + } + return memcmp(s1, s2, s2_len) == 0; +} + +int main_exit(Stage2ProgressNode *root_progress_node, int exit_code) { + if (root_progress_node != nullptr) { + stage2_progress_end(root_progress_node); + } + return exit_code; +} + +int main(int argc, char **argv) { + zig_stage1_os_init(); + + char *arg0 = argv[0]; + Error err; + + const char *in_file = nullptr; + const char *emit_bin_path = nullptr; + bool strip = false; + const char *out_name = nullptr; + bool verbose_tokenize = false; + bool verbose_ast = false; + bool verbose_ir = false; + bool verbose_llvm_ir = false; + bool verbose_cimport = false; + bool verbose_llvm_cpu_features = false; + ErrColor color = ErrColorAuto; + const char *dynamic_linker = nullptr; + bool link_libc = false; + bool link_libcpp = false; + const char *target_string = nullptr; + ZigStage1Pkg *cur_pkg = heap::c_allocator.create(); + BuildMode optimize_mode = BuildModeDebug; + TargetSubsystem subsystem = TargetSubsystemAuto; + const char *override_lib_dir = nullptr; + const char *mcpu = nullptr; + + for (int i = 1; i < argc; i += 1) { + char *arg = argv[i]; + + if (arg[0] == '-') { + if (strcmp(arg, "--") == 0) { + fprintf(stderr, "Unexpected end-of-parameter mark: %s\n", arg); + } else if (strcmp(arg, "-ODebug") == 0) { + optimize_mode = BuildModeDebug; + } else if (strcmp(arg, "-OReleaseFast") == 0) { + optimize_mode = BuildModeFastRelease; + } else if (strcmp(arg, "-OReleaseSafe") == 0) { + optimize_mode = BuildModeSafeRelease; + } else if (strcmp(arg, "-OReleaseSmall") == 0) { + optimize_mode = BuildModeSmallRelease; + } else if (strcmp(arg, "--help") == 0) { + return print_full_usage(arg0, stdout, EXIT_SUCCESS); + } else if (strcmp(arg, "--strip") == 0) { + strip = true; + } else if (strcmp(arg, "--verbose-tokenize") == 0) { + verbose_tokenize = true; + } else if (strcmp(arg, "--verbose-ast") == 0) { + verbose_ast = true; + } else if (strcmp(arg, "--verbose-ir") == 0) { + verbose_ir = true; + } else if (strcmp(arg, "--verbose-llvm-ir") == 0) { + verbose_llvm_ir = true; + } else if (strcmp(arg, "--verbose-cimport") == 0) { + verbose_cimport = true; + } else if (strcmp(arg, "--verbose-llvm-cpu-features") == 0) { + verbose_llvm_cpu_features = true; + } else if (arg[1] == 'l' && arg[2] != 0) { + // alias for --library + const char *l = &arg[2]; + if (strcmp(l, "c") == 0) { + link_libc = true; + } else if (strcmp(l, "c++") == 0 || strcmp(l, "stdc++") == 0) { + link_libcpp = true; + } + } else if (strcmp(arg, "--pkg-begin") == 0) { + if (i + 2 >= argc) { + fprintf(stderr, "Expected 2 arguments after --pkg-begin\n"); + return print_error_usage(arg0); + } + ZigStage1Pkg *new_cur_pkg = heap::c_allocator.create(); + i += 1; + new_cur_pkg->name_ptr = argv[i]; + new_cur_pkg->name_len = strlen(argv[i]); + i += 1; + new_cur_pkg->path_ptr = argv[i]; + new_cur_pkg->path_len = strlen(argv[i]); + new_cur_pkg->parent = cur_pkg; + cur_pkg->children_ptr = heap::c_allocator.reallocate(cur_pkg->children_ptr, + cur_pkg->children_len, cur_pkg->children_len + 1); + cur_pkg->children_ptr[cur_pkg->children_len] = new_cur_pkg; + cur_pkg->children_len += 1; + + cur_pkg = new_cur_pkg; + } else if (strcmp(arg, "--pkg-end") == 0) { + if (cur_pkg->parent == nullptr) { + fprintf(stderr, "Encountered --pkg-end with no matching --pkg-begin\n"); + return EXIT_FAILURE; + } + cur_pkg = cur_pkg->parent; + } else if (str_starts_with(arg, "-mcpu=")) { + mcpu = arg + strlen("-mcpu="); + } else if (str_starts_with(arg, "-femit-bin=")) { + emit_bin_path = arg + strlen("-femit-bin="); + } else if (i + 1 >= argc) { + fprintf(stderr, "Expected another argument after %s\n", arg); + return print_error_usage(arg0); + } else { + i += 1; + if (strcmp(arg, "--color") == 0) { + if (strcmp(argv[i], "auto") == 0) { + color = ErrColorAuto; + } else if (strcmp(argv[i], "on") == 0) { + color = ErrColorOn; + } else if (strcmp(argv[i], "off") == 0) { + color = ErrColorOff; + } else { + fprintf(stderr, "--color options are 'auto', 'on', or 'off'\n"); + return print_error_usage(arg0); + } + } else if (strcmp(arg, "--name") == 0) { + out_name = argv[i]; + } else if (strcmp(arg, "--dynamic-linker") == 0) { + dynamic_linker = argv[i]; + } else if (strcmp(arg, "--override-lib-dir") == 0) { + override_lib_dir = argv[i]; + } else if (strcmp(arg, "--library") == 0 || strcmp(arg, "-l") == 0) { + if (strcmp(argv[i], "c") == 0) { + link_libc = true; + } else if (strcmp(argv[i], "c++") == 0 || strcmp(argv[i], "stdc++") == 0) { + link_libcpp = true; + } + } else if (strcmp(arg, "-target") == 0) { + target_string = argv[i]; + } else if (strcmp(arg, "--subsystem") == 0) { + if (strcmp(argv[i], "console") == 0) { + subsystem = TargetSubsystemConsole; + } else if (strcmp(argv[i], "windows") == 0) { + subsystem = TargetSubsystemWindows; + } else if (strcmp(argv[i], "posix") == 0) { + subsystem = TargetSubsystemPosix; + } else if (strcmp(argv[i], "native") == 0) { + subsystem = TargetSubsystemNative; + } else if (strcmp(argv[i], "efi_application") == 0) { + subsystem = TargetSubsystemEfiApplication; + } else if (strcmp(argv[i], "efi_boot_service_driver") == 0) { + subsystem = TargetSubsystemEfiBootServiceDriver; + } else if (strcmp(argv[i], "efi_rom") == 0) { + subsystem = TargetSubsystemEfiRom; + } else if (strcmp(argv[i], "efi_runtime_driver") == 0) { + subsystem = TargetSubsystemEfiRuntimeDriver; + } else { + fprintf(stderr, "invalid: --subsystem %s\n" + "Options are:\n" + " console\n" + " windows\n" + " posix\n" + " native\n" + " efi_application\n" + " efi_boot_service_driver\n" + " efi_rom\n" + " efi_runtime_driver\n" + , argv[i]); + return EXIT_FAILURE; + } + } else if (strcmp(arg, "-mcpu") == 0) { + mcpu = argv[i]; + } else { + fprintf(stderr, "Invalid argument: %s\n", arg); + return print_error_usage(arg0); + } + } + } else if (!in_file) { + in_file = arg; + } else { + fprintf(stderr, "Unexpected extra parameter: %s\n", arg); + return print_error_usage(arg0); + } + } + + if (cur_pkg->parent != nullptr) { + fprintf(stderr, "Unmatched --pkg-begin\n"); + return EXIT_FAILURE; + } + + Stage2Progress *progress = stage2_progress_create(); + Stage2ProgressNode *root_progress_node = stage2_progress_start_root(progress, "", 0, 0); + if (color == ErrColorOff) stage2_progress_disable_tty(progress); + + ZigTarget target; + if ((err = target_parse_triple(&target, target_string, mcpu, dynamic_linker))) { + fprintf(stderr, "invalid target: %s\n", err_str(err)); + return print_error_usage(arg0); + } + + if (in_file == nullptr) { + fprintf(stderr, "missing zig file\n"); + return print_error_usage(arg0); + } + + if (out_name == nullptr) { + fprintf(stderr, "missing --name\n"); + return print_error_usage(arg0); + } + + ZigStage1 *stage1 = zig_stage1_create(optimize_mode, + nullptr, 0, + in_file, strlen(in_file), + override_lib_dir, strlen(override_lib_dir), + &target, false); + + stage1->main_progress_node = root_progress_node; + stage1->root_name_ptr = out_name; + stage1->root_name_len = strlen(out_name); + stage1->strip = strip; + stage1->verbose_tokenize = verbose_tokenize; + stage1->verbose_ast = verbose_ast; + stage1->verbose_ir = verbose_ir; + stage1->verbose_llvm_ir = verbose_llvm_ir; + stage1->verbose_cimport = verbose_cimport; + stage1->verbose_llvm_cpu_features = verbose_llvm_cpu_features; + stage1->emit_o_ptr = emit_bin_path; + stage1->emit_o_len = strlen(emit_bin_path); + stage1->root_pkg = cur_pkg; + stage1->err_color = color; + stage1->link_libc = link_libc; + stage1->link_libcpp = link_libcpp; + stage1->subsystem = subsystem; + stage1->pic = true; + + zig_stage1_build_object(stage1); + + zig_stage1_destroy(stage1); + + return main_exit(root_progress_node, EXIT_SUCCESS); +} + +void stage2_panic(const char *ptr, size_t len) { + fwrite(ptr, 1, len, stderr); + fprintf(stderr, "\n"); + fflush(stderr); + abort(); +} + +struct Stage2Progress { + int trash; +}; + +struct Stage2ProgressNode { + int trash; +}; + +Stage2Progress *stage2_progress_create(void) { + return nullptr; +} + +void stage2_progress_destroy(Stage2Progress *progress) {} + +Stage2ProgressNode *stage2_progress_start_root(Stage2Progress *progress, + const char *name_ptr, size_t name_len, size_t estimated_total_items) +{ + return nullptr; +} +Stage2ProgressNode *stage2_progress_start(Stage2ProgressNode *node, + const char *name_ptr, size_t name_len, size_t estimated_total_items) +{ + return nullptr; +} +void stage2_progress_end(Stage2ProgressNode *node) {} +void stage2_progress_complete_one(Stage2ProgressNode *node) {} +void stage2_progress_disable_tty(Stage2Progress *progress) {} +void stage2_progress_update_node(Stage2ProgressNode *node, size_t completed_count, size_t estimated_total_items){} + +const char *stage2_fetch_file(struct ZigStage1 *stage1, const char *path_ptr, size_t path_len, + size_t *result_len) +{ + Error err; + Buf contents_buf = BUF_INIT; + Buf path_buf = BUF_INIT; + + buf_init_from_mem(&path_buf, path_ptr, path_len); + if ((err = os_fetch_file_path(&path_buf, &contents_buf))) { + return nullptr; + } + *result_len = buf_len(&contents_buf); + return buf_ptr(&contents_buf); +} + +Error stage2_cimport(struct ZigStage1 *stage1, const char *c_src_ptr, size_t c_src_len, + const char **out_zig_path_ptr, size_t *out_zig_path_len, + struct Stage2ErrorMsg **out_errors_ptr, size_t *out_errors_len) +{ + const char *msg = "stage0 called stage2_cimport"; + stage2_panic(msg, strlen(msg)); +} + +const char *stage2_add_link_lib(struct ZigStage1 *stage1, + const char *lib_name_ptr, size_t lib_name_len, + const char *symbol_name_ptr, size_t symbol_name_len) +{ + return nullptr; +} diff --git a/src/stage2.cpp b/src/stage2.cpp deleted file mode 100644 index 6c010de84f7b77e1b8e60e7a4a044c4ec8fa17ce..0000000000000000000000000000000000000000 --- a/src/stage2.cpp +++ /dev/null @@ -1,331 +0,0 @@ -// This file is a shim for zig1. The real implementations of these are in -// src-self-hosted/stage1.zig - -#include "stage2.h" -#include "util.hpp" -#include "zig_llvm.h" -#include "target.hpp" -#include -#include -#include - -Error stage2_translate_c(struct Stage2Ast **out_ast, - struct Stage2ErrorMsg **out_errors_ptr, size_t *out_errors_len, - const char **args_begin, const char **args_end, const char *resources_path) -{ - const char *msg = "stage0 called stage2_translate_c"; - stage2_panic(msg, strlen(msg)); -} - -void stage2_free_clang_errors(struct Stage2ErrorMsg *ptr, size_t len) { - const char *msg = "stage0 called stage2_free_clang_errors"; - stage2_panic(msg, strlen(msg)); -} - -void stage2_zen(const char **ptr, size_t *len) { - const char *msg = "stage0 called stage2_zen"; - stage2_panic(msg, strlen(msg)); -} - -int stage2_env(int argc, char** argv) { - const char *msg = "stage0 called stage2_env"; - stage2_panic(msg, strlen(msg)); -} - -void stage2_attach_segfault_handler(void) { } - -void stage2_panic(const char *ptr, size_t len) { - fwrite(ptr, 1, len, stderr); - fprintf(stderr, "\n"); - fflush(stderr); - abort(); -} - -void stage2_render_ast(struct Stage2Ast *ast, FILE *output_file) { - const char *msg = "stage0 called stage2_render_ast"; - stage2_panic(msg, strlen(msg)); -} - -int stage2_fmt(int argc, char **argv) { - const char *msg = "stage0 called stage2_fmt"; - stage2_panic(msg, strlen(msg)); -} - -stage2_DepTokenizer stage2_DepTokenizer_init(const char *input, size_t len) { - const char *msg = "stage0 called stage2_DepTokenizer_init"; - stage2_panic(msg, strlen(msg)); -} - -void stage2_DepTokenizer_deinit(stage2_DepTokenizer *self) { - const char *msg = "stage0 called stage2_DepTokenizer_deinit"; - stage2_panic(msg, strlen(msg)); -} - -stage2_DepNextResult stage2_DepTokenizer_next(stage2_DepTokenizer *self) { - const char *msg = "stage0 called stage2_DepTokenizer_next"; - stage2_panic(msg, strlen(msg)); -} - - -struct Stage2Progress { - int trash; -}; - -struct Stage2ProgressNode { - int trash; -}; - -Stage2Progress *stage2_progress_create(void) { - return nullptr; -} - -void stage2_progress_destroy(Stage2Progress *progress) {} - -Stage2ProgressNode *stage2_progress_start_root(Stage2Progress *progress, - const char *name_ptr, size_t name_len, size_t estimated_total_items) -{ - return nullptr; -} -Stage2ProgressNode *stage2_progress_start(Stage2ProgressNode *node, - const char *name_ptr, size_t name_len, size_t estimated_total_items) -{ - return nullptr; -} -void stage2_progress_end(Stage2ProgressNode *node) {} -void stage2_progress_complete_one(Stage2ProgressNode *node) {} -void stage2_progress_disable_tty(Stage2Progress *progress) {} -void stage2_progress_update_node(Stage2ProgressNode *node, size_t completed_count, size_t estimated_total_items){} - -static Os get_zig_os_type(ZigLLVM_OSType os_type) { - switch (os_type) { - case ZigLLVM_UnknownOS: - return OsFreestanding; - case ZigLLVM_Ananas: - return OsAnanas; - case ZigLLVM_CloudABI: - return OsCloudABI; - case ZigLLVM_DragonFly: - return OsDragonFly; - case ZigLLVM_FreeBSD: - return OsFreeBSD; - case ZigLLVM_Fuchsia: - return OsFuchsia; - case ZigLLVM_IOS: - return OsIOS; - case ZigLLVM_KFreeBSD: - return OsKFreeBSD; - case ZigLLVM_Linux: - return OsLinux; - case ZigLLVM_Lv2: - return OsLv2; - case ZigLLVM_Darwin: - case ZigLLVM_MacOSX: - return OsMacOSX; - case ZigLLVM_NetBSD: - return OsNetBSD; - case ZigLLVM_OpenBSD: - return OsOpenBSD; - case ZigLLVM_Solaris: - return OsSolaris; - case ZigLLVM_Win32: - return OsWindows; - case ZigLLVM_Haiku: - return OsHaiku; - case ZigLLVM_Minix: - return OsMinix; - case ZigLLVM_RTEMS: - return OsRTEMS; - case ZigLLVM_NaCl: - return OsNaCl; - case ZigLLVM_CNK: - return OsCNK; - case ZigLLVM_AIX: - return OsAIX; - case ZigLLVM_CUDA: - return OsCUDA; - case ZigLLVM_NVCL: - return OsNVCL; - case ZigLLVM_AMDHSA: - return OsAMDHSA; - case ZigLLVM_PS4: - return OsPS4; - case ZigLLVM_ELFIAMCU: - return OsELFIAMCU; - case ZigLLVM_TvOS: - return OsTvOS; - case ZigLLVM_WatchOS: - return OsWatchOS; - case ZigLLVM_Mesa3D: - return OsMesa3D; - case ZigLLVM_Contiki: - return OsContiki; - case ZigLLVM_AMDPAL: - return OsAMDPAL; - case ZigLLVM_HermitCore: - return OsHermitCore; - case ZigLLVM_Hurd: - return OsHurd; - case ZigLLVM_WASI: - return OsWASI; - case ZigLLVM_Emscripten: - return OsEmscripten; - } - zig_unreachable(); -} - -static void get_native_target(ZigTarget *target) { - // first zero initialize - *target = {}; - - ZigLLVM_OSType os_type; - ZigLLVM_ObjectFormatType oformat; // ignored; based on arch/os - ZigLLVMGetNativeTarget( - &target->arch, - &target->vendor, - &os_type, - &target->abi, - &oformat); - target->os = get_zig_os_type(os_type); - target->is_native_os = true; - target->is_native_cpu = true; - if (target->abi == ZigLLVM_UnknownEnvironment) { - target->abi = target_default_abi(target->arch, target->os); - } - if (target_is_glibc(target)) { - target->glibc_or_darwin_version = heap::c_allocator.create(); - target_init_default_glibc_version(target); - } -} - -Error stage2_target_parse(struct ZigTarget *target, const char *zig_triple, const char *mcpu, - const char *dynamic_linker) -{ - Error err; - - if (zig_triple != nullptr && strcmp(zig_triple, "native") == 0) { - zig_triple = nullptr; - } - - if (zig_triple == nullptr) { - get_native_target(target); - - if (mcpu == nullptr) { - target->llvm_cpu_name = ZigLLVMGetHostCPUName(); - target->llvm_cpu_features = ZigLLVMGetNativeFeatures(); - target->cache_hash = "native\n\n"; - } else if (strcmp(mcpu, "baseline") == 0) { - target->is_native_os = false; - target->is_native_cpu = false; - target->llvm_cpu_name = ""; - target->llvm_cpu_features = ""; - target->cache_hash = "baseline\n\n"; - } else { - const char *msg = "stage0 can't handle CPU/features in the target"; - stage2_panic(msg, strlen(msg)); - } - } else { - // first initialize all to zero - *target = {}; - - SplitIterator it = memSplit(str(zig_triple), str("-")); - - Optional> opt_archsub = SplitIterator_next(&it); - Optional> opt_os = SplitIterator_next(&it); - Optional> opt_abi = SplitIterator_next(&it); - - if (!opt_archsub.is_some) - return ErrorMissingArchitecture; - - if ((err = target_parse_arch(&target->arch, (char*)opt_archsub.value.ptr, opt_archsub.value.len))) { - return err; - } - - if (!opt_os.is_some) - return ErrorMissingOperatingSystem; - - if ((err = target_parse_os(&target->os, (char*)opt_os.value.ptr, opt_os.value.len))) { - return err; - } - - if (opt_abi.is_some) { - if ((err = target_parse_abi(&target->abi, (char*)opt_abi.value.ptr, opt_abi.value.len))) { - return err; - } - } else { - target->abi = target_default_abi(target->arch, target->os); - } - - if (mcpu != nullptr && strcmp(mcpu, "baseline") != 0) { - const char *msg = "stage0 can't handle CPU/features in the target"; - stage2_panic(msg, strlen(msg)); - } - target->cache_hash = "\n\n"; - } - - target->cache_hash_len = strlen(target->cache_hash); - - if (dynamic_linker != nullptr) { - target->dynamic_linker = dynamic_linker; - } - - return ErrorNone; -} - -int stage2_cmd_targets(const char *zig_triple, const char *mcpu, const char *dynamic_linker) { - const char *msg = "stage0 called stage2_cmd_targets"; - stage2_panic(msg, strlen(msg)); -} - -enum Error stage2_libc_parse(struct Stage2LibCInstallation *libc, const char *libc_file) { - libc->include_dir = "/dummy/include"; - libc->include_dir_len = strlen(libc->include_dir); - libc->sys_include_dir = "/dummy/sys/include"; - libc->sys_include_dir_len = strlen(libc->sys_include_dir); - libc->crt_dir = ""; - libc->crt_dir_len = strlen(libc->crt_dir); - libc->msvc_lib_dir = ""; - libc->msvc_lib_dir_len = strlen(libc->msvc_lib_dir); - libc->kernel32_lib_dir = ""; - libc->kernel32_lib_dir_len = strlen(libc->kernel32_lib_dir); - return ErrorNone; -} - -enum Error stage2_libc_render(struct Stage2LibCInstallation *self, FILE *file) { - const char *msg = "stage0 called stage2_libc_render"; - stage2_panic(msg, strlen(msg)); -} - -enum Error stage2_libc_find_native(struct Stage2LibCInstallation *libc) { - const char *msg = "stage0 called stage2_libc_find_native"; - stage2_panic(msg, strlen(msg)); -} - -enum Error stage2_detect_native_paths(struct Stage2NativePaths *native_paths) { - native_paths->include_dirs_ptr = nullptr; - native_paths->include_dirs_len = 0; - - native_paths->lib_dirs_ptr = nullptr; - native_paths->lib_dirs_len = 0; - - native_paths->rpaths_ptr = nullptr; - native_paths->rpaths_len = 0; - - native_paths->warnings_ptr = nullptr; - native_paths->warnings_len = 0; - - return ErrorNone; -} - -void stage2_clang_arg_iterator(struct Stage2ClangArgIterator *it, - size_t argc, char **argv) -{ - const char *msg = "stage0 called stage2_clang_arg_iterator"; - stage2_panic(msg, strlen(msg)); -} - -enum Error stage2_clang_arg_next(struct Stage2ClangArgIterator *it) { - const char *msg = "stage0 called stage2_clang_arg_next"; - stage2_panic(msg, strlen(msg)); -} - -const bool stage2_is_zig0 = true; diff --git a/src/stage2.h b/src/stage2.h deleted file mode 100644 index 38a1f77d4611898e6cb8143c9d1402a24980ea78..0000000000000000000000000000000000000000 --- a/src/stage2.h +++ /dev/null @@ -1,388 +0,0 @@ -/* - * Copyright (c) 2019 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#ifndef ZIG_STAGE2_H -#define ZIG_STAGE2_H - -#include -#include -#include - -#include "zig_llvm.h" - -#ifdef __cplusplus -#define ZIG_EXTERN_C extern "C" -#else -#define ZIG_EXTERN_C -#endif - -#if defined(_MSC_VER) -#define ZIG_ATTRIBUTE_NORETURN __declspec(noreturn) -#else -#define ZIG_ATTRIBUTE_NORETURN __attribute__((noreturn)) -#endif - -// ABI warning: the types and declarations in this file must match both those in -// stage2.cpp and src-self-hosted/stage2.zig. - -// ABI warning -enum Error { - ErrorNone, - ErrorNoMem, - ErrorInvalidFormat, - ErrorSemanticAnalyzeFail, - ErrorAccess, - ErrorInterrupted, - ErrorSystemResources, - ErrorFileNotFound, - ErrorFileSystem, - ErrorFileTooBig, - ErrorDivByZero, - ErrorOverflow, - ErrorPathAlreadyExists, - ErrorUnexpected, - ErrorExactDivRemainder, - ErrorNegativeDenominator, - ErrorShiftedOutOneBits, - ErrorCCompileErrors, - ErrorEndOfFile, - ErrorIsDir, - ErrorNotDir, - ErrorUnsupportedOperatingSystem, - ErrorSharingViolation, - ErrorPipeBusy, - ErrorPrimitiveTypeNotFound, - ErrorCacheUnavailable, - ErrorPathTooLong, - ErrorCCompilerCannotFindFile, - ErrorNoCCompilerInstalled, - ErrorReadingDepFile, - ErrorInvalidDepFile, - ErrorMissingArchitecture, - ErrorMissingOperatingSystem, - ErrorUnknownArchitecture, - ErrorUnknownOperatingSystem, - ErrorUnknownABI, - ErrorInvalidFilename, - ErrorDiskQuota, - ErrorDiskSpace, - ErrorUnexpectedWriteFailure, - ErrorUnexpectedSeekFailure, - ErrorUnexpectedFileTruncationFailure, - ErrorUnimplemented, - ErrorOperationAborted, - ErrorBrokenPipe, - ErrorNoSpaceLeft, - ErrorNotLazy, - ErrorIsAsync, - ErrorImportOutsidePkgPath, - ErrorUnknownCpu, - ErrorUnknownCpuFeature, - ErrorInvalidCpuFeatures, - ErrorInvalidLlvmCpuFeaturesFormat, - ErrorUnknownApplicationBinaryInterface, - ErrorASTUnitFailure, - ErrorBadPathName, - ErrorSymLinkLoop, - ErrorProcessFdQuotaExceeded, - ErrorSystemFdQuotaExceeded, - ErrorNoDevice, - ErrorDeviceBusy, - ErrorUnableToSpawnCCompiler, - ErrorCCompilerExitCode, - ErrorCCompilerCrashed, - ErrorCCompilerCannotFindHeaders, - ErrorLibCRuntimeNotFound, - ErrorLibCStdLibHeaderNotFound, - ErrorLibCKernel32LibNotFound, - ErrorUnsupportedArchitecture, - ErrorWindowsSdkNotFound, - ErrorUnknownDynamicLinkerPath, - ErrorTargetHasNoDynamicLinker, - ErrorInvalidAbiVersion, - ErrorInvalidOperatingSystemVersion, - ErrorUnknownClangOption, - ErrorNestedResponseFile, - ErrorZigIsTheCCompiler, - ErrorFileBusy, - ErrorLocked, -}; - -// ABI warning -struct Stage2ErrorMsg { - const char *filename_ptr; // can be null - size_t filename_len; - const char *msg_ptr; - size_t msg_len; - const char *source; // valid until the ASTUnit is freed. can be null - unsigned line; // 0 based - unsigned column; // 0 based - unsigned offset; // byte offset into source -}; - -// ABI warning -struct Stage2Ast; - -// ABI warning -ZIG_EXTERN_C enum Error stage2_translate_c(struct Stage2Ast **out_ast, - struct Stage2ErrorMsg **out_errors_ptr, size_t *out_errors_len, - const char **args_begin, const char **args_end, const char *resources_path); - -// ABI warning -ZIG_EXTERN_C void stage2_free_clang_errors(struct Stage2ErrorMsg *ptr, size_t len); - -// ABI warning -ZIG_EXTERN_C void stage2_render_ast(struct Stage2Ast *ast, FILE *output_file); - -// ABI warning -ZIG_EXTERN_C void stage2_zen(const char **ptr, size_t *len); - -// ABI warning -ZIG_EXTERN_C int stage2_env(int argc, char **argv); - -// ABI warning -ZIG_EXTERN_C void stage2_attach_segfault_handler(void); - -// ABI warning -ZIG_EXTERN_C ZIG_ATTRIBUTE_NORETURN void stage2_panic(const char *ptr, size_t len); - -// ABI warning -ZIG_EXTERN_C int stage2_fmt(int argc, char **argv); - -// ABI warning -struct stage2_DepTokenizer { - void *handle; -}; - -// ABI warning -struct stage2_DepNextResult { - enum TypeId { - error, - null, - target, - prereq, - }; - - TypeId type_id; - - // when ent == error --> error text - // when ent == null --> undefined - // when ent == target --> target pathname - // when ent == prereq --> prereq pathname - const char *textz; -}; - -// ABI warning -ZIG_EXTERN_C stage2_DepTokenizer stage2_DepTokenizer_init(const char *input, size_t len); - -// ABI warning -ZIG_EXTERN_C void stage2_DepTokenizer_deinit(stage2_DepTokenizer *self); - -// ABI warning -ZIG_EXTERN_C stage2_DepNextResult stage2_DepTokenizer_next(stage2_DepTokenizer *self); - -// ABI warning -struct Stage2Progress; -// ABI warning -struct Stage2ProgressNode; -// ABI warning -ZIG_EXTERN_C Stage2Progress *stage2_progress_create(void); -// ABI warning -ZIG_EXTERN_C void stage2_progress_disable_tty(Stage2Progress *progress); -// ABI warning -ZIG_EXTERN_C void stage2_progress_destroy(Stage2Progress *progress); -// ABI warning -ZIG_EXTERN_C Stage2ProgressNode *stage2_progress_start_root(Stage2Progress *progress, - const char *name_ptr, size_t name_len, size_t estimated_total_items); -// ABI warning -ZIG_EXTERN_C Stage2ProgressNode *stage2_progress_start(Stage2ProgressNode *node, - const char *name_ptr, size_t name_len, size_t estimated_total_items); -// ABI warning -ZIG_EXTERN_C void stage2_progress_end(Stage2ProgressNode *node); -// ABI warning -ZIG_EXTERN_C void stage2_progress_complete_one(Stage2ProgressNode *node); -// ABI warning -ZIG_EXTERN_C void stage2_progress_update_node(Stage2ProgressNode *node, - size_t completed_count, size_t estimated_total_items); - -// ABI warning -struct Stage2LibCInstallation { - const char *include_dir; - size_t include_dir_len; - const char *sys_include_dir; - size_t sys_include_dir_len; - const char *crt_dir; - size_t crt_dir_len; - const char *msvc_lib_dir; - size_t msvc_lib_dir_len; - const char *kernel32_lib_dir; - size_t kernel32_lib_dir_len; -}; - -// ABI warning -ZIG_EXTERN_C enum Error stage2_libc_parse(struct Stage2LibCInstallation *libc, const char *libc_file); -// ABI warning -ZIG_EXTERN_C enum Error stage2_libc_render(struct Stage2LibCInstallation *self, FILE *file); -// ABI warning -ZIG_EXTERN_C enum Error stage2_libc_find_native(struct Stage2LibCInstallation *libc); - -// ABI warning -// Synchronize with target.cpp::os_list -enum Os { - OsFreestanding, - OsAnanas, - OsCloudABI, - OsDragonFly, - OsFreeBSD, - OsFuchsia, - OsIOS, - OsKFreeBSD, - OsLinux, - OsLv2, // PS3 - OsMacOSX, - OsNetBSD, - OsOpenBSD, - OsSolaris, - OsWindows, - OsHaiku, - OsMinix, - OsRTEMS, - OsNaCl, // Native Client - OsCNK, // BG/P Compute-Node Kernel - OsAIX, - OsCUDA, // NVIDIA CUDA - OsNVCL, // NVIDIA OpenCL - OsAMDHSA, // AMD HSA Runtime - OsPS4, - OsELFIAMCU, - OsTvOS, // Apple tvOS - OsWatchOS, // Apple watchOS - OsMesa3D, - OsContiki, - OsAMDPAL, - OsHermitCore, - OsHurd, - OsWASI, - OsEmscripten, - OsUefi, - OsOther, -}; - -// ABI warning -struct Stage2SemVer { - uint32_t major; - uint32_t minor; - uint32_t patch; -}; - -// ABI warning -struct ZigTarget { - enum ZigLLVM_ArchType arch; - enum ZigLLVM_VendorType vendor; - - enum ZigLLVM_EnvironmentType abi; - Os os; - - bool is_native_os; - bool is_native_cpu; - - // null means default. this is double-purposed to be darwin min version - struct Stage2SemVer *glibc_or_darwin_version; - - const char *llvm_cpu_name; - const char *llvm_cpu_features; - const char *cpu_builtin_str; - const char *cache_hash; - size_t cache_hash_len; - const char *os_builtin_str; - const char *dynamic_linker; - const char *standard_dynamic_linker_path; - - const char **llvm_cpu_features_asm_ptr; - size_t llvm_cpu_features_asm_len; -}; - -// ABI warning -ZIG_EXTERN_C enum Error stage2_target_parse(struct ZigTarget *target, const char *zig_triple, const char *mcpu, - const char *dynamic_linker); - -// ABI warning -ZIG_EXTERN_C int stage2_cmd_targets(const char *zig_triple, const char *mcpu, const char *dynamic_linker); - - -// ABI warning -struct Stage2NativePaths { - const char **include_dirs_ptr; - size_t include_dirs_len; - const char **lib_dirs_ptr; - size_t lib_dirs_len; - const char **rpaths_ptr; - size_t rpaths_len; - const char **warnings_ptr; - size_t warnings_len; -}; -// ABI warning -ZIG_EXTERN_C enum Error stage2_detect_native_paths(struct Stage2NativePaths *native_paths); - -// ABI warning -enum Stage2ClangArg { - Stage2ClangArgTarget, - Stage2ClangArgO, - Stage2ClangArgC, - Stage2ClangArgOther, - Stage2ClangArgPositional, - Stage2ClangArgL, - Stage2ClangArgIgnore, - Stage2ClangArgDriverPunt, - Stage2ClangArgPIC, - Stage2ClangArgNoPIC, - Stage2ClangArgNoStdLib, - Stage2ClangArgNoStdLibCpp, - Stage2ClangArgShared, - Stage2ClangArgRDynamic, - Stage2ClangArgWL, - Stage2ClangArgPreprocessOrAsm, - Stage2ClangArgOptimize, - Stage2ClangArgDebug, - Stage2ClangArgSanitize, - Stage2ClangArgLinkerScript, - Stage2ClangArgVerboseCmds, - Stage2ClangArgForLinker, - Stage2ClangArgLinkerInputZ, - Stage2ClangArgLibDir, - Stage2ClangArgMCpu, - Stage2ClangArgDepFile, - Stage2ClangArgFrameworkDir, - Stage2ClangArgFramework, - Stage2ClangArgNoStdLibInc, -}; - -// ABI warning -struct Stage2ClangArgIterator { - bool has_next; - enum Stage2ClangArg kind; - const char *only_arg; - const char *second_arg; - const char **other_args_ptr; - size_t other_args_len; - const char **argv_ptr; - size_t argv_len; - size_t next_index; - size_t root_args; -}; - -// ABI warning -ZIG_EXTERN_C void stage2_clang_arg_iterator(struct Stage2ClangArgIterator *it, - size_t argc, char **argv); - -// ABI warning -ZIG_EXTERN_C enum Error stage2_clang_arg_next(struct Stage2ClangArgIterator *it); - -// ABI warning -ZIG_EXTERN_C const bool stage2_is_zig0; - -#endif diff --git a/src/target.cpp b/src/target.cpp deleted file mode 100644 index dff134a01d1fc78863183407855793795767db9c..0000000000000000000000000000000000000000 --- a/src/target.cpp +++ /dev/null @@ -1,1343 +0,0 @@ -/* - * Copyright (c) 2016 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#include "buffer.hpp" -#include "error.hpp" -#include "target.hpp" -#include "util.hpp" -#include "os.hpp" -#include "compiler.hpp" -#include "glibc.hpp" - -#include - -static const ZigLLVM_ArchType arch_list[] = { - ZigLLVM_arm, // ARM (little endian): arm, armv.*, xscale - ZigLLVM_armeb, // ARM (big endian): armeb - ZigLLVM_aarch64, // AArch64 (little endian): aarch64 - ZigLLVM_aarch64_be, // AArch64 (big endian): aarch64_be - ZigLLVM_aarch64_32, // AArch64 (little endian) ILP32: aarch64_32 - ZigLLVM_arc, // ARC: Synopsys ARC - ZigLLVM_avr, // AVR: Atmel AVR microcontroller - ZigLLVM_bpfel, // eBPF or extended BPF or 64-bit BPF (little endian) - ZigLLVM_bpfeb, // eBPF or extended BPF or 64-bit BPF (big endian) - ZigLLVM_hexagon, // Hexagon: hexagon - ZigLLVM_mips, // MIPS: mips, mipsallegrex, mipsr6 - ZigLLVM_mipsel, // MIPSEL: mipsel, mipsallegrexe, mipsr6el - ZigLLVM_mips64, // MIPS64: mips64, mips64r6, mipsn32, mipsn32r6 - ZigLLVM_mips64el, // MIPS64EL: mips64el, mips64r6el, mipsn32el, mipsn32r6el - ZigLLVM_msp430, // MSP430: msp430 - ZigLLVM_ppc, // PPC: powerpc - ZigLLVM_ppc64, // PPC64: powerpc64, ppu - ZigLLVM_ppc64le, // PPC64LE: powerpc64le - ZigLLVM_r600, // R600: AMD GPUs HD2XXX - HD6XXX - ZigLLVM_amdgcn, // AMDGCN: AMD GCN GPUs - ZigLLVM_riscv32, // RISC-V (32-bit): riscv32 - ZigLLVM_riscv64, // RISC-V (64-bit): riscv64 - ZigLLVM_sparc, // Sparc: sparc - ZigLLVM_sparcv9, // Sparcv9: Sparcv9 - ZigLLVM_sparcel, // Sparc: (endianness = little). NB: 'Sparcle' is a CPU variant - ZigLLVM_systemz, // SystemZ: s390x - ZigLLVM_tce, // TCE (http://tce.cs.tut.fi/): tce - ZigLLVM_tcele, // TCE little endian (http://tce.cs.tut.fi/): tcele - ZigLLVM_thumb, // Thumb (little endian): thumb, thumbv.* - ZigLLVM_thumbeb, // Thumb (big endian): thumbeb - ZigLLVM_x86, // X86: i[3-9]86 - ZigLLVM_x86_64, // X86-64: amd64, x86_64 - ZigLLVM_xcore, // XCore: xcore - ZigLLVM_nvptx, // NVPTX: 32-bit - ZigLLVM_nvptx64, // NVPTX: 64-bit - ZigLLVM_le32, // le32: generic little-endian 32-bit CPU (PNaCl) - ZigLLVM_le64, // le64: generic little-endian 64-bit CPU (PNaCl) - ZigLLVM_amdil, // AMDIL - ZigLLVM_amdil64, // AMDIL with 64-bit pointers - ZigLLVM_hsail, // AMD HSAIL - ZigLLVM_hsail64, // AMD HSAIL with 64-bit pointers - ZigLLVM_spir, // SPIR: standard portable IR for OpenCL 32-bit version - ZigLLVM_spir64, // SPIR: standard portable IR for OpenCL 64-bit version - ZigLLVM_kalimba, // Kalimba: generic kalimba - ZigLLVM_shave, // SHAVE: Movidius vector VLIW processors - ZigLLVM_lanai, // Lanai: Lanai 32-bit - ZigLLVM_wasm32, // WebAssembly with 32-bit pointers - ZigLLVM_wasm64, // WebAssembly with 64-bit pointers - ZigLLVM_renderscript32, // 32-bit RenderScript - ZigLLVM_renderscript64, // 64-bit RenderScript - ZigLLVM_ve, // NEC SX-Aurora Vector Engine -}; - -static const ZigLLVM_VendorType vendor_list[] = { - ZigLLVM_Apple, - ZigLLVM_PC, - ZigLLVM_SCEI, - ZigLLVM_BGP, - ZigLLVM_BGQ, - ZigLLVM_Freescale, - ZigLLVM_IBM, - ZigLLVM_ImaginationTechnologies, - ZigLLVM_MipsTechnologies, - ZigLLVM_NVIDIA, - ZigLLVM_CSR, - ZigLLVM_Myriad, - ZigLLVM_AMD, - ZigLLVM_Mesa, - ZigLLVM_SUSE, -}; - -static const Os os_list[] = { - OsFreestanding, - OsAnanas, - OsCloudABI, - OsDragonFly, - OsFreeBSD, - OsFuchsia, - OsIOS, - OsKFreeBSD, - OsLinux, - OsLv2, // PS3 - OsMacOSX, - OsNetBSD, - OsOpenBSD, - OsSolaris, - OsWindows, - OsHaiku, - OsMinix, - OsRTEMS, - OsNaCl, // Native Client - OsCNK, // BG/P Compute-Node Kernel - OsAIX, - OsCUDA, // NVIDIA CUDA - OsNVCL, // NVIDIA OpenCL - OsAMDHSA, // AMD HSA Runtime - OsPS4, - OsELFIAMCU, - OsTvOS, // Apple tvOS - OsWatchOS, // Apple watchOS - OsMesa3D, - OsContiki, - OsAMDPAL, - OsHermitCore, - OsHurd, - OsWASI, - OsEmscripten, - OsUefi, - OsOther, -}; - -// Coordinate with zig_llvm.h -static const ZigLLVM_EnvironmentType abi_list[] = { - ZigLLVM_UnknownEnvironment, - - ZigLLVM_GNU, - ZigLLVM_GNUABIN32, - ZigLLVM_GNUABI64, - ZigLLVM_GNUEABI, - ZigLLVM_GNUEABIHF, - ZigLLVM_GNUX32, - ZigLLVM_CODE16, - ZigLLVM_EABI, - ZigLLVM_EABIHF, - ZigLLVM_Android, - ZigLLVM_Musl, - ZigLLVM_MuslEABI, - ZigLLVM_MuslEABIHF, - - ZigLLVM_MSVC, - ZigLLVM_Itanium, - ZigLLVM_Cygnus, - ZigLLVM_CoreCLR, - ZigLLVM_Simulator, - ZigLLVM_MacABI, -}; - -static const ZigLLVM_ObjectFormatType oformat_list[] = { - ZigLLVM_UnknownObjectFormat, - ZigLLVM_COFF, - ZigLLVM_ELF, - ZigLLVM_MachO, - ZigLLVM_Wasm, -}; - -size_t target_oformat_count(void) { - return array_length(oformat_list); -} - -ZigLLVM_ObjectFormatType target_oformat_enum(size_t index) { - assert(index < array_length(oformat_list)); - return oformat_list[index]; -} - -const char *target_oformat_name(ZigLLVM_ObjectFormatType oformat) { - switch (oformat) { - case ZigLLVM_UnknownObjectFormat: return "unknown"; - case ZigLLVM_COFF: return "coff"; - case ZigLLVM_ELF: return "elf"; - case ZigLLVM_MachO: return "macho"; - case ZigLLVM_Wasm: return "wasm"; - case ZigLLVM_XCOFF: return "xcoff"; - } - zig_unreachable(); -} - -size_t target_arch_count(void) { - return array_length(arch_list); -} - -ZigLLVM_ArchType target_arch_enum(size_t index) { - assert(index < array_length(arch_list)); - return arch_list[index]; -} - -size_t target_vendor_count(void) { - return array_length(vendor_list); -} - -ZigLLVM_VendorType target_vendor_enum(size_t index) { - assert(index < array_length(vendor_list)); - return vendor_list[index]; -} - -size_t target_os_count(void) { - return array_length(os_list); -} -Os target_os_enum(size_t index) { - assert(index < array_length(os_list)); - return os_list[index]; -} - -ZigLLVM_OSType get_llvm_os_type(Os os_type) { - switch (os_type) { - case OsFreestanding: - case OsOther: - return ZigLLVM_UnknownOS; - case OsAnanas: - return ZigLLVM_Ananas; - case OsCloudABI: - return ZigLLVM_CloudABI; - case OsDragonFly: - return ZigLLVM_DragonFly; - case OsFreeBSD: - return ZigLLVM_FreeBSD; - case OsFuchsia: - return ZigLLVM_Fuchsia; - case OsIOS: - return ZigLLVM_IOS; - case OsKFreeBSD: - return ZigLLVM_KFreeBSD; - case OsLinux: - return ZigLLVM_Linux; - case OsLv2: - return ZigLLVM_Lv2; - case OsMacOSX: - return ZigLLVM_MacOSX; - case OsNetBSD: - return ZigLLVM_NetBSD; - case OsOpenBSD: - return ZigLLVM_OpenBSD; - case OsSolaris: - return ZigLLVM_Solaris; - case OsWindows: - case OsUefi: - return ZigLLVM_Win32; - case OsHaiku: - return ZigLLVM_Haiku; - case OsMinix: - return ZigLLVM_Minix; - case OsRTEMS: - return ZigLLVM_RTEMS; - case OsNaCl: - return ZigLLVM_NaCl; - case OsCNK: - return ZigLLVM_CNK; - case OsAIX: - return ZigLLVM_AIX; - case OsCUDA: - return ZigLLVM_CUDA; - case OsNVCL: - return ZigLLVM_NVCL; - case OsAMDHSA: - return ZigLLVM_AMDHSA; - case OsPS4: - return ZigLLVM_PS4; - case OsELFIAMCU: - return ZigLLVM_ELFIAMCU; - case OsTvOS: - return ZigLLVM_TvOS; - case OsWatchOS: - return ZigLLVM_WatchOS; - case OsMesa3D: - return ZigLLVM_Mesa3D; - case OsContiki: - return ZigLLVM_Contiki; - case OsAMDPAL: - return ZigLLVM_AMDPAL; - case OsHermitCore: - return ZigLLVM_HermitCore; - case OsHurd: - return ZigLLVM_Hurd; - case OsWASI: - return ZigLLVM_WASI; - case OsEmscripten: - return ZigLLVM_Emscripten; - } - zig_unreachable(); -} - -const char *target_os_name(Os os_type) { - switch (os_type) { - case OsFreestanding: - return "freestanding"; - case OsUefi: - return "uefi"; - case OsOther: - return "other"; - case OsAnanas: - case OsCloudABI: - case OsDragonFly: - case OsFreeBSD: - case OsFuchsia: - case OsIOS: - case OsKFreeBSD: - case OsLinux: - case OsLv2: // PS3 - case OsMacOSX: - case OsNetBSD: - case OsOpenBSD: - case OsSolaris: - case OsWindows: - case OsHaiku: - case OsMinix: - case OsRTEMS: - case OsNaCl: // Native Client - case OsCNK: // BG/P Compute-Node Kernel - case OsAIX: - case OsCUDA: // NVIDIA CUDA - case OsNVCL: // NVIDIA OpenCL - case OsAMDHSA: // AMD HSA Runtime - case OsPS4: - case OsELFIAMCU: - case OsTvOS: // Apple tvOS - case OsWatchOS: // Apple watchOS - case OsMesa3D: - case OsContiki: - case OsAMDPAL: - case OsHermitCore: - case OsHurd: - case OsWASI: - case OsEmscripten: - return ZigLLVMGetOSTypeName(get_llvm_os_type(os_type)); - } - zig_unreachable(); -} - -size_t target_abi_count(void) { - return array_length(abi_list); -} -ZigLLVM_EnvironmentType target_abi_enum(size_t index) { - assert(index < array_length(abi_list)); - return abi_list[index]; -} -const char *target_abi_name(ZigLLVM_EnvironmentType abi) { - if (abi == ZigLLVM_UnknownEnvironment) - return "none"; - return ZigLLVMGetEnvironmentTypeName(abi); -} - -Error target_parse_glibc_version(Stage2SemVer *glibc_ver, const char *text) { - glibc_ver->major = 2; - glibc_ver->minor = 0; - glibc_ver->patch = 0; - SplitIterator it = memSplit(str(text), str("GLIBC_.")); - { - Optional> opt_component = SplitIterator_next(&it); - if (!opt_component.is_some) return ErrorUnknownABI; - glibc_ver->major = strtoul(buf_ptr(buf_create_from_slice(opt_component.value)), nullptr, 10); - } - { - Optional> opt_component = SplitIterator_next(&it); - if (!opt_component.is_some) return ErrorNone; - glibc_ver->minor = strtoul(buf_ptr(buf_create_from_slice(opt_component.value)), nullptr, 10); - } - { - Optional> opt_component = SplitIterator_next(&it); - if (!opt_component.is_some) return ErrorNone; - glibc_ver->patch = strtoul(buf_ptr(buf_create_from_slice(opt_component.value)), nullptr, 10); - } - return ErrorNone; -} - -void target_init_default_glibc_version(ZigTarget *target) { - *target->glibc_or_darwin_version = {2, 17, 0}; -} - -Error target_parse_arch(ZigLLVM_ArchType *out_arch, const char *arch_ptr, size_t arch_len) { - *out_arch = ZigLLVM_UnknownArch; - for (size_t arch_i = 0; arch_i < array_length(arch_list); arch_i += 1) { - ZigLLVM_ArchType arch = arch_list[arch_i]; - if (mem_eql_str(arch_ptr, arch_len, target_arch_name(arch))) { - *out_arch = arch; - return ErrorNone; - } - } - return ErrorUnknownArchitecture; -} - -Error target_parse_os(Os *out_os, const char *os_ptr, size_t os_len) { - for (size_t i = 0; i < array_length(os_list); i += 1) { - Os os = os_list[i]; - const char *os_name = target_os_name(os); - if (mem_eql_str(os_ptr, os_len, os_name)) { - *out_os = os; - return ErrorNone; - } - } - return ErrorUnknownOperatingSystem; -} - -Error target_parse_abi(ZigLLVM_EnvironmentType *out_abi, const char *abi_ptr, size_t abi_len) { - for (size_t i = 0; i < array_length(abi_list); i += 1) { - ZigLLVM_EnvironmentType abi = abi_list[i]; - const char *abi_name = target_abi_name(abi); - if (mem_eql_str(abi_ptr, abi_len, abi_name)) { - *out_abi = abi; - return ErrorNone; - } - } - return ErrorUnknownABI; -} - -Error target_parse_triple(ZigTarget *target, const char *triple, const char *mcpu, const char *dynamic_linker) { - return stage2_target_parse(target, triple, mcpu, dynamic_linker); -} - -const char *target_arch_name(ZigLLVM_ArchType arch) { - return ZigLLVMGetArchTypeName(arch); -} - -void init_all_targets(void) { - LLVMInitializeAllTargets(); - LLVMInitializeAllTargetInfos(); - LLVMInitializeAllTargetMCs(); - LLVMInitializeAllAsmPrinters(); - LLVMInitializeAllAsmParsers(); -} - -void target_triple_zig(Buf *triple, const ZigTarget *target) { - buf_resize(triple, 0); - buf_appendf(triple, "%s-%s-%s", - target_arch_name(target->arch), - target_os_name(target->os), - target_abi_name(target->abi)); -} - -void target_triple_llvm(Buf *triple, const ZigTarget *target) { - buf_resize(triple, 0); - buf_appendf(triple, "%s-%s-%s-%s", - ZigLLVMGetArchTypeName(target->arch), - ZigLLVMGetVendorTypeName(target->vendor), - ZigLLVMGetOSTypeName(get_llvm_os_type(target->os)), - ZigLLVMGetEnvironmentTypeName(target->abi)); -} - -bool target_os_is_darwin(Os os) { - switch (os) { - case OsMacOSX: - case OsIOS: - case OsWatchOS: - case OsTvOS: - return true; - default: - return false; - } -} - -ZigLLVM_ObjectFormatType target_object_format(const ZigTarget *target) { - if (target->os == OsUefi || target->os == OsWindows) { - return ZigLLVM_COFF; - } else if (target_os_is_darwin(target->os)) { - return ZigLLVM_MachO; - } - if (target->arch == ZigLLVM_wasm32 || - target->arch == ZigLLVM_wasm64) - { - return ZigLLVM_Wasm; - } - return ZigLLVM_ELF; -} - -// See lib/Support/Triple.cpp in LLVM for the source of this data. -// getArchPointerBitWidth -uint32_t target_arch_pointer_bit_width(ZigLLVM_ArchType arch) { - switch (arch) { - case ZigLLVM_UnknownArch: - return 0; - - case ZigLLVM_avr: - case ZigLLVM_msp430: - return 16; - - case ZigLLVM_arc: - case ZigLLVM_arm: - case ZigLLVM_armeb: - case ZigLLVM_hexagon: - case ZigLLVM_le32: - case ZigLLVM_mips: - case ZigLLVM_mipsel: - case ZigLLVM_nvptx: - case ZigLLVM_ppc: - case ZigLLVM_r600: - case ZigLLVM_riscv32: - case ZigLLVM_sparc: - case ZigLLVM_sparcel: - case ZigLLVM_tce: - case ZigLLVM_tcele: - case ZigLLVM_thumb: - case ZigLLVM_thumbeb: - case ZigLLVM_x86: - case ZigLLVM_xcore: - case ZigLLVM_amdil: - case ZigLLVM_hsail: - case ZigLLVM_spir: - case ZigLLVM_kalimba: - case ZigLLVM_lanai: - case ZigLLVM_shave: - case ZigLLVM_wasm32: - case ZigLLVM_renderscript32: - case ZigLLVM_aarch64_32: - return 32; - - case ZigLLVM_aarch64: - case ZigLLVM_aarch64_be: - case ZigLLVM_amdgcn: - case ZigLLVM_bpfel: - case ZigLLVM_bpfeb: - case ZigLLVM_le64: - case ZigLLVM_mips64: - case ZigLLVM_mips64el: - case ZigLLVM_nvptx64: - case ZigLLVM_ppc64: - case ZigLLVM_ppc64le: - case ZigLLVM_riscv64: - case ZigLLVM_sparcv9: - case ZigLLVM_systemz: - case ZigLLVM_x86_64: - case ZigLLVM_amdil64: - case ZigLLVM_hsail64: - case ZigLLVM_spir64: - case ZigLLVM_wasm64: - case ZigLLVM_renderscript64: - case ZigLLVM_ve: - return 64; - } - zig_unreachable(); -} - -uint32_t target_arch_largest_atomic_bits(ZigLLVM_ArchType arch) { - switch (arch) { - case ZigLLVM_UnknownArch: - zig_unreachable(); - - case ZigLLVM_avr: - case ZigLLVM_msp430: - return 16; - - case ZigLLVM_arc: - case ZigLLVM_arm: - case ZigLLVM_armeb: - case ZigLLVM_hexagon: - case ZigLLVM_le32: - case ZigLLVM_mips: - case ZigLLVM_mipsel: - case ZigLLVM_nvptx: - case ZigLLVM_ppc: - case ZigLLVM_r600: - case ZigLLVM_riscv32: - case ZigLLVM_sparc: - case ZigLLVM_sparcel: - case ZigLLVM_tce: - case ZigLLVM_tcele: - case ZigLLVM_thumb: - case ZigLLVM_thumbeb: - case ZigLLVM_x86: - case ZigLLVM_xcore: - case ZigLLVM_amdil: - case ZigLLVM_hsail: - case ZigLLVM_spir: - case ZigLLVM_kalimba: - case ZigLLVM_lanai: - case ZigLLVM_shave: - case ZigLLVM_wasm32: - case ZigLLVM_renderscript32: - return 32; - - case ZigLLVM_aarch64: - case ZigLLVM_aarch64_be: - case ZigLLVM_aarch64_32: - case ZigLLVM_amdgcn: - case ZigLLVM_bpfel: - case ZigLLVM_bpfeb: - case ZigLLVM_le64: - case ZigLLVM_mips64: - case ZigLLVM_mips64el: - case ZigLLVM_nvptx64: - case ZigLLVM_ppc64: - case ZigLLVM_ppc64le: - case ZigLLVM_riscv64: - case ZigLLVM_sparcv9: - case ZigLLVM_systemz: - case ZigLLVM_amdil64: - case ZigLLVM_hsail64: - case ZigLLVM_spir64: - case ZigLLVM_wasm64: - case ZigLLVM_renderscript64: - case ZigLLVM_ve: - return 64; - - case ZigLLVM_x86_64: - return 128; - } - zig_unreachable(); -} - -uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id) { - switch (target->os) { - case OsFreestanding: - case OsOther: - switch (target->arch) { - case ZigLLVM_msp430: - switch (id) { - case CIntTypeShort: - case CIntTypeUShort: - return 16; - case CIntTypeInt: - case CIntTypeUInt: - return 16; - case CIntTypeLong: - case CIntTypeULong: - return 32; - case CIntTypeLongLong: - case CIntTypeULongLong: - return 64; - case CIntTypeCount: - zig_unreachable(); - } - zig_unreachable(); - default: - switch (id) { - case CIntTypeShort: - case CIntTypeUShort: - return 16; - case CIntTypeInt: - case CIntTypeUInt: - return 32; - case CIntTypeLong: - case CIntTypeULong: - return target_arch_pointer_bit_width(target->arch); - case CIntTypeLongLong: - case CIntTypeULongLong: - return 64; - case CIntTypeCount: - zig_unreachable(); - } - } - zig_unreachable(); - case OsLinux: - case OsMacOSX: - case OsFreeBSD: - case OsNetBSD: - case OsDragonFly: - case OsOpenBSD: - case OsWASI: - case OsEmscripten: - switch (id) { - case CIntTypeShort: - case CIntTypeUShort: - return 16; - case CIntTypeInt: - case CIntTypeUInt: - return 32; - case CIntTypeLong: - case CIntTypeULong: - return target_arch_pointer_bit_width(target->arch); - case CIntTypeLongLong: - case CIntTypeULongLong: - return 64; - case CIntTypeCount: - zig_unreachable(); - } - zig_unreachable(); - case OsUefi: - case OsWindows: - switch (id) { - case CIntTypeShort: - case CIntTypeUShort: - return 16; - case CIntTypeInt: - case CIntTypeUInt: - case CIntTypeLong: - case CIntTypeULong: - return 32; - case CIntTypeLongLong: - case CIntTypeULongLong: - return 64; - case CIntTypeCount: - zig_unreachable(); - } - zig_unreachable(); - case OsIOS: - switch (id) { - case CIntTypeShort: - case CIntTypeUShort: - return 16; - case CIntTypeInt: - case CIntTypeUInt: - return 32; - case CIntTypeLong: - case CIntTypeULong: - case CIntTypeLongLong: - case CIntTypeULongLong: - return 64; - case CIntTypeCount: - zig_unreachable(); - } - zig_unreachable(); - case OsAnanas: - case OsCloudABI: - case OsKFreeBSD: - case OsLv2: - case OsSolaris: - case OsHaiku: - case OsMinix: - case OsRTEMS: - case OsNaCl: - case OsCNK: - case OsAIX: - case OsCUDA: - case OsNVCL: - case OsAMDHSA: - case OsPS4: - case OsELFIAMCU: - case OsTvOS: - case OsWatchOS: - case OsMesa3D: - case OsFuchsia: - case OsContiki: - case OsAMDPAL: - case OsHermitCore: - case OsHurd: - zig_panic("TODO c type size in bits for this target"); - } - zig_unreachable(); -} - -bool target_allows_addr_zero(const ZigTarget *target) { - return target->os == OsFreestanding || target->os == OsUefi; -} - -const char *target_o_file_ext(const ZigTarget *target) { - if (target->abi == ZigLLVM_MSVC || - (target->os == OsWindows && !target_abi_is_gnu(target->abi)) || - target->os == OsUefi) - { - return ".obj"; - } else { - return ".o"; - } -} - -const char *target_asm_file_ext(const ZigTarget *target) { - return ".s"; -} - -const char *target_llvm_ir_file_ext(const ZigTarget *target) { - return ".ll"; -} - -const char *target_exe_file_ext(const ZigTarget *target) { - if (target->os == OsWindows) { - return ".exe"; - } else if (target->os == OsUefi) { - return ".efi"; - } else if (target_is_wasm(target)) { - return ".wasm"; - } else { - return ""; - } -} - -const char *target_lib_file_prefix(const ZigTarget *target) { - if ((target->os == OsWindows && !target_abi_is_gnu(target->abi)) || - target->os == OsUefi || - target_is_wasm(target)) - { - return ""; - } else { - return "lib"; - } -} - -const char *target_lib_file_ext(const ZigTarget *target, bool is_static, bool is_versioned, - size_t version_major, size_t version_minor, size_t version_patch) -{ - if (target_is_wasm(target)) { - return ".wasm"; - } - if (target->os == OsWindows || target->os == OsUefi) { - if (is_static) { - if (target->os == OsWindows && target_abi_is_gnu(target->abi)) { - return ".a"; - } else { - return ".lib"; - } - } else { - return ".dll"; - } - } else { - if (is_static) { - return ".a"; - } else if (target_os_is_darwin(target->os)) { - if (is_versioned) { - return buf_ptr(buf_sprintf(".%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".dylib", - version_major, version_minor, version_patch)); - } else { - return ".dylib"; - } - } else { - if (is_versioned) { - return buf_ptr(buf_sprintf(".so.%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize, - version_major, version_minor, version_patch)); - } else { - return ".so"; - } - } - } -} - -bool target_is_android(const ZigTarget *target) { - return target->abi == ZigLLVM_Android; -} - -bool target_can_exec(const ZigTarget *host_target, const ZigTarget *guest_target) { - assert(host_target != nullptr); - - if (guest_target == nullptr) { - // null guest target means that the guest target is native - return true; - } - - if (guest_target->os == host_target->os && guest_target->arch == host_target->arch) { - // OS and arch match - return true; - } - - if (guest_target->os == OsWindows && host_target->os == OsWindows && - host_target->arch == ZigLLVM_x86_64 && guest_target->arch == ZigLLVM_x86) - { - // 64-bit windows can run 32-bit programs - return true; - } - - return false; -} - -const char *arch_stack_pointer_register_name(ZigLLVM_ArchType arch) { - switch (arch) { - case ZigLLVM_UnknownArch: - zig_unreachable(); - case ZigLLVM_x86: - return "esp"; - case ZigLLVM_x86_64: - return "rsp"; - case ZigLLVM_arm: - case ZigLLVM_armeb: - case ZigLLVM_thumb: - case ZigLLVM_thumbeb: - case ZigLLVM_aarch64: - case ZigLLVM_aarch64_be: - case ZigLLVM_aarch64_32: - case ZigLLVM_riscv32: - case ZigLLVM_riscv64: - case ZigLLVM_mipsel: - case ZigLLVM_ppc: - case ZigLLVM_ppc64: - case ZigLLVM_ppc64le: - return "sp"; - - case ZigLLVM_wasm32: - case ZigLLVM_wasm64: - return nullptr; // known to be not available - - case ZigLLVM_amdgcn: - case ZigLLVM_amdil: - case ZigLLVM_amdil64: - case ZigLLVM_arc: - case ZigLLVM_avr: - case ZigLLVM_bpfeb: - case ZigLLVM_bpfel: - case ZigLLVM_hexagon: - case ZigLLVM_lanai: - case ZigLLVM_hsail: - case ZigLLVM_hsail64: - case ZigLLVM_kalimba: - case ZigLLVM_le32: - case ZigLLVM_le64: - case ZigLLVM_mips: - case ZigLLVM_mips64: - case ZigLLVM_mips64el: - case ZigLLVM_msp430: - case ZigLLVM_nvptx: - case ZigLLVM_nvptx64: - case ZigLLVM_r600: - case ZigLLVM_renderscript32: - case ZigLLVM_renderscript64: - case ZigLLVM_shave: - case ZigLLVM_sparc: - case ZigLLVM_sparcel: - case ZigLLVM_sparcv9: - case ZigLLVM_spir: - case ZigLLVM_spir64: - case ZigLLVM_systemz: - case ZigLLVM_tce: - case ZigLLVM_tcele: - case ZigLLVM_xcore: - case ZigLLVM_ve: - zig_panic("TODO populate this table with stack pointer register name for this CPU architecture"); - } - zig_unreachable(); -} - -bool target_is_arm(const ZigTarget *target) { - switch (target->arch) { - case ZigLLVM_UnknownArch: - zig_unreachable(); - case ZigLLVM_aarch64: - case ZigLLVM_aarch64_be: - case ZigLLVM_aarch64_32: - case ZigLLVM_arm: - case ZigLLVM_armeb: - case ZigLLVM_thumb: - case ZigLLVM_thumbeb: - return true; - - case ZigLLVM_x86: - case ZigLLVM_x86_64: - case ZigLLVM_amdgcn: - case ZigLLVM_amdil: - case ZigLLVM_amdil64: - case ZigLLVM_arc: - case ZigLLVM_avr: - case ZigLLVM_bpfeb: - case ZigLLVM_bpfel: - case ZigLLVM_hexagon: - case ZigLLVM_lanai: - case ZigLLVM_hsail: - case ZigLLVM_hsail64: - case ZigLLVM_kalimba: - case ZigLLVM_le32: - case ZigLLVM_le64: - case ZigLLVM_mips: - case ZigLLVM_mips64: - case ZigLLVM_mips64el: - case ZigLLVM_mipsel: - case ZigLLVM_msp430: - case ZigLLVM_nvptx: - case ZigLLVM_nvptx64: - case ZigLLVM_ppc64le: - case ZigLLVM_r600: - case ZigLLVM_renderscript32: - case ZigLLVM_renderscript64: - case ZigLLVM_riscv32: - case ZigLLVM_riscv64: - case ZigLLVM_shave: - case ZigLLVM_sparc: - case ZigLLVM_sparcel: - case ZigLLVM_sparcv9: - case ZigLLVM_spir: - case ZigLLVM_spir64: - case ZigLLVM_systemz: - case ZigLLVM_tce: - case ZigLLVM_tcele: - case ZigLLVM_wasm32: - case ZigLLVM_wasm64: - case ZigLLVM_xcore: - case ZigLLVM_ppc: - case ZigLLVM_ppc64: - case ZigLLVM_ve: - return false; - } - zig_unreachable(); -} - -// Valgrind supports more, but Zig does not support them yet. -bool target_has_valgrind_support(const ZigTarget *target) { - switch (target->arch) { - case ZigLLVM_UnknownArch: - zig_unreachable(); - case ZigLLVM_x86_64: - return (target->os == OsLinux || target_os_is_darwin(target->os) || target->os == OsSolaris || - (target->os == OsWindows && target->abi != ZigLLVM_MSVC)); - default: - return false; - } - zig_unreachable(); -} - -bool target_os_requires_libc(Os os) { - // On Darwin, we always link libSystem which contains libc. - // Similarly on FreeBSD and NetBSD we always link system libc - // since this is the stable syscall interface. - return (target_os_is_darwin(os) || os == OsFreeBSD || os == OsNetBSD || os == OsDragonFly); -} - -bool target_supports_fpic(const ZigTarget *target) { - // This is not whether the target supports Position Independent Code, but whether the -fPIC - // C compiler argument is valid. - return target->os != OsWindows; -} - -bool target_supports_clang_march_native(const ZigTarget *target) { - // Whether clang supports -march=native on this target. - // Arguably it should always work, but in reality it gives: - // error: the clang compiler does not support '-march=native' - // If we move CPU detection logic into Zig itelf, we will not need this, - // instead we will always pass target features and CPU configuration explicitly. - return target->arch != ZigLLVM_aarch64 && - target->arch != ZigLLVM_aarch64_be; -} - -bool target_supports_stack_probing(const ZigTarget *target) { - return target->os != OsWindows && target->os != OsUefi && (target->arch == ZigLLVM_x86 || target->arch == ZigLLVM_x86_64); -} - -bool target_supports_sanitize_c(const ZigTarget *target) { - return true; -} - -bool target_requires_pic(const ZigTarget *target, bool linking_libc) { - // This function returns whether non-pic code is completely invalid on the given target. - return target_is_android(target) || target->os == OsWindows || target->os == OsUefi || target_os_requires_libc(target->os) || - (linking_libc && target_is_glibc(target)); -} - -bool target_requires_pie(const ZigTarget *target) { - return target_is_android(target); -} - -bool target_is_glibc(const ZigTarget *target) { - return target->os == OsLinux && target_abi_is_gnu(target->abi); -} - -bool target_is_musl(const ZigTarget *target) { - return target->os == OsLinux && target_abi_is_musl(target->abi); -} - -bool target_is_wasm(const ZigTarget *target) { - return target->arch == ZigLLVM_wasm32 || target->arch == ZigLLVM_wasm64; -} - -bool target_is_single_threaded(const ZigTarget *target) { - return target_is_wasm(target); -} - -ZigLLVM_EnvironmentType target_default_abi(ZigLLVM_ArchType arch, Os os) { - if (arch == ZigLLVM_wasm32 || arch == ZigLLVM_wasm64) { - return ZigLLVM_Musl; - } - switch (os) { - case OsFreestanding: - case OsAnanas: - case OsCloudABI: - case OsLv2: - case OsSolaris: - case OsHaiku: - case OsMinix: - case OsRTEMS: - case OsNaCl: - case OsCNK: - case OsAIX: - case OsCUDA: - case OsNVCL: - case OsAMDHSA: - case OsPS4: - case OsELFIAMCU: - case OsMesa3D: - case OsContiki: - case OsAMDPAL: - case OsHermitCore: - case OsOther: - return ZigLLVM_EABI; - case OsOpenBSD: - case OsMacOSX: - case OsFreeBSD: - case OsIOS: - case OsTvOS: - case OsWatchOS: - case OsFuchsia: - case OsKFreeBSD: - case OsNetBSD: - case OsDragonFly: - case OsHurd: - return ZigLLVM_GNU; - case OsUefi: - case OsWindows: - return ZigLLVM_MSVC; - case OsLinux: - case OsWASI: - case OsEmscripten: - return ZigLLVM_Musl; - } - zig_unreachable(); -} - -bool target_abi_is_gnu(ZigLLVM_EnvironmentType abi) { - switch (abi) { - case ZigLLVM_GNU: - case ZigLLVM_GNUABIN32: - case ZigLLVM_GNUABI64: - case ZigLLVM_GNUEABI: - case ZigLLVM_GNUEABIHF: - case ZigLLVM_GNUX32: - return true; - default: - return false; - } -} - -bool target_abi_is_musl(ZigLLVM_EnvironmentType abi) { - switch (abi) { - case ZigLLVM_Musl: - case ZigLLVM_MuslEABI: - case ZigLLVM_MuslEABIHF: - return true; - default: - return false; - } -} - -struct AvailableLibC { - ZigLLVM_ArchType arch; - Os os; - ZigLLVM_EnvironmentType abi; -}; - -static const AvailableLibC libcs_available[] = { - {ZigLLVM_aarch64_be, OsLinux, ZigLLVM_GNU}, - {ZigLLVM_aarch64_be, OsLinux, ZigLLVM_Musl}, - {ZigLLVM_aarch64_be, OsWindows, ZigLLVM_GNU}, - {ZigLLVM_aarch64, OsLinux, ZigLLVM_GNU}, - {ZigLLVM_aarch64, OsLinux, ZigLLVM_Musl}, - {ZigLLVM_aarch64, OsWindows, ZigLLVM_GNU}, - {ZigLLVM_armeb, OsLinux, ZigLLVM_GNUEABI}, - {ZigLLVM_armeb, OsLinux, ZigLLVM_GNUEABIHF}, - {ZigLLVM_armeb, OsLinux, ZigLLVM_MuslEABI}, - {ZigLLVM_armeb, OsLinux, ZigLLVM_MuslEABIHF}, - {ZigLLVM_armeb, OsWindows, ZigLLVM_GNU}, - {ZigLLVM_arm, OsLinux, ZigLLVM_GNUEABI}, - {ZigLLVM_arm, OsLinux, ZigLLVM_GNUEABIHF}, - {ZigLLVM_arm, OsLinux, ZigLLVM_MuslEABI}, - {ZigLLVM_arm, OsLinux, ZigLLVM_MuslEABIHF}, - {ZigLLVM_arm, OsWindows, ZigLLVM_GNU}, - {ZigLLVM_x86, OsLinux, ZigLLVM_GNU}, - {ZigLLVM_x86, OsLinux, ZigLLVM_Musl}, - {ZigLLVM_x86, OsWindows, ZigLLVM_GNU}, - {ZigLLVM_mips64el, OsLinux, ZigLLVM_GNUABI64}, - {ZigLLVM_mips64el, OsLinux, ZigLLVM_GNUABIN32}, - {ZigLLVM_mips64el, OsLinux, ZigLLVM_Musl}, - {ZigLLVM_mips64, OsLinux, ZigLLVM_GNUABI64}, - {ZigLLVM_mips64, OsLinux, ZigLLVM_GNUABIN32}, - {ZigLLVM_mips64, OsLinux, ZigLLVM_Musl}, - {ZigLLVM_mipsel, OsLinux, ZigLLVM_GNU}, - {ZigLLVM_mipsel, OsLinux, ZigLLVM_Musl}, - {ZigLLVM_mips, OsLinux, ZigLLVM_GNU}, - {ZigLLVM_mips, OsLinux, ZigLLVM_Musl}, - {ZigLLVM_ppc64le, OsLinux, ZigLLVM_GNU}, - {ZigLLVM_ppc64le, OsLinux, ZigLLVM_Musl}, - {ZigLLVM_ppc64, OsLinux, ZigLLVM_GNU}, - {ZigLLVM_ppc64, OsLinux, ZigLLVM_Musl}, - {ZigLLVM_ppc, OsLinux, ZigLLVM_GNU}, - {ZigLLVM_ppc, OsLinux, ZigLLVM_Musl}, - {ZigLLVM_riscv64, OsLinux, ZigLLVM_GNU}, - {ZigLLVM_riscv64, OsLinux, ZigLLVM_Musl}, - {ZigLLVM_systemz, OsLinux, ZigLLVM_GNU}, - {ZigLLVM_systemz, OsLinux, ZigLLVM_Musl}, - {ZigLLVM_sparc, OsLinux, ZigLLVM_GNU}, - {ZigLLVM_sparcv9, OsLinux, ZigLLVM_GNU}, - {ZigLLVM_wasm32, OsFreestanding, ZigLLVM_Musl}, - {ZigLLVM_x86_64, OsLinux, ZigLLVM_GNU}, - {ZigLLVM_x86_64, OsLinux, ZigLLVM_GNUX32}, - {ZigLLVM_x86_64, OsLinux, ZigLLVM_Musl}, - {ZigLLVM_x86_64, OsWindows, ZigLLVM_GNU}, -}; - -bool target_can_build_libc(const ZigTarget *target) { - for (size_t i = 0; i < array_length(libcs_available); i += 1) { - if (target->arch == libcs_available[i].arch && - target->os == libcs_available[i].os && - target->abi == libcs_available[i].abi) - { - return true; - } - } - return false; -} - -const char *target_libc_generic_name(const ZigTarget *target) { - if (target->os == OsWindows) { - return "mingw"; - } - switch (target->abi) { - case ZigLLVM_GNU: - case ZigLLVM_GNUABIN32: - case ZigLLVM_GNUABI64: - case ZigLLVM_GNUEABI: - case ZigLLVM_GNUEABIHF: - case ZigLLVM_GNUX32: - return "glibc"; - case ZigLLVM_Musl: - case ZigLLVM_MuslEABI: - case ZigLLVM_MuslEABIHF: - case ZigLLVM_UnknownEnvironment: - return "musl"; - case ZigLLVM_CODE16: - case ZigLLVM_EABI: - case ZigLLVM_EABIHF: - case ZigLLVM_Android: - case ZigLLVM_MSVC: - case ZigLLVM_Itanium: - case ZigLLVM_Cygnus: - case ZigLLVM_CoreCLR: - case ZigLLVM_Simulator: - case ZigLLVM_MacABI: - zig_unreachable(); - } - zig_unreachable(); -} - -bool target_is_libc_lib_name(const ZigTarget *target, const char *name) { - auto equal = str_eql_str; - if (target->os == OsMacOSX) - equal = str_eql_str_ignore_case; - - if (equal(name, "c")) - return true; - - if (target_abi_is_gnu(target->abi) && target->os == OsWindows) { - // mingw-w64 - - if (equal(name, "m")) - return true; - - return false; - } - - if (target_abi_is_gnu(target->abi) || target_abi_is_musl(target->abi) || target_os_is_darwin(target->os)) { - if (equal(name, "m")) - return true; - if (equal(name, "rt")) - return true; - if (equal(name, "pthread")) - return true; - if (equal(name, "crypt")) - return true; - if (equal(name, "util")) - return true; - if (equal(name, "xnet")) - return true; - if (equal(name, "resolv")) - return true; - if (equal(name, "dl")) - return true; - if (equal(name, "util")) - return true; - } - - if (target_os_is_darwin(target->os) && equal(name, "System")) - return true; - - return false; -} - -bool target_is_libcpp_lib_name(const ZigTarget *target, const char *name) { - if (strcmp(name, "c++") == 0 || strcmp(name, "c++abi") == 0) - return true; - - return false; -} - -size_t target_libc_count(void) { - return array_length(libcs_available); -} - -void target_libc_enum(size_t index, ZigTarget *out_target) { - assert(index < array_length(libcs_available)); - out_target->arch = libcs_available[index].arch; - out_target->os = libcs_available[index].os; - out_target->abi = libcs_available[index].abi; - out_target->vendor = ZigLLVM_UnknownVendor; - out_target->is_native_os = false; - out_target->is_native_cpu = false; -} - -bool target_has_debug_info(const ZigTarget *target) { - return !target_is_wasm(target); -} - -const char *target_arch_musl_name(ZigLLVM_ArchType arch) { - switch (arch) { - case ZigLLVM_aarch64: - case ZigLLVM_aarch64_be: - return "aarch64"; - case ZigLLVM_arm: - case ZigLLVM_armeb: - return "arm"; - case ZigLLVM_mips: - case ZigLLVM_mipsel: - return "mips"; - case ZigLLVM_mips64el: - case ZigLLVM_mips64: - return "mips64"; - case ZigLLVM_ppc: - return "powerpc"; - case ZigLLVM_ppc64: - case ZigLLVM_ppc64le: - return "powerpc64"; - case ZigLLVM_systemz: - return "s390x"; - case ZigLLVM_x86: - return "i386"; - case ZigLLVM_x86_64: - return "x86_64"; - case ZigLLVM_riscv64: - return "riscv64"; - default: - zig_unreachable(); - } -} - -bool target_libc_needs_crti_crtn(const ZigTarget *target) { - if (target->arch == ZigLLVM_riscv32 || target->arch == ZigLLVM_riscv64 || target_is_android(target)) { - return false; - } - return true; -} - -bool target_is_riscv(const ZigTarget *target) { - return target->arch == ZigLLVM_riscv32 || target->arch == ZigLLVM_riscv64; -} - -bool target_is_mips(const ZigTarget *target) { - return target->arch == ZigLLVM_mips || target->arch == ZigLLVM_mipsel || - target->arch == ZigLLVM_mips64 || target->arch == ZigLLVM_mips64el; -} - -bool target_is_ppc(const ZigTarget *target) { - return target->arch == ZigLLVM_ppc || target->arch == ZigLLVM_ppc64 || - target->arch == ZigLLVM_ppc64le; -} - -unsigned target_fn_align(const ZigTarget *target) { - return 16; -} diff --git a/src/target.hpp b/src/target.hpp deleted file mode 100644 index 5e44301ffff492a3dd0aa057f6765af18f49d409..0000000000000000000000000000000000000000 --- a/src/target.hpp +++ /dev/null @@ -1,133 +0,0 @@ -/* - * Copyright (c) 2016 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#ifndef ZIG_TARGET_HPP -#define ZIG_TARGET_HPP - -#include "stage2.h" - -struct Buf; - -enum TargetSubsystem { - TargetSubsystemConsole, - TargetSubsystemWindows, - TargetSubsystemPosix, - TargetSubsystemNative, - TargetSubsystemEfiApplication, - TargetSubsystemEfiBootServiceDriver, - TargetSubsystemEfiRom, - TargetSubsystemEfiRuntimeDriver, - - // This means Zig should infer the subsystem. - // It's last so that the indexes of other items can line up - // with the enum in builtin.zig. - TargetSubsystemAuto -}; - -enum CIntType { - CIntTypeShort, - CIntTypeUShort, - CIntTypeInt, - CIntTypeUInt, - CIntTypeLong, - CIntTypeULong, - CIntTypeLongLong, - CIntTypeULongLong, - - CIntTypeCount, -}; - -Error target_parse_triple(ZigTarget *target, const char *triple, const char *mcpu, const char *dynamic_linker); -Error target_parse_arch(ZigLLVM_ArchType *arch, const char *arch_ptr, size_t arch_len); -Error target_parse_os(Os *os, const char *os_ptr, size_t os_len); -Error target_parse_abi(ZigLLVM_EnvironmentType *abi, const char *abi_ptr, size_t abi_len); - -Error target_parse_glibc_version(Stage2SemVer *out, const char *text); -void target_init_default_glibc_version(ZigTarget *target); - -size_t target_arch_count(void); -ZigLLVM_ArchType target_arch_enum(size_t index); -const char *target_arch_name(ZigLLVM_ArchType arch); - -const char *arch_stack_pointer_register_name(ZigLLVM_ArchType arch); - -size_t target_vendor_count(void); -ZigLLVM_VendorType target_vendor_enum(size_t index); - -size_t target_os_count(void); -Os target_os_enum(size_t index); -const char *target_os_name(Os os_type); - -size_t target_abi_count(void); -ZigLLVM_EnvironmentType target_abi_enum(size_t index); -const char *target_abi_name(ZigLLVM_EnvironmentType abi); -ZigLLVM_EnvironmentType target_default_abi(ZigLLVM_ArchType arch, Os os); - - -size_t target_oformat_count(void); -ZigLLVM_ObjectFormatType target_oformat_enum(size_t index); -const char *target_oformat_name(ZigLLVM_ObjectFormatType oformat); -ZigLLVM_ObjectFormatType target_object_format(const ZigTarget *target); - -void target_triple_llvm(Buf *triple, const ZigTarget *target); -void target_triple_zig(Buf *triple, const ZigTarget *target); - -void init_all_targets(void); - -void resolve_target_object_format(ZigTarget *target); - -uint32_t target_c_type_size_in_bits(const ZigTarget *target, CIntType id); - -const char *target_o_file_ext(const ZigTarget *target); -const char *target_asm_file_ext(const ZigTarget *target); -const char *target_llvm_ir_file_ext(const ZigTarget *target); -const char *target_exe_file_ext(const ZigTarget *target); -const char *target_lib_file_prefix(const ZigTarget *target); -const char *target_lib_file_ext(const ZigTarget *target, bool is_static, bool is_versioned, - size_t version_major, size_t version_minor, size_t version_patch); - -bool target_can_exec(const ZigTarget *host_target, const ZigTarget *guest_target); -ZigLLVM_OSType get_llvm_os_type(Os os_type); - -bool target_is_arm(const ZigTarget *target); -bool target_is_mips(const ZigTarget *target); -bool target_is_ppc(const ZigTarget *target); -bool target_allows_addr_zero(const ZigTarget *target); -bool target_has_valgrind_support(const ZigTarget *target); -bool target_os_is_darwin(Os os); -bool target_os_requires_libc(Os os); -bool target_can_build_libc(const ZigTarget *target); -const char *target_libc_generic_name(const ZigTarget *target); -bool target_is_libc_lib_name(const ZigTarget *target, const char *name); -bool target_is_libcpp_lib_name(const ZigTarget *target, const char *name); -bool target_supports_fpic(const ZigTarget *target); -bool target_supports_clang_march_native(const ZigTarget *target); -bool target_requires_pic(const ZigTarget *target, bool linking_libc); -bool target_requires_pie(const ZigTarget *target); -bool target_abi_is_gnu(ZigLLVM_EnvironmentType abi); -bool target_abi_is_musl(ZigLLVM_EnvironmentType abi); -bool target_is_glibc(const ZigTarget *target); -bool target_is_musl(const ZigTarget *target); -bool target_is_wasm(const ZigTarget *target); -bool target_is_riscv(const ZigTarget *target); -bool target_is_android(const ZigTarget *target); -bool target_is_single_threaded(const ZigTarget *target); -bool target_supports_stack_probing(const ZigTarget *target); -bool target_supports_sanitize_c(const ZigTarget *target); -bool target_has_debug_info(const ZigTarget *target); -const char *target_arch_musl_name(ZigLLVM_ArchType arch); - -uint32_t target_arch_pointer_bit_width(ZigLLVM_ArchType arch); -uint32_t target_arch_largest_atomic_bits(ZigLLVM_ArchType arch); - -size_t target_libc_count(void); -void target_libc_enum(size_t index, ZigTarget *out_target); -bool target_libc_needs_crti_crtn(const ZigTarget *target); - -unsigned target_fn_align(const ZigTarget *target); - -#endif diff --git a/src/target.zig b/src/target.zig new file mode 100644 index 0000000000000000000000000000000000000000..fc0c7a0745a05d26386e78bb5fb4b2b708d1e457 --- /dev/null +++ b/src/target.zig @@ -0,0 +1,342 @@ +const std = @import("std"); +const llvm = @import("llvm.zig"); + +pub const ArchOsAbi = struct { + arch: std.Target.Cpu.Arch, + os: std.Target.Os.Tag, + abi: std.Target.Abi, +}; + +pub const available_libcs = [_]ArchOsAbi{ + .{ .arch = .aarch64_be, .os = .linux, .abi = .gnu }, + .{ .arch = .aarch64_be, .os = .linux, .abi = .musl }, + .{ .arch = .aarch64_be, .os = .windows, .abi = .gnu }, + .{ .arch = .aarch64, .os = .linux, .abi = .gnu }, + .{ .arch = .aarch64, .os = .linux, .abi = .musl }, + .{ .arch = .aarch64, .os = .windows, .abi = .gnu }, + .{ .arch = .armeb, .os = .linux, .abi = .gnueabi }, + .{ .arch = .armeb, .os = .linux, .abi = .gnueabihf }, + .{ .arch = .armeb, .os = .linux, .abi = .musleabi }, + .{ .arch = .armeb, .os = .linux, .abi = .musleabihf }, + .{ .arch = .armeb, .os = .windows, .abi = .gnu }, + .{ .arch = .arm, .os = .linux, .abi = .gnueabi }, + .{ .arch = .arm, .os = .linux, .abi = .gnueabihf }, + .{ .arch = .arm, .os = .linux, .abi = .musleabi }, + .{ .arch = .arm, .os = .linux, .abi = .musleabihf }, + .{ .arch = .arm, .os = .windows, .abi = .gnu }, + .{ .arch = .i386, .os = .linux, .abi = .gnu }, + .{ .arch = .i386, .os = .linux, .abi = .musl }, + .{ .arch = .i386, .os = .windows, .abi = .gnu }, + .{ .arch = .mips64el, .os = .linux, .abi = .gnuabi64 }, + .{ .arch = .mips64el, .os = .linux, .abi = .gnuabin32 }, + .{ .arch = .mips64el, .os = .linux, .abi = .musl }, + .{ .arch = .mips64, .os = .linux, .abi = .gnuabi64 }, + .{ .arch = .mips64, .os = .linux, .abi = .gnuabin32 }, + .{ .arch = .mips64, .os = .linux, .abi = .musl }, + .{ .arch = .mipsel, .os = .linux, .abi = .gnu }, + .{ .arch = .mipsel, .os = .linux, .abi = .musl }, + .{ .arch = .mips, .os = .linux, .abi = .gnu }, + .{ .arch = .mips, .os = .linux, .abi = .musl }, + .{ .arch = .powerpc64le, .os = .linux, .abi = .gnu }, + .{ .arch = .powerpc64le, .os = .linux, .abi = .musl }, + .{ .arch = .powerpc64, .os = .linux, .abi = .gnu }, + .{ .arch = .powerpc64, .os = .linux, .abi = .musl }, + .{ .arch = .powerpc, .os = .linux, .abi = .gnu }, + .{ .arch = .powerpc, .os = .linux, .abi = .musl }, + .{ .arch = .riscv64, .os = .linux, .abi = .gnu }, + .{ .arch = .riscv64, .os = .linux, .abi = .musl }, + .{ .arch = .s390x, .os = .linux, .abi = .gnu }, + .{ .arch = .s390x, .os = .linux, .abi = .musl }, + .{ .arch = .sparc, .os = .linux, .abi = .gnu }, + .{ .arch = .sparcv9, .os = .linux, .abi = .gnu }, + .{ .arch = .wasm32, .os = .freestanding, .abi = .musl }, + .{ .arch = .x86_64, .os = .linux, .abi = .gnu }, + .{ .arch = .x86_64, .os = .linux, .abi = .gnux32 }, + .{ .arch = .x86_64, .os = .linux, .abi = .musl }, + .{ .arch = .x86_64, .os = .windows, .abi = .gnu }, +}; + +pub fn libCGenericName(target: std.Target) [:0]const u8 { + if (target.os.tag == .windows) + return "mingw"; + switch (target.abi) { + .gnu, + .gnuabin32, + .gnuabi64, + .gnueabi, + .gnueabihf, + .gnux32, + => return "glibc", + .musl, + .musleabi, + .musleabihf, + .none, + => return "musl", + .code16, + .eabi, + .eabihf, + .android, + .msvc, + .itanium, + .cygnus, + .coreclr, + .simulator, + .macabi, + => unreachable, + } +} + +pub fn archMuslName(arch: std.Target.Cpu.Arch) [:0]const u8 { + switch (arch) { + .aarch64, .aarch64_be => return "aarch64", + .arm, .armeb => return "arm", + .mips, .mipsel => return "mips", + .mips64el, .mips64 => return "mips64", + .powerpc => return "powerpc", + .powerpc64, .powerpc64le => return "powerpc64", + .s390x => return "s390x", + .i386 => return "i386", + .x86_64 => return "x86_64", + .riscv64 => return "riscv64", + else => unreachable, + } +} + +pub fn canBuildLibC(target: std.Target) bool { + for (available_libcs) |libc| { + if (target.cpu.arch == libc.arch and target.os.tag == libc.os and target.abi == libc.abi) { + return true; + } + } + return false; +} + +pub fn cannotDynamicLink(target: std.Target) bool { + return switch (target.os.tag) { + .freestanding, .other => true, + else => false, + }; +} + +/// On Darwin, we always link libSystem which contains libc. +/// Similarly on FreeBSD and NetBSD we always link system libc +/// since this is the stable syscall interface. +pub fn osRequiresLibC(target: std.Target) bool { + return switch (target.os.tag) { + .freebsd, .netbsd, .dragonfly, .macosx, .ios, .watchos, .tvos => true, + else => false, + }; +} + +pub fn libcNeedsLibUnwind(target: std.Target) bool { + return switch (target.os.tag) { + .windows, + .macosx, + .ios, + .watchos, + .tvos, + .freestanding, + => false, + + else => true, + }; +} + +pub fn requiresPIE(target: std.Target) bool { + return target.isAndroid() or target.isDarwin(); +} + +/// This function returns whether non-pic code is completely invalid on the given target. +pub fn requiresPIC(target: std.Target, linking_libc: bool) bool { + return target.isAndroid() or + target.os.tag == .windows or target.os.tag == .uefi or + osRequiresLibC(target) or + (linking_libc and target.isGnuLibC()); +} + +/// This is not whether the target supports Position Independent Code, but whether the -fPIC +/// C compiler argument is valid to Clang. +pub fn supports_fpic(target: std.Target) bool { + return target.os.tag != .windows; +} + +pub fn libc_needs_crti_crtn(target: std.Target) bool { + return !(target.cpu.arch.isRISCV() or target.isAndroid()); +} + +pub fn isSingleThreaded(target: std.Target) bool { + return target.isWasm(); +} + +/// Valgrind supports more, but Zig does not support them yet. +pub fn hasValgrindSupport(target: std.Target) bool { + switch (target.cpu.arch) { + .x86_64 => { + return target.os.tag == .linux or target.isDarwin() or target.os.tag == .solaris or + (target.os.tag == .windows and target.abi != .msvc); + }, + else => return false, + } +} + +pub fn supportsStackProbing(target: std.Target) bool { + return target.os.tag != .windows and target.os.tag != .uefi and + (target.cpu.arch == .i386 or target.cpu.arch == .x86_64); +} + +pub fn osToLLVM(os_tag: std.Target.Os.Tag) llvm.OSType { + return switch (os_tag) { + .freestanding, .other => .UnknownOS, + .windows, .uefi => .Win32, + .ananas => .Ananas, + .cloudabi => .CloudABI, + .dragonfly => .DragonFly, + .freebsd => .FreeBSD, + .fuchsia => .Fuchsia, + .ios => .IOS, + .kfreebsd => .KFreeBSD, + .linux => .Linux, + .lv2 => .Lv2, + .macosx => .MacOSX, + .netbsd => .NetBSD, + .openbsd => .OpenBSD, + .solaris => .Solaris, + .haiku => .Haiku, + .minix => .Minix, + .rtems => .RTEMS, + .nacl => .NaCl, + .cnk => .CNK, + .aix => .AIX, + .cuda => .CUDA, + .nvcl => .NVCL, + .amdhsa => .AMDHSA, + .ps4 => .PS4, + .elfiamcu => .ELFIAMCU, + .tvos => .TvOS, + .watchos => .WatchOS, + .mesa3d => .Mesa3D, + .contiki => .Contiki, + .amdpal => .AMDPAL, + .hermit => .HermitCore, + .hurd => .Hurd, + .wasi => .WASI, + .emscripten => .Emscripten, + }; +} + +pub fn archToLLVM(arch_tag: std.Target.Cpu.Arch) llvm.ArchType { + return switch (arch_tag) { + .arm => .arm, + .armeb => .armeb, + .aarch64 => .aarch64, + .aarch64_be => .aarch64_be, + .aarch64_32 => .aarch64_32, + .arc => .arc, + .avr => .avr, + .bpfel => .bpfel, + .bpfeb => .bpfeb, + .hexagon => .hexagon, + .mips => .mips, + .mipsel => .mipsel, + .mips64 => .mips64, + .mips64el => .mips64el, + .msp430 => .msp430, + .powerpc => .ppc, + .powerpc64 => .ppc64, + .powerpc64le => .ppc64le, + .r600 => .r600, + .amdgcn => .amdgcn, + .riscv32 => .riscv32, + .riscv64 => .riscv64, + .sparc => .sparc, + .sparcv9 => .sparcv9, + .sparcel => .sparcel, + .s390x => .systemz, + .tce => .tce, + .tcele => .tcele, + .thumb => .thumb, + .thumbeb => .thumbeb, + .i386 => .x86, + .x86_64 => .x86_64, + .xcore => .xcore, + .nvptx => .nvptx, + .nvptx64 => .nvptx64, + .le32 => .le32, + .le64 => .le64, + .amdil => .amdil, + .amdil64 => .amdil64, + .hsail => .hsail, + .hsail64 => .hsail64, + .spir => .spir, + .spir64 => .spir64, + .kalimba => .kalimba, + .shave => .shave, + .lanai => .lanai, + .wasm32 => .wasm32, + .wasm64 => .wasm64, + .renderscript32 => .renderscript32, + .renderscript64 => .renderscript64, + .ve => .ve, + .spu_2 => .UnknownArch, + }; +} + +fn eqlIgnoreCase(ignore_case: bool, a: []const u8, b: []const u8) bool { + if (ignore_case) { + return std.ascii.eqlIgnoreCase(a, b); + } else { + return std.mem.eql(u8, a, b); + } +} + +pub fn is_libc_lib_name(target: std.Target, name: []const u8) bool { + const ignore_case = target.os.tag.isDarwin() or target.os.tag == .windows; + + if (eqlIgnoreCase(ignore_case, name, "c")) + return true; + + if (target.isMinGW()) { + if (eqlIgnoreCase(ignore_case, name, "m")) + return true; + + return false; + } + + if (target.abi.isGnu() or target.abi.isMusl() or target.os.tag.isDarwin()) { + if (eqlIgnoreCase(ignore_case, name, "m")) + return true; + if (eqlIgnoreCase(ignore_case, name, "rt")) + return true; + if (eqlIgnoreCase(ignore_case, name, "pthread")) + return true; + if (eqlIgnoreCase(ignore_case, name, "crypt")) + return true; + if (eqlIgnoreCase(ignore_case, name, "util")) + return true; + if (eqlIgnoreCase(ignore_case, name, "xnet")) + return true; + if (eqlIgnoreCase(ignore_case, name, "resolv")) + return true; + if (eqlIgnoreCase(ignore_case, name, "dl")) + return true; + if (eqlIgnoreCase(ignore_case, name, "util")) + return true; + } + + if (target.os.tag.isDarwin() and eqlIgnoreCase(ignore_case, name, "System")) + return true; + + return false; +} + +pub fn is_libcpp_lib_name(target: std.Target, name: []const u8) bool { + const ignore_case = target.os.tag.isDarwin() or target.os.tag == .windows; + + return eqlIgnoreCase(ignore_case, name, "c++") or + eqlIgnoreCase(ignore_case, name, "stdc++") or + eqlIgnoreCase(ignore_case, name, "c++abi"); +} + +pub fn hasDebugInfo(target: std.Target) bool { + return !target.cpu.arch.isWasm(); +} diff --git a/src/test.zig b/src/test.zig new file mode 100644 index 0000000000000000000000000000000000000000..8ad11efa9c49677b978ba35ba3f5b77ab952ff82 --- /dev/null +++ b/src/test.zig @@ -0,0 +1,824 @@ +const std = @import("std"); +const link = @import("link.zig"); +const Compilation = @import("Compilation.zig"); +const Allocator = std.mem.Allocator; +const zir = @import("zir.zig"); +const Package = @import("Package.zig"); +const introspect = @import("introspect.zig"); +const build_options = @import("build_options"); +const enable_qemu: bool = build_options.enable_qemu; +const enable_wine: bool = build_options.enable_wine; +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 Compilation is used for each + /// update, so each update's source is treated as a single file being + /// updated by the test harness and incrementally compiled. + pub const Case = struct { + /// 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(); + + var zig_lib_directory = try introspect.findZigLibDir(std.testing.allocator); + defer zig_lib_directory.handle.close(); + defer std.testing.allocator.free(zig_lib_directory.path.?); + + const random_seed = blk: { + var random_seed: u64 = undefined; + try std.crypto.randomBytes(std.mem.asBytes(&random_seed)); + break :blk random_seed; + }; + var default_prng = std.rand.DefaultPrng.init(random_seed); + + for (self.cases.items) |case| { + var prg_node = root_node.start(case.name, case.updates.items.len); + prg_node.activate(); + 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, zig_lib_directory, &default_prng.random); + } + } + + fn runOneCase( + self: *TestContext, + allocator: *Allocator, + root_node: *std.Progress.Node, + case: Case, + zig_lib_directory: Compilation.Directory, + rand: *std.rand.Random, + ) !void { + const target_info = try std.zig.system.NativeTargetInfo.detect(allocator, case.target); + const target = target_info.target; + + 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(); + + var cache_dir = try tmp.dir.makeOpenPath("zig-cache", .{}); + defer cache_dir.close(); + const bogus_path = "bogus"; // TODO this will need to be fixed before we can test LLVM extensions + const zig_cache_directory: Compilation.Directory = .{ + .handle = cache_dir, + .path = try std.fs.path.join(arena, &[_][]const u8{ bogus_path, "zig-cache" }), + }; + + const tmp_src_path = switch (case.extension) { + .Zig => "test_case.zig", + .ZIR => "test_case.zir", + }; + + var root_pkg: Package = .{ + .root_src_directory = .{ .path = bogus_path, .handle = tmp.dir }, + .root_src_path = tmp_src_path, + }; + + const ofmt: ?std.builtin.ObjectFormat = if (case.cbe) .c else null; + const bin_name = try std.zig.binNameAlloc(arena, .{ + .root_name = "test_case", + .target = target, + .output_mode = case.output_mode, + .object_format = ofmt, + }); + + const emit_directory: Compilation.Directory = .{ + .path = bogus_path, + .handle = tmp.dir, + }; + const emit_bin: Compilation.EmitLoc = .{ + .directory = emit_directory, + .basename = bin_name, + }; + const comp = try Compilation.create(allocator, .{ + .local_cache_directory = zig_cache_directory, + .global_cache_directory = zig_cache_directory, + .zig_lib_directory = zig_lib_directory, + .rand = rand, + .root_name = "test_case", + .target = target, + // TODO: support tests for object file building, and library builds + // and linking. This will require a rework to support multi-file + // tests. + .output_mode = case.output_mode, + // TODO: support testing optimizations + .optimize_mode = .Debug, + .emit_bin = emit_bin, + .root_pkg = &root_pkg, + .keep_source_files_loaded = true, + .object_format = ofmt, + .is_native_os = case.target.isNativeOs(), + }); + defer comp.destroy(); + + 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 comp.makeBinFileWritable(); + try comp.update(); + module_node.end(); + + if (update.case != .Error) { + var all_errors = try comp.getAllErrorsAlloc(); + defer all_errors.deinit(allocator); + if (all_errors.list.len != 0) { + std.debug.print("\nErrors occurred updating the compilation:\n================\n", .{}); + for (all_errors.list) |err| { + std.debug.print(":{}:{}: error: {}\n================\n", .{ err.line + 1, err.column + 1, err.msg }); + } + if (case.cbe) { + const C = comp.bin_file.cast(link.File.C).?; + std.debug.print("Generated C: \n===============\n{}\n\n===========\n\n", .{C.main.items}); + } + std.debug.print("Test failed.\n", .{}); + 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, comp.bin_file.options.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 comp.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 comp.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/tokenizer.cpp b/src/tokenizer.cpp deleted file mode 100644 index fa14dd40fa3420406511719b75051fa8d13ece3d..0000000000000000000000000000000000000000 --- a/src/tokenizer.cpp +++ /dev/null @@ -1,1671 +0,0 @@ -/* - * Copyright (c) 2015 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#include "tokenizer.hpp" -#include "util.hpp" - -#include -#include -#include -#include -#include -#include - -#define WHITESPACE \ - ' ': \ - case '\r': \ - case '\n' - -#define DIGIT_NON_ZERO \ - '1': \ - case '2': \ - case '3': \ - case '4': \ - case '5': \ - case '6': \ - case '7': \ - case '8': \ - case '9' -#define DIGIT \ - '0': \ - case DIGIT_NON_ZERO - -#define ALPHA \ - 'a': \ - case 'b': \ - case 'c': \ - case 'd': \ - case 'e': \ - case 'f': \ - case 'g': \ - case 'h': \ - case 'i': \ - case 'j': \ - case 'k': \ - case 'l': \ - case 'm': \ - case 'n': \ - case 'o': \ - case 'p': \ - case 'q': \ - case 'r': \ - case 's': \ - case 't': \ - case 'u': \ - case 'v': \ - case 'w': \ - case 'x': \ - case 'y': \ - case 'z': \ - case 'A': \ - case 'B': \ - case 'C': \ - case 'D': \ - case 'E': \ - case 'F': \ - case 'G': \ - case 'H': \ - case 'I': \ - case 'J': \ - case 'K': \ - case 'L': \ - case 'M': \ - case 'N': \ - case 'O': \ - case 'P': \ - case 'Q': \ - case 'R': \ - case 'S': \ - case 'T': \ - case 'U': \ - case 'V': \ - case 'W': \ - case 'X': \ - case 'Y': \ - case 'Z' - -#define SYMBOL_CHAR \ - ALPHA: \ - case DIGIT: \ - case '_' - -#define SYMBOL_START \ - ALPHA: \ - case '_' - -struct ZigKeyword { - const char *text; - TokenId token_id; -}; - -static const struct ZigKeyword zig_keywords[] = { - {"align", TokenIdKeywordAlign}, - {"allowzero", TokenIdKeywordAllowZero}, - {"and", TokenIdKeywordAnd}, - {"anyframe", TokenIdKeywordAnyFrame}, - {"anytype", TokenIdKeywordAnyType}, - {"asm", TokenIdKeywordAsm}, - {"async", TokenIdKeywordAsync}, - {"await", TokenIdKeywordAwait}, - {"break", TokenIdKeywordBreak}, - {"callconv", TokenIdKeywordCallconv}, - {"catch", TokenIdKeywordCatch}, - {"comptime", TokenIdKeywordCompTime}, - {"const", TokenIdKeywordConst}, - {"continue", TokenIdKeywordContinue}, - {"defer", TokenIdKeywordDefer}, - {"else", TokenIdKeywordElse}, - {"enum", TokenIdKeywordEnum}, - {"errdefer", TokenIdKeywordErrdefer}, - {"error", TokenIdKeywordError}, - {"export", TokenIdKeywordExport}, - {"extern", TokenIdKeywordExtern}, - {"false", TokenIdKeywordFalse}, - {"fn", TokenIdKeywordFn}, - {"for", TokenIdKeywordFor}, - {"if", TokenIdKeywordIf}, - {"inline", TokenIdKeywordInline}, - {"noalias", TokenIdKeywordNoAlias}, - {"noinline", TokenIdKeywordNoInline}, - {"nosuspend", TokenIdKeywordNoSuspend}, - {"null", TokenIdKeywordNull}, - {"or", TokenIdKeywordOr}, - {"orelse", TokenIdKeywordOrElse}, - {"packed", TokenIdKeywordPacked}, - {"pub", TokenIdKeywordPub}, - {"resume", TokenIdKeywordResume}, - {"return", TokenIdKeywordReturn}, - {"linksection", TokenIdKeywordLinkSection}, - {"struct", TokenIdKeywordStruct}, - {"suspend", TokenIdKeywordSuspend}, - {"switch", TokenIdKeywordSwitch}, - {"test", TokenIdKeywordTest}, - {"threadlocal", TokenIdKeywordThreadLocal}, - {"true", TokenIdKeywordTrue}, - {"try", TokenIdKeywordTry}, - {"undefined", TokenIdKeywordUndefined}, - {"union", TokenIdKeywordUnion}, - {"unreachable", TokenIdKeywordUnreachable}, - {"usingnamespace", TokenIdKeywordUsingNamespace}, - {"var", TokenIdKeywordVar}, - {"volatile", TokenIdKeywordVolatile}, - {"while", TokenIdKeywordWhile}, -}; - -bool is_zig_keyword(Buf *buf) { - for (size_t i = 0; i < array_length(zig_keywords); i += 1) { - if (buf_eql_str(buf, zig_keywords[i].text)) { - return true; - } - } - return false; -} - -static bool is_symbol_char(uint8_t c) { - switch (c) { - case SYMBOL_CHAR: - return true; - default: - return false; - } -} - -enum TokenizeState { - TokenizeStateStart, - TokenizeStateSymbol, - TokenizeStateZero, // "0", which might lead to "0x" - TokenizeStateNumber, // "123", "0x123" - TokenizeStateNumberNoUnderscore, // "12_", "0x12_" next char must be digit - TokenizeStateNumberDot, - TokenizeStateFloatFraction, // "123.456", "0x123.456" - TokenizeStateFloatFractionNoUnderscore, // "123.45_", "0x123.45_" - TokenizeStateFloatExponentUnsigned, // "123.456e", "123e", "0x123p" - TokenizeStateFloatExponentNumber, // "123.456e7", "123.456e+7", "123.456e-7" - TokenizeStateFloatExponentNumberNoUnderscore, // "123.456e7_", "123.456e+7_", "123.456e-7_" - TokenizeStateString, - TokenizeStateStringEscape, - TokenizeStateStringEscapeUnicodeStart, - TokenizeStateCharLiteral, - TokenizeStateCharLiteralEnd, - TokenizeStateCharLiteralUnicode, - TokenizeStateSawStar, - TokenizeStateSawStarPercent, - TokenizeStateSawSlash, - TokenizeStateSawSlash2, - TokenizeStateSawSlash3, - TokenizeStateSawSlashBang, - TokenizeStateSawBackslash, - TokenizeStateSawPercent, - TokenizeStateSawPlus, - TokenizeStateSawPlusPercent, - TokenizeStateSawDash, - TokenizeStateSawMinusPercent, - TokenizeStateSawAmpersand, - TokenizeStateSawCaret, - TokenizeStateSawBar, - TokenizeStateSawBarBar, - TokenizeStateDocComment, - TokenizeStateContainerDocComment, - TokenizeStateLineComment, - TokenizeStateLineString, - TokenizeStateLineStringEnd, - TokenizeStateLineStringContinue, - TokenizeStateSawEq, - TokenizeStateSawBang, - TokenizeStateSawLessThan, - TokenizeStateSawLessThanLessThan, - TokenizeStateSawGreaterThan, - TokenizeStateSawGreaterThanGreaterThan, - TokenizeStateSawDot, - TokenizeStateSawDotDot, - TokenizeStateSawAtSign, - TokenizeStateCharCode, - TokenizeStateError, -}; - - -struct Tokenize { - Buf *buf; - size_t pos; - TokenizeState state; - ZigList *tokens; - int line; - int column; - Token *cur_tok; - Tokenization *out; - uint32_t radix; - bool is_trailing_underscore; - size_t char_code_index; - bool unicode; - uint32_t char_code; - size_t remaining_code_units; -}; - -ATTRIBUTE_PRINTF(2, 3) -static void tokenize_error(Tokenize *t, const char *format, ...) { - t->state = TokenizeStateError; - - t->out->err_line = t->line; - t->out->err_column = t->column; - - va_list ap; - va_start(ap, format); - t->out->err = buf_vprintf(format, ap); - va_end(ap); -} - -static void set_token_id(Tokenize *t, Token *token, TokenId id) { - token->id = id; - - if (id == TokenIdIntLiteral) { - bigint_init_unsigned(&token->data.int_lit.bigint, 0); - } else if (id == TokenIdFloatLiteral) { - bigfloat_init_32(&token->data.float_lit.bigfloat, 0.0f); - token->data.float_lit.overflow = false; - } else if (id == TokenIdStringLiteral || id == TokenIdMultilineStringLiteral || id == TokenIdSymbol) { - memset(&token->data.str_lit.str, 0, sizeof(Buf)); - buf_resize(&token->data.str_lit.str, 0); - } -} - -static void begin_token(Tokenize *t, TokenId id) { - assert(!t->cur_tok); - t->tokens->add_one(); - Token *token = &t->tokens->last(); - token->start_line = t->line; - token->start_column = t->column; - token->start_pos = t->pos; - - set_token_id(t, token, id); - - t->cur_tok = token; -} - -static void cancel_token(Tokenize *t) { - t->tokens->pop(); - t->cur_tok = nullptr; -} - -static void end_float_token(Tokenize *t) { - uint8_t *ptr_buf = (uint8_t*)buf_ptr(t->buf) + t->cur_tok->start_pos; - size_t buf_len = t->cur_tok->end_pos - t->cur_tok->start_pos; - if (bigfloat_init_buf(&t->cur_tok->data.float_lit.bigfloat, ptr_buf, buf_len)) { - t->cur_tok->data.float_lit.overflow = true; - } -} - -static void end_token(Tokenize *t) { - assert(t->cur_tok); - t->cur_tok->end_pos = t->pos + 1; - - if (t->cur_tok->id == TokenIdFloatLiteral) { - end_float_token(t); - } else if (t->cur_tok->id == TokenIdSymbol) { - char *token_mem = buf_ptr(t->buf) + t->cur_tok->start_pos; - int token_len = (int)(t->cur_tok->end_pos - t->cur_tok->start_pos); - - for (size_t i = 0; i < array_length(zig_keywords); i += 1) { - if (mem_eql_str(token_mem, token_len, zig_keywords[i].text)) { - t->cur_tok->id = zig_keywords[i].token_id; - break; - } - } - } - - t->cur_tok = nullptr; -} - -static bool is_exponent_signifier(uint8_t c, int radix) { - if (radix == 16) { - return c == 'p' || c == 'P'; - } else { - return c == 'e' || c == 'E'; - } -} - -static uint32_t get_digit_value(uint8_t c) { - if ('0' <= c && c <= '9') { - return c - '0'; - } - if ('A' <= c && c <= 'Z') { - return c - 'A' + 10; - } - if ('a' <= c && c <= 'z') { - return c - 'a' + 10; - } - return UINT32_MAX; -} - -static void handle_string_escape(Tokenize *t, uint8_t c) { - if (t->cur_tok->id == TokenIdCharLiteral) { - t->cur_tok->data.char_lit.c = c; - t->state = TokenizeStateCharLiteralEnd; - } else if (t->cur_tok->id == TokenIdStringLiteral || t->cur_tok->id == TokenIdSymbol) { - buf_append_char(&t->cur_tok->data.str_lit.str, c); - t->state = TokenizeStateString; - } else { - zig_unreachable(); - } -} - -static const char* get_escape_shorthand(uint8_t c) { - switch (c) { - case '\0': - return "\\0"; - case '\a': - return "\\a"; - case '\b': - return "\\b"; - case '\t': - return "\\t"; - case '\n': - return "\\n"; - case '\v': - return "\\v"; - case '\f': - return "\\f"; - case '\r': - return "\\r"; - default: - return nullptr; - } -} - -static void invalid_char_error(Tokenize *t, uint8_t c) { - if (c == '\r') { - tokenize_error(t, "invalid carriage return, only '\\n' line endings are supported"); - return; - } - - const char *sh = get_escape_shorthand(c); - if (sh) { - tokenize_error(t, "invalid character: '%s'", sh); - return; - } - - if (isprint(c)) { - tokenize_error(t, "invalid character: '%c'", c); - return; - } - - tokenize_error(t, "invalid character: '\\x%02x'", c); -} - -void tokenize(Buf *buf, Tokenization *out) { - Tokenize t = {0}; - t.out = out; - t.tokens = out->tokens = heap::c_allocator.create>(); - t.buf = buf; - - out->line_offsets = heap::c_allocator.create>(); - out->line_offsets->append(0); - - // Skip the UTF-8 BOM if present - if (buf_starts_with_mem(buf, "\xEF\xBB\xBF", 3)) { - t.pos += 3; - } - - for (; t.pos < buf_len(t.buf); t.pos += 1) { - uint8_t c = buf_ptr(t.buf)[t.pos]; - switch (t.state) { - case TokenizeStateError: - break; - case TokenizeStateStart: - switch (c) { - case WHITESPACE: - break; - case ALPHA: - case '_': - t.state = TokenizeStateSymbol; - begin_token(&t, TokenIdSymbol); - buf_append_char(&t.cur_tok->data.str_lit.str, c); - break; - case '0': - t.state = TokenizeStateZero; - begin_token(&t, TokenIdIntLiteral); - t.is_trailing_underscore = false; - t.radix = 10; - bigint_init_unsigned(&t.cur_tok->data.int_lit.bigint, 0); - break; - case DIGIT_NON_ZERO: - t.state = TokenizeStateNumber; - begin_token(&t, TokenIdIntLiteral); - t.is_trailing_underscore = false; - t.radix = 10; - bigint_init_unsigned(&t.cur_tok->data.int_lit.bigint, get_digit_value(c)); - break; - case '"': - begin_token(&t, TokenIdStringLiteral); - t.state = TokenizeStateString; - break; - case '\'': - begin_token(&t, TokenIdCharLiteral); - t.state = TokenizeStateCharLiteral; - break; - case '(': - begin_token(&t, TokenIdLParen); - end_token(&t); - break; - case ')': - begin_token(&t, TokenIdRParen); - end_token(&t); - break; - case ',': - begin_token(&t, TokenIdComma); - end_token(&t); - break; - case '?': - begin_token(&t, TokenIdQuestion); - end_token(&t); - break; - case '{': - begin_token(&t, TokenIdLBrace); - end_token(&t); - break; - case '}': - begin_token(&t, TokenIdRBrace); - end_token(&t); - break; - case '[': - begin_token(&t, TokenIdLBracket); - end_token(&t); - break; - case ']': - begin_token(&t, TokenIdRBracket); - end_token(&t); - break; - case ';': - begin_token(&t, TokenIdSemicolon); - end_token(&t); - break; - case ':': - begin_token(&t, TokenIdColon); - end_token(&t); - break; - case '#': - begin_token(&t, TokenIdNumberSign); - end_token(&t); - break; - case '*': - begin_token(&t, TokenIdStar); - t.state = TokenizeStateSawStar; - break; - case '/': - begin_token(&t, TokenIdSlash); - t.state = TokenizeStateSawSlash; - break; - case '\\': - begin_token(&t, TokenIdMultilineStringLiteral); - t.state = TokenizeStateSawBackslash; - break; - case '%': - begin_token(&t, TokenIdPercent); - t.state = TokenizeStateSawPercent; - break; - case '+': - begin_token(&t, TokenIdPlus); - t.state = TokenizeStateSawPlus; - break; - case '~': - begin_token(&t, TokenIdTilde); - end_token(&t); - break; - case '@': - begin_token(&t, TokenIdAtSign); - t.state = TokenizeStateSawAtSign; - break; - case '-': - begin_token(&t, TokenIdDash); - t.state = TokenizeStateSawDash; - break; - case '&': - begin_token(&t, TokenIdAmpersand); - t.state = TokenizeStateSawAmpersand; - break; - case '^': - begin_token(&t, TokenIdBinXor); - t.state = TokenizeStateSawCaret; - break; - case '|': - begin_token(&t, TokenIdBinOr); - t.state = TokenizeStateSawBar; - break; - case '=': - begin_token(&t, TokenIdEq); - t.state = TokenizeStateSawEq; - break; - case '!': - begin_token(&t, TokenIdBang); - t.state = TokenizeStateSawBang; - break; - case '<': - begin_token(&t, TokenIdCmpLessThan); - t.state = TokenizeStateSawLessThan; - break; - case '>': - begin_token(&t, TokenIdCmpGreaterThan); - t.state = TokenizeStateSawGreaterThan; - break; - case '.': - begin_token(&t, TokenIdDot); - t.state = TokenizeStateSawDot; - break; - default: - invalid_char_error(&t, c); - } - break; - case TokenizeStateSawDot: - switch (c) { - case '.': - t.state = TokenizeStateSawDotDot; - set_token_id(&t, t.cur_tok, TokenIdEllipsis2); - break; - case '*': - t.state = TokenizeStateStart; - set_token_id(&t, t.cur_tok, TokenIdDotStar); - end_token(&t); - break; - default: - t.pos -= 1; - end_token(&t); - t.state = TokenizeStateStart; - continue; - } - break; - case TokenizeStateSawDotDot: - switch (c) { - case '.': - t.state = TokenizeStateStart; - set_token_id(&t, t.cur_tok, TokenIdEllipsis3); - end_token(&t); - break; - default: - t.pos -= 1; - end_token(&t); - t.state = TokenizeStateStart; - continue; - } - break; - case TokenizeStateSawGreaterThan: - switch (c) { - case '=': - set_token_id(&t, t.cur_tok, TokenIdCmpGreaterOrEq); - end_token(&t); - t.state = TokenizeStateStart; - break; - case '>': - set_token_id(&t, t.cur_tok, TokenIdBitShiftRight); - t.state = TokenizeStateSawGreaterThanGreaterThan; - break; - default: - t.pos -= 1; - end_token(&t); - t.state = TokenizeStateStart; - continue; - } - break; - case TokenizeStateSawGreaterThanGreaterThan: - switch (c) { - case '=': - set_token_id(&t, t.cur_tok, TokenIdBitShiftRightEq); - end_token(&t); - t.state = TokenizeStateStart; - break; - default: - t.pos -= 1; - end_token(&t); - t.state = TokenizeStateStart; - continue; - } - break; - case TokenizeStateSawLessThan: - switch (c) { - case '=': - set_token_id(&t, t.cur_tok, TokenIdCmpLessOrEq); - end_token(&t); - t.state = TokenizeStateStart; - break; - case '<': - set_token_id(&t, t.cur_tok, TokenIdBitShiftLeft); - t.state = TokenizeStateSawLessThanLessThan; - break; - default: - t.pos -= 1; - end_token(&t); - t.state = TokenizeStateStart; - continue; - } - break; - case TokenizeStateSawLessThanLessThan: - switch (c) { - case '=': - set_token_id(&t, t.cur_tok, TokenIdBitShiftLeftEq); - end_token(&t); - t.state = TokenizeStateStart; - break; - default: - t.pos -= 1; - end_token(&t); - t.state = TokenizeStateStart; - continue; - } - break; - case TokenizeStateSawBang: - switch (c) { - case '=': - set_token_id(&t, t.cur_tok, TokenIdCmpNotEq); - end_token(&t); - t.state = TokenizeStateStart; - break; - default: - t.pos -= 1; - end_token(&t); - t.state = TokenizeStateStart; - continue; - } - break; - case TokenizeStateSawEq: - switch (c) { - case '=': - set_token_id(&t, t.cur_tok, TokenIdCmpEq); - end_token(&t); - t.state = TokenizeStateStart; - break; - case '>': - set_token_id(&t, t.cur_tok, TokenIdFatArrow); - end_token(&t); - t.state = TokenizeStateStart; - break; - default: - t.pos -= 1; - end_token(&t); - t.state = TokenizeStateStart; - continue; - } - break; - case TokenizeStateSawStar: - switch (c) { - case '=': - set_token_id(&t, t.cur_tok, TokenIdTimesEq); - end_token(&t); - t.state = TokenizeStateStart; - break; - case '*': - set_token_id(&t, t.cur_tok, TokenIdStarStar); - end_token(&t); - t.state = TokenizeStateStart; - break; - case '%': - set_token_id(&t, t.cur_tok, TokenIdTimesPercent); - t.state = TokenizeStateSawStarPercent; - break; - default: - t.pos -= 1; - end_token(&t); - t.state = TokenizeStateStart; - continue; - } - break; - case TokenizeStateSawStarPercent: - switch (c) { - case '=': - set_token_id(&t, t.cur_tok, TokenIdTimesPercentEq); - end_token(&t); - t.state = TokenizeStateStart; - break; - default: - t.pos -= 1; - end_token(&t); - t.state = TokenizeStateStart; - continue; - } - break; - case TokenizeStateSawPercent: - switch (c) { - case '=': - set_token_id(&t, t.cur_tok, TokenIdModEq); - end_token(&t); - t.state = TokenizeStateStart; - break; - case '.': - set_token_id(&t, t.cur_tok, TokenIdPercentDot); - end_token(&t); - t.state = TokenizeStateStart; - break; - default: - t.pos -= 1; - end_token(&t); - t.state = TokenizeStateStart; - continue; - } - break; - case TokenizeStateSawPlus: - switch (c) { - case '=': - set_token_id(&t, t.cur_tok, TokenIdPlusEq); - end_token(&t); - t.state = TokenizeStateStart; - break; - case '+': - set_token_id(&t, t.cur_tok, TokenIdPlusPlus); - end_token(&t); - t.state = TokenizeStateStart; - break; - case '%': - set_token_id(&t, t.cur_tok, TokenIdPlusPercent); - t.state = TokenizeStateSawPlusPercent; - break; - default: - t.pos -= 1; - end_token(&t); - t.state = TokenizeStateStart; - continue; - } - break; - case TokenizeStateSawPlusPercent: - switch (c) { - case '=': - set_token_id(&t, t.cur_tok, TokenIdPlusPercentEq); - end_token(&t); - t.state = TokenizeStateStart; - break; - default: - t.pos -= 1; - end_token(&t); - t.state = TokenizeStateStart; - continue; - } - break; - case TokenizeStateSawAmpersand: - switch (c) { - case '&': - tokenize_error(&t, "`&&` is invalid. Note that `and` is boolean AND"); - break; - case '=': - set_token_id(&t, t.cur_tok, TokenIdBitAndEq); - end_token(&t); - t.state = TokenizeStateStart; - break; - default: - t.pos -= 1; - end_token(&t); - t.state = TokenizeStateStart; - continue; - } - break; - case TokenizeStateSawCaret: - switch (c) { - case '=': - set_token_id(&t, t.cur_tok, TokenIdBitXorEq); - end_token(&t); - t.state = TokenizeStateStart; - break; - default: - t.pos -= 1; - end_token(&t); - t.state = TokenizeStateStart; - continue; - } - break; - case TokenizeStateSawBar: - switch (c) { - case '=': - set_token_id(&t, t.cur_tok, TokenIdBitOrEq); - end_token(&t); - t.state = TokenizeStateStart; - break; - case '|': - set_token_id(&t, t.cur_tok, TokenIdBarBar); - t.state = TokenizeStateSawBarBar; - break; - default: - t.pos -= 1; - end_token(&t); - t.state = TokenizeStateStart; - continue; - } - break; - case TokenizeStateSawBarBar: - switch (c) { - case '=': - set_token_id(&t, t.cur_tok, TokenIdBarBarEq); - end_token(&t); - t.state = TokenizeStateStart; - break; - default: - t.pos -= 1; - end_token(&t); - t.state = TokenizeStateStart; - continue; - } - break; - case TokenizeStateSawSlash: - switch (c) { - case '/': - t.state = TokenizeStateSawSlash2; - break; - case '=': - set_token_id(&t, t.cur_tok, TokenIdDivEq); - end_token(&t); - t.state = TokenizeStateStart; - break; - default: - t.pos -= 1; - end_token(&t); - t.state = TokenizeStateStart; - continue; - } - break; - case TokenizeStateSawSlash2: - switch (c) { - case '/': - t.state = TokenizeStateSawSlash3; - break; - case '!': - t.state = TokenizeStateSawSlashBang; - break; - case '\n': - cancel_token(&t); - t.state = TokenizeStateStart; - break; - default: - cancel_token(&t); - t.state = TokenizeStateLineComment; - break; - } - break; - case TokenizeStateSawSlash3: - switch (c) { - case '/': - cancel_token(&t); - t.state = TokenizeStateLineComment; - break; - case '\n': - set_token_id(&t, t.cur_tok, TokenIdDocComment); - end_token(&t); - t.state = TokenizeStateStart; - break; - default: - set_token_id(&t, t.cur_tok, TokenIdDocComment); - t.state = TokenizeStateDocComment; - break; - } - break; - case TokenizeStateSawSlashBang: - switch (c) { - case '\n': - set_token_id(&t, t.cur_tok, TokenIdContainerDocComment); - end_token(&t); - t.state = TokenizeStateStart; - break; - default: - set_token_id(&t, t.cur_tok, TokenIdContainerDocComment); - t.state = TokenizeStateContainerDocComment; - break; - } - break; - case TokenizeStateSawBackslash: - switch (c) { - case '\\': - t.state = TokenizeStateLineString; - break; - default: - invalid_char_error(&t, c); - break; - } - break; - case TokenizeStateLineString: - switch (c) { - case '\n': - t.state = TokenizeStateLineStringEnd; - break; - default: - buf_append_char(&t.cur_tok->data.str_lit.str, c); - break; - } - break; - case TokenizeStateLineStringEnd: - switch (c) { - case WHITESPACE: - break; - case '\\': - t.state = TokenizeStateLineStringContinue; - break; - default: - t.pos -= 1; - end_token(&t); - t.state = TokenizeStateStart; - continue; - } - break; - case TokenizeStateLineStringContinue: - switch (c) { - case '\\': - t.state = TokenizeStateLineString; - buf_append_char(&t.cur_tok->data.str_lit.str, '\n'); - break; - default: - invalid_char_error(&t, c); - break; - } - break; - case TokenizeStateLineComment: - switch (c) { - case '\n': - t.state = TokenizeStateStart; - break; - default: - // do nothing - break; - } - break; - case TokenizeStateDocComment: - switch (c) { - case '\n': - end_token(&t); - t.state = TokenizeStateStart; - break; - default: - // do nothing - break; - } - break; - case TokenizeStateContainerDocComment: - switch (c) { - case '\n': - end_token(&t); - t.state = TokenizeStateStart; - break; - default: - // do nothing - break; - } - break; - case TokenizeStateSawAtSign: - switch (c) { - case '"': - set_token_id(&t, t.cur_tok, TokenIdSymbol); - t.state = TokenizeStateString; - break; - default: - t.pos -= 1; - end_token(&t); - t.state = TokenizeStateStart; - continue; - } - break; - case TokenizeStateSymbol: - switch (c) { - case SYMBOL_CHAR: - buf_append_char(&t.cur_tok->data.str_lit.str, c); - break; - default: - t.pos -= 1; - end_token(&t); - t.state = TokenizeStateStart; - continue; - } - break; - case TokenizeStateString: - switch (c) { - case '"': - end_token(&t); - t.state = TokenizeStateStart; - break; - case '\n': - tokenize_error(&t, "newline not allowed in string literal"); - break; - case '\\': - t.state = TokenizeStateStringEscape; - break; - default: - buf_append_char(&t.cur_tok->data.str_lit.str, c); - break; - } - break; - case TokenizeStateStringEscape: - switch (c) { - case 'x': - t.state = TokenizeStateCharCode; - t.radix = 16; - t.char_code = 0; - t.char_code_index = 0; - t.unicode = false; - break; - case 'u': - t.state = TokenizeStateStringEscapeUnicodeStart; - break; - case 'n': - handle_string_escape(&t, '\n'); - break; - case 'r': - handle_string_escape(&t, '\r'); - break; - case '\\': - handle_string_escape(&t, '\\'); - break; - case 't': - handle_string_escape(&t, '\t'); - break; - case '\'': - handle_string_escape(&t, '\''); - break; - case '"': - handle_string_escape(&t, '\"'); - break; - default: - invalid_char_error(&t, c); - } - break; - case TokenizeStateStringEscapeUnicodeStart: - switch (c) { - case '{': - t.state = TokenizeStateCharCode; - t.radix = 16; - t.char_code = 0; - t.char_code_index = 0; - t.unicode = true; - break; - default: - invalid_char_error(&t, c); - } - break; - case TokenizeStateCharCode: - { - if (t.unicode && c == '}') { - if (t.char_code_index == 0) { - tokenize_error(&t, "empty unicode escape sequence"); - break; - } - if (t.char_code > 0x10ffff) { - tokenize_error(&t, "unicode value out of range: %x", t.char_code); - break; - } - if (t.cur_tok->id == TokenIdCharLiteral) { - t.cur_tok->data.char_lit.c = t.char_code; - t.state = TokenizeStateCharLiteralEnd; - } else if (t.char_code <= 0x7f) { - // 00000000 00000000 00000000 0xxxxxxx - handle_string_escape(&t, (uint8_t)t.char_code); - } else if (t.char_code <= 0x7ff) { - // 00000000 00000000 00000xxx xx000000 - handle_string_escape(&t, (uint8_t)(0xc0 | (t.char_code >> 6))); - // 00000000 00000000 00000000 00xxxxxx - handle_string_escape(&t, (uint8_t)(0x80 | (t.char_code & 0x3f))); - } else if (t.char_code <= 0xffff) { - // 00000000 00000000 xxxx0000 00000000 - handle_string_escape(&t, (uint8_t)(0xe0 | (t.char_code >> 12))); - // 00000000 00000000 0000xxxx xx000000 - handle_string_escape(&t, (uint8_t)(0x80 | ((t.char_code >> 6) & 0x3f))); - // 00000000 00000000 00000000 00xxxxxx - handle_string_escape(&t, (uint8_t)(0x80 | (t.char_code & 0x3f))); - } else if (t.char_code <= 0x10ffff) { - // 00000000 000xxx00 00000000 00000000 - handle_string_escape(&t, (uint8_t)(0xf0 | (t.char_code >> 18))); - // 00000000 000000xx xxxx0000 00000000 - handle_string_escape(&t, (uint8_t)(0x80 | ((t.char_code >> 12) & 0x3f))); - // 00000000 00000000 0000xxxx xx000000 - handle_string_escape(&t, (uint8_t)(0x80 | ((t.char_code >> 6) & 0x3f))); - // 00000000 00000000 00000000 00xxxxxx - handle_string_escape(&t, (uint8_t)(0x80 | (t.char_code & 0x3f))); - } else { - zig_unreachable(); - } - break; - } - - uint32_t digit_value = get_digit_value(c); - if (digit_value >= t.radix) { - tokenize_error(&t, "invalid digit: '%c'", c); - break; - } - t.char_code *= t.radix; - t.char_code += digit_value; - t.char_code_index += 1; - - if (!t.unicode && t.char_code_index >= 2) { - assert(t.char_code <= 255); - handle_string_escape(&t, (uint8_t)t.char_code); - } - } - break; - case TokenizeStateCharLiteral: - if (c == '\'') { - tokenize_error(&t, "expected character"); - } else if (c == '\\') { - t.state = TokenizeStateStringEscape; - } else if ((c >= 0x80 && c <= 0xbf) || c >= 0xf8) { - // 10xxxxxx - // 11111xxx - invalid_char_error(&t, c); - } else if (c >= 0xc0 && c <= 0xdf) { - // 110xxxxx - t.cur_tok->data.char_lit.c = c & 0x1f; - t.remaining_code_units = 1; - t.state = TokenizeStateCharLiteralUnicode; - } else if (c >= 0xe0 && c <= 0xef) { - // 1110xxxx - t.cur_tok->data.char_lit.c = c & 0x0f; - t.remaining_code_units = 2; - t.state = TokenizeStateCharLiteralUnicode; - } else if (c >= 0xf0 && c <= 0xf7) { - // 11110xxx - t.cur_tok->data.char_lit.c = c & 0x07; - t.remaining_code_units = 3; - t.state = TokenizeStateCharLiteralUnicode; - } else { - t.cur_tok->data.char_lit.c = c; - t.state = TokenizeStateCharLiteralEnd; - } - break; - case TokenizeStateCharLiteralEnd: - switch (c) { - case '\'': - end_token(&t); - t.state = TokenizeStateStart; - break; - default: - invalid_char_error(&t, c); - } - break; - case TokenizeStateCharLiteralUnicode: - if (c <= 0x7f || c >= 0xc0) { - invalid_char_error(&t, c); - } - t.cur_tok->data.char_lit.c <<= 6; - t.cur_tok->data.char_lit.c += c & 0x3f; - t.remaining_code_units--; - if (t.remaining_code_units == 0) { - t.state = TokenizeStateCharLiteralEnd; - } - break; - case TokenizeStateZero: - switch (c) { - case 'b': - t.radix = 2; - t.state = TokenizeStateNumberNoUnderscore; - break; - case 'o': - t.radix = 8; - t.state = TokenizeStateNumberNoUnderscore; - break; - case 'x': - t.radix = 16; - t.state = TokenizeStateNumberNoUnderscore; - break; - default: - // reinterpret as normal number - t.pos -= 1; - t.state = TokenizeStateNumber; - continue; - } - break; - case TokenizeStateNumberNoUnderscore: - if (c == '_') { - invalid_char_error(&t, c); - break; - } else if (get_digit_value(c) < t.radix) { - t.is_trailing_underscore = false; - t.state = TokenizeStateNumber; - } - ZIG_FALLTHROUGH; - case TokenizeStateNumber: - { - if (c == '_') { - t.is_trailing_underscore = true; - t.state = TokenizeStateNumberNoUnderscore; - break; - } - if (c == '.') { - if (t.is_trailing_underscore) { - invalid_char_error(&t, c); - break; - } - t.state = TokenizeStateNumberDot; - break; - } - if (is_exponent_signifier(c, t.radix)) { - if (t.is_trailing_underscore) { - invalid_char_error(&t, c); - break; - } - if (t.radix != 16 && t.radix != 10) { - invalid_char_error(&t, c); - } - t.state = TokenizeStateFloatExponentUnsigned; - t.radix = 10; // exponent is always base 10 - assert(t.cur_tok->id == TokenIdIntLiteral); - set_token_id(&t, t.cur_tok, TokenIdFloatLiteral); - break; - } - uint32_t digit_value = get_digit_value(c); - if (digit_value >= t.radix) { - if (t.is_trailing_underscore) { - invalid_char_error(&t, c); - break; - } - - if (is_symbol_char(c)) { - invalid_char_error(&t, c); - } - // not my char - t.pos -= 1; - end_token(&t); - t.state = TokenizeStateStart; - continue; - } - BigInt digit_value_bi; - bigint_init_unsigned(&digit_value_bi, digit_value); - - BigInt radix_bi; - bigint_init_unsigned(&radix_bi, t.radix); - - BigInt multiplied; - bigint_mul(&multiplied, &t.cur_tok->data.int_lit.bigint, &radix_bi); - - bigint_add(&t.cur_tok->data.int_lit.bigint, &multiplied, &digit_value_bi); - break; - } - case TokenizeStateNumberDot: - { - if (c == '.') { - t.pos -= 2; - end_token(&t); - t.state = TokenizeStateStart; - continue; - } - if (t.radix != 16 && t.radix != 10) { - invalid_char_error(&t, c); - } - t.pos -= 1; - t.state = TokenizeStateFloatFractionNoUnderscore; - assert(t.cur_tok->id == TokenIdIntLiteral); - set_token_id(&t, t.cur_tok, TokenIdFloatLiteral); - continue; - } - case TokenizeStateFloatFractionNoUnderscore: - if (c == '_') { - invalid_char_error(&t, c); - } else if (get_digit_value(c) < t.radix) { - t.is_trailing_underscore = false; - t.state = TokenizeStateFloatFraction; - } - ZIG_FALLTHROUGH; - case TokenizeStateFloatFraction: - { - if (c == '_') { - t.is_trailing_underscore = true; - t.state = TokenizeStateFloatFractionNoUnderscore; - break; - } - if (is_exponent_signifier(c, t.radix)) { - if (t.is_trailing_underscore) { - invalid_char_error(&t, c); - break; - } - t.state = TokenizeStateFloatExponentUnsigned; - t.radix = 10; // exponent is always base 10 - break; - } - uint32_t digit_value = get_digit_value(c); - if (digit_value >= t.radix) { - if (t.is_trailing_underscore) { - invalid_char_error(&t, c); - break; - } - if (is_symbol_char(c)) { - invalid_char_error(&t, c); - } - // not my char - t.pos -= 1; - end_token(&t); - t.state = TokenizeStateStart; - continue; - } - - // we use parse_f128 to generate the float literal, so just - // need to get to the end of the token - } - break; - case TokenizeStateFloatExponentUnsigned: - switch (c) { - case '+': - t.state = TokenizeStateFloatExponentNumberNoUnderscore; - break; - case '-': - t.state = TokenizeStateFloatExponentNumberNoUnderscore; - break; - default: - // reinterpret as normal exponent number - t.pos -= 1; - t.state = TokenizeStateFloatExponentNumberNoUnderscore; - continue; - } - break; - case TokenizeStateFloatExponentNumberNoUnderscore: - if (c == '_') { - invalid_char_error(&t, c); - } else if (get_digit_value(c) < t.radix) { - t.is_trailing_underscore = false; - t.state = TokenizeStateFloatExponentNumber; - } - ZIG_FALLTHROUGH; - case TokenizeStateFloatExponentNumber: - { - if (c == '_') { - t.is_trailing_underscore = true; - t.state = TokenizeStateFloatExponentNumberNoUnderscore; - break; - } - uint32_t digit_value = get_digit_value(c); - if (digit_value >= t.radix) { - if (t.is_trailing_underscore) { - invalid_char_error(&t, c); - break; - } - if (is_symbol_char(c)) { - invalid_char_error(&t, c); - } - // not my char - t.pos -= 1; - end_token(&t); - t.state = TokenizeStateStart; - continue; - } - - // we use parse_f128 to generate the float literal, so just - // need to get to the end of the token - } - break; - case TokenizeStateSawDash: - switch (c) { - case '>': - set_token_id(&t, t.cur_tok, TokenIdArrow); - end_token(&t); - t.state = TokenizeStateStart; - break; - case '=': - set_token_id(&t, t.cur_tok, TokenIdMinusEq); - end_token(&t); - t.state = TokenizeStateStart; - break; - case '%': - set_token_id(&t, t.cur_tok, TokenIdMinusPercent); - t.state = TokenizeStateSawMinusPercent; - break; - default: - t.pos -= 1; - end_token(&t); - t.state = TokenizeStateStart; - continue; - } - break; - case TokenizeStateSawMinusPercent: - switch (c) { - case '=': - set_token_id(&t, t.cur_tok, TokenIdMinusPercentEq); - end_token(&t); - t.state = TokenizeStateStart; - break; - default: - t.pos -= 1; - end_token(&t); - t.state = TokenizeStateStart; - continue; - } - break; - } - if (c == '\n') { - out->line_offsets->append(t.pos + 1); - t.line += 1; - t.column = 0; - } else { - t.column += 1; - } - } - // EOF - switch (t.state) { - case TokenizeStateStart: - case TokenizeStateError: - break; - case TokenizeStateNumberNoUnderscore: - case TokenizeStateFloatFractionNoUnderscore: - case TokenizeStateFloatExponentNumberNoUnderscore: - case TokenizeStateNumberDot: - tokenize_error(&t, "unterminated number literal"); - break; - case TokenizeStateString: - tokenize_error(&t, "unterminated string"); - break; - case TokenizeStateStringEscape: - case TokenizeStateStringEscapeUnicodeStart: - case TokenizeStateCharCode: - if (t.cur_tok->id == TokenIdStringLiteral) { - tokenize_error(&t, "unterminated string"); - break; - } else if (t.cur_tok->id == TokenIdCharLiteral) { - tokenize_error(&t, "unterminated character literal"); - break; - } else { - zig_unreachable(); - } - break; - case TokenizeStateCharLiteral: - case TokenizeStateCharLiteralEnd: - case TokenizeStateCharLiteralUnicode: - tokenize_error(&t, "unterminated character literal"); - break; - case TokenizeStateSymbol: - case TokenizeStateZero: - case TokenizeStateNumber: - case TokenizeStateFloatFraction: - case TokenizeStateFloatExponentUnsigned: - case TokenizeStateFloatExponentNumber: - case TokenizeStateSawStar: - case TokenizeStateSawSlash: - case TokenizeStateSawPercent: - case TokenizeStateSawPlus: - case TokenizeStateSawDash: - case TokenizeStateSawAmpersand: - case TokenizeStateSawCaret: - case TokenizeStateSawBar: - case TokenizeStateSawEq: - case TokenizeStateSawBang: - case TokenizeStateSawLessThan: - case TokenizeStateSawLessThanLessThan: - case TokenizeStateSawGreaterThan: - case TokenizeStateSawGreaterThanGreaterThan: - case TokenizeStateSawDot: - case TokenizeStateSawAtSign: - case TokenizeStateSawStarPercent: - case TokenizeStateSawPlusPercent: - case TokenizeStateSawMinusPercent: - case TokenizeStateLineString: - case TokenizeStateLineStringEnd: - case TokenizeStateSawBarBar: - case TokenizeStateDocComment: - case TokenizeStateContainerDocComment: - end_token(&t); - break; - case TokenizeStateSawDotDot: - case TokenizeStateSawBackslash: - case TokenizeStateLineStringContinue: - tokenize_error(&t, "unexpected EOF"); - break; - case TokenizeStateLineComment: - break; - case TokenizeStateSawSlash2: - cancel_token(&t); - break; - case TokenizeStateSawSlash3: - set_token_id(&t, t.cur_tok, TokenIdDocComment); - end_token(&t); - break; - case TokenizeStateSawSlashBang: - set_token_id(&t, t.cur_tok, TokenIdContainerDocComment); - end_token(&t); - break; - } - if (t.state != TokenizeStateError) { - if (t.tokens->length > 0) { - Token *last_token = &t.tokens->last(); - t.line = (int)last_token->start_line; - t.column = (int)last_token->start_column; - t.pos = last_token->start_pos; - } else { - t.pos = 0; - } - begin_token(&t, TokenIdEof); - end_token(&t); - assert(!t.cur_tok); - } -} - -const char * token_name(TokenId id) { - switch (id) { - case TokenIdAmpersand: return "&"; - case TokenIdArrow: return "->"; - case TokenIdAtSign: return "@"; - case TokenIdBang: return "!"; - case TokenIdBarBar: return "||"; - case TokenIdBinOr: return "|"; - case TokenIdBinXor: return "^"; - case TokenIdBitAndEq: return "&="; - case TokenIdBitOrEq: return "|="; - case TokenIdBitShiftLeft: return "<<"; - case TokenIdBitShiftLeftEq: return "<<="; - case TokenIdBitShiftRight: return ">>"; - case TokenIdBitShiftRightEq: return ">>="; - case TokenIdBitXorEq: return "^="; - case TokenIdCharLiteral: return "CharLiteral"; - case TokenIdCmpEq: return "=="; - case TokenIdCmpGreaterOrEq: return ">="; - case TokenIdCmpGreaterThan: return ">"; - case TokenIdCmpLessOrEq: return "<="; - case TokenIdCmpLessThan: return "<"; - case TokenIdCmpNotEq: return "!="; - case TokenIdColon: return ":"; - case TokenIdComma: return ","; - case TokenIdDash: return "-"; - case TokenIdDivEq: return "/="; - case TokenIdDocComment: return "DocComment"; - case TokenIdContainerDocComment: return "ContainerDocComment"; - case TokenIdDot: return "."; - case TokenIdDotStar: return ".*"; - case TokenIdEllipsis2: return ".."; - case TokenIdEllipsis3: return "..."; - case TokenIdEof: return "EOF"; - case TokenIdEq: return "="; - case TokenIdFatArrow: return "=>"; - case TokenIdFloatLiteral: return "FloatLiteral"; - case TokenIdIntLiteral: return "IntLiteral"; - case TokenIdKeywordAsync: return "async"; - case TokenIdKeywordAllowZero: return "allowzero"; - case TokenIdKeywordAwait: return "await"; - case TokenIdKeywordResume: return "resume"; - case TokenIdKeywordSuspend: return "suspend"; - case TokenIdKeywordAlign: return "align"; - case TokenIdKeywordAnd: return "and"; - case TokenIdKeywordAnyFrame: return "anyframe"; - case TokenIdKeywordAnyType: return "anytype"; - case TokenIdKeywordAsm: return "asm"; - case TokenIdKeywordBreak: return "break"; - case TokenIdKeywordCatch: return "catch"; - case TokenIdKeywordCallconv: return "callconv"; - case TokenIdKeywordCompTime: return "comptime"; - case TokenIdKeywordConst: return "const"; - case TokenIdKeywordContinue: return "continue"; - case TokenIdKeywordDefer: return "defer"; - case TokenIdKeywordElse: return "else"; - case TokenIdKeywordEnum: return "enum"; - case TokenIdKeywordErrdefer: return "errdefer"; - case TokenIdKeywordError: return "error"; - case TokenIdKeywordExport: return "export"; - case TokenIdKeywordExtern: return "extern"; - case TokenIdKeywordFalse: return "false"; - case TokenIdKeywordFn: return "fn"; - case TokenIdKeywordFor: return "for"; - case TokenIdKeywordIf: return "if"; - case TokenIdKeywordInline: return "inline"; - case TokenIdKeywordNoAlias: return "noalias"; - case TokenIdKeywordNoInline: return "noinline"; - case TokenIdKeywordNoSuspend: return "nosuspend"; - case TokenIdKeywordNull: return "null"; - case TokenIdKeywordOr: return "or"; - case TokenIdKeywordOrElse: return "orelse"; - case TokenIdKeywordPacked: return "packed"; - case TokenIdKeywordPub: return "pub"; - case TokenIdKeywordReturn: return "return"; - case TokenIdKeywordLinkSection: return "linksection"; - case TokenIdKeywordStruct: return "struct"; - case TokenIdKeywordSwitch: return "switch"; - case TokenIdKeywordTest: return "test"; - case TokenIdKeywordThreadLocal: return "threadlocal"; - case TokenIdKeywordTrue: return "true"; - case TokenIdKeywordTry: return "try"; - case TokenIdKeywordUndefined: return "undefined"; - case TokenIdKeywordUnion: return "union"; - case TokenIdKeywordUnreachable: return "unreachable"; - case TokenIdKeywordUsingNamespace: return "usingnamespace"; - case TokenIdKeywordVar: return "var"; - case TokenIdKeywordVolatile: return "volatile"; - case TokenIdKeywordWhile: return "while"; - case TokenIdLBrace: return "{"; - case TokenIdLBracket: return "["; - case TokenIdLParen: return "("; - case TokenIdQuestion: return "?"; - case TokenIdMinusEq: return "-="; - case TokenIdMinusPercent: return "-%"; - case TokenIdMinusPercentEq: return "-%="; - case TokenIdModEq: return "%="; - case TokenIdNumberSign: return "#"; - case TokenIdPercent: return "%"; - case TokenIdPercentDot: return "%."; - case TokenIdPlus: return "+"; - case TokenIdPlusEq: return "+="; - case TokenIdPlusPercent: return "+%"; - case TokenIdPlusPercentEq: return "+%="; - case TokenIdPlusPlus: return "++"; - case TokenIdRBrace: return "}"; - case TokenIdRBracket: return "]"; - case TokenIdRParen: return ")"; - case TokenIdSemicolon: return ";"; - case TokenIdSlash: return "/"; - case TokenIdStar: return "*"; - case TokenIdStarStar: return "**"; - case TokenIdStringLiteral: return "StringLiteral"; - case TokenIdMultilineStringLiteral: return "MultilineStringLiteral"; - case TokenIdSymbol: return "Symbol"; - case TokenIdTilde: return "~"; - case TokenIdTimesEq: return "*="; - case TokenIdTimesPercent: return "*%"; - case TokenIdTimesPercentEq: return "*%="; - case TokenIdBarBarEq: return "||="; - case TokenIdCount: - zig_unreachable(); - } - return "(invalid token)"; -} - -void print_tokens(Buf *buf, ZigList *tokens) { - for (size_t i = 0; i < tokens->length; i += 1) { - Token *token = &tokens->at(i); - fprintf(stderr, "%s ", token_name(token->id)); - if (token->start_pos != SIZE_MAX) { - fwrite(buf_ptr(buf) + token->start_pos, 1, token->end_pos - token->start_pos, stderr); - } - fprintf(stderr, "\n"); - } -} - -bool valid_symbol_starter(uint8_t c) { - switch (c) { - case SYMBOL_START: - return true; - } - return false; -} diff --git a/src/tokenizer.hpp b/src/tokenizer.hpp deleted file mode 100644 index d8af21ee006eb2070990680e8c8540f6acf42008..0000000000000000000000000000000000000000 --- a/src/tokenizer.hpp +++ /dev/null @@ -1,199 +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_TOKENIZER_HPP -#define ZIG_TOKENIZER_HPP - -#include "buffer.hpp" -#include "bigint.hpp" -#include "bigfloat.hpp" - -enum TokenId { - TokenIdAmpersand, - TokenIdArrow, - TokenIdAtSign, - TokenIdBang, - TokenIdBarBar, - TokenIdBarBarEq, - TokenIdBinOr, - TokenIdBinXor, - TokenIdBitAndEq, - TokenIdBitOrEq, - TokenIdBitShiftLeft, - TokenIdBitShiftLeftEq, - TokenIdBitShiftRight, - TokenIdBitShiftRightEq, - TokenIdBitXorEq, - TokenIdCharLiteral, - TokenIdCmpEq, - TokenIdCmpGreaterOrEq, - TokenIdCmpGreaterThan, - TokenIdCmpLessOrEq, - TokenIdCmpLessThan, - TokenIdCmpNotEq, - TokenIdColon, - TokenIdComma, - TokenIdDash, - TokenIdDivEq, - TokenIdDocComment, - TokenIdContainerDocComment, - TokenIdDot, - TokenIdDotStar, - TokenIdEllipsis2, - TokenIdEllipsis3, - TokenIdEof, - TokenIdEq, - TokenIdFatArrow, - TokenIdFloatLiteral, - TokenIdIntLiteral, - TokenIdKeywordAlign, - TokenIdKeywordAllowZero, - TokenIdKeywordAnd, - TokenIdKeywordAnyFrame, - TokenIdKeywordAnyType, - TokenIdKeywordAsm, - TokenIdKeywordAsync, - TokenIdKeywordAwait, - TokenIdKeywordBreak, - TokenIdKeywordCatch, - TokenIdKeywordCallconv, - TokenIdKeywordCompTime, - TokenIdKeywordConst, - TokenIdKeywordContinue, - TokenIdKeywordDefer, - TokenIdKeywordElse, - TokenIdKeywordEnum, - TokenIdKeywordErrdefer, - TokenIdKeywordError, - TokenIdKeywordExport, - TokenIdKeywordExtern, - TokenIdKeywordFalse, - TokenIdKeywordFn, - TokenIdKeywordFor, - TokenIdKeywordIf, - TokenIdKeywordInline, - TokenIdKeywordNoInline, - TokenIdKeywordLinkSection, - TokenIdKeywordNoAlias, - TokenIdKeywordNoSuspend, - TokenIdKeywordNull, - TokenIdKeywordOr, - TokenIdKeywordOrElse, - TokenIdKeywordPacked, - TokenIdKeywordPub, - TokenIdKeywordResume, - TokenIdKeywordReturn, - TokenIdKeywordStruct, - TokenIdKeywordSuspend, - TokenIdKeywordSwitch, - TokenIdKeywordTest, - TokenIdKeywordThreadLocal, - TokenIdKeywordTrue, - TokenIdKeywordTry, - TokenIdKeywordUndefined, - TokenIdKeywordUnion, - TokenIdKeywordUnreachable, - TokenIdKeywordUsingNamespace, - TokenIdKeywordVar, - TokenIdKeywordVolatile, - TokenIdKeywordWhile, - TokenIdLBrace, - TokenIdLBracket, - TokenIdLParen, - TokenIdQuestion, - TokenIdMinusEq, - TokenIdMinusPercent, - TokenIdMinusPercentEq, - TokenIdModEq, - TokenIdNumberSign, - TokenIdPercent, - TokenIdPercentDot, - TokenIdPlus, - TokenIdPlusEq, - TokenIdPlusPercent, - TokenIdPlusPercentEq, - TokenIdPlusPlus, - TokenIdRBrace, - TokenIdRBracket, - TokenIdRParen, - TokenIdSemicolon, - TokenIdSlash, - TokenIdStar, - TokenIdStarStar, - TokenIdStringLiteral, - TokenIdMultilineStringLiteral, - TokenIdSymbol, - TokenIdTilde, - TokenIdTimesEq, - TokenIdTimesPercent, - TokenIdTimesPercentEq, - TokenIdCount, -}; - -struct TokenFloatLit { - BigFloat bigfloat; - // overflow is true if when parsing the number, we discovered it would not fit - // without losing data - bool overflow; -}; - -struct TokenIntLit { - BigInt bigint; -}; - -struct TokenStrLit { - Buf str; -}; - -struct TokenCharLit { - uint32_t c; -}; - -struct Token { - TokenId id; - size_t start_pos; - size_t end_pos; - size_t start_line; - size_t start_column; - - union { - // TokenIdIntLiteral - TokenIntLit int_lit; - - // TokenIdFloatLiteral - TokenFloatLit float_lit; - - // TokenIdStringLiteral, TokenIdMultilineStringLiteral or TokenIdSymbol - TokenStrLit str_lit; - - // TokenIdCharLiteral - TokenCharLit char_lit; - } data; -}; -// work around conflicting name Token which is also found in libclang -typedef Token ZigToken; - -struct Tokenization { - ZigList *tokens; - ZigList *line_offsets; - - // if an error occurred - Buf *err; - size_t err_line; - size_t err_column; -}; - -void tokenize(Buf *buf, Tokenization *out_tokenization); - -void print_tokens(Buf *buf, ZigList *tokens); - -const char * token_name(TokenId id); - -bool valid_symbol_starter(uint8_t c); -bool is_zig_keyword(Buf *buf); - -#endif diff --git a/src/tracy.zig b/src/tracy.zig new file mode 100644 index 0000000000000000000000000000000000000000..6f56a87ce6fad8484cfc8f37ff5e4e97ca0570f1 --- /dev/null +++ b/src/tracy.zig @@ -0,0 +1,45 @@ +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/translate_c.zig b/src/translate_c.zig new file mode 100644 index 0000000000000000000000000000000000000000..66b5752930017e8bc7393cba0c61110bc5a43bcf --- /dev/null +++ b/src/translate_c.zig @@ -0,0 +1,6470 @@ +//! 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/type.zig b/src/type.zig new file mode 100644 index 0000000000000000000000000000000000000000..49663955124152ab8dc90a20e29fd612edd5841f --- /dev/null +++ b/src/type.zig @@ -0,0 +1,3075 @@ +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/util.cpp b/src/util.cpp deleted file mode 100644 index 2de09df8087be2d8a012328117e1898560f0dc0f..0000000000000000000000000000000000000000 --- a/src/util.cpp +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (c) 2015 Andrew Kelley - * - * This file is part of zig, which is MIT licensed. - * See http://opensource.org/licenses/MIT - */ - -#include "util.hpp" -#include "stage2.h" - -#include -#include - -void zig_panic(const char *format, ...) { - va_list ap; - va_start(ap, format); - vfprintf(stderr, format, ap); - fflush(stderr); - va_end(ap); - stage2_panic("", 0); - abort(); -} - -uint32_t int_hash(int i) { - return (uint32_t)(i % UINT32_MAX); -} -bool int_eq(int a, int b) { - return a == b; -} - -uint32_t uint64_hash(uint64_t i) { - return (uint32_t)(i % UINT32_MAX); -} - -bool uint64_eq(uint64_t a, uint64_t b) { - return a == b; -} - -uint32_t ptr_hash(const void *ptr) { - return (uint32_t)(((uintptr_t)ptr) % UINT32_MAX); -} - -bool ptr_eq(const void *a, const void *b) { - return a == b; -} - -// Ported from std/mem.zig. -bool SplitIterator_isSplitByte(SplitIterator *self, uint8_t byte) { - for (size_t i = 0; i < self->split_bytes.len; i += 1) { - if (byte == self->split_bytes.ptr[i]) { - return true; - } - } - return false; -} - -// Ported from std/mem.zig. -Optional> SplitIterator_next(SplitIterator *self) { - // move to beginning of token - while (self->index < self->buffer.len && - SplitIterator_isSplitByte(self, self->buffer.ptr[self->index])) - { - self->index += 1; - } - size_t start = self->index; - if (start == self->buffer.len) { - return {}; - } - - // move to end of token - while (self->index < self->buffer.len && - !SplitIterator_isSplitByte(self, self->buffer.ptr[self->index])) - { - self->index += 1; - } - size_t end = self->index; - - return Optional>::some(self->buffer.slice(start, end)); -} - -// Ported from std/mem.zig. -// This one won't collapse multiple separators into one, so you could use it, for example, -// to parse Comma Separated Value format. -Optional> SplitIterator_next_separate(SplitIterator *self) { - // move to beginning of token - if (self->index < self->buffer.len && - SplitIterator_isSplitByte(self, self->buffer.ptr[self->index])) - { - self->index += 1; - } - size_t start = self->index; - if (start == self->buffer.len) { - return {}; - } - - // move to end of token - while (self->index < self->buffer.len && - !SplitIterator_isSplitByte(self, self->buffer.ptr[self->index])) - { - self->index += 1; - } - size_t end = self->index; - - return Optional>::some(self->buffer.slice(start, end)); -} - -// Ported from std/mem.zig -Slice SplitIterator_rest(SplitIterator *self) { - // move to beginning of token - size_t index = self->index; - while (index < self->buffer.len && SplitIterator_isSplitByte(self, self->buffer.ptr[index])) { - index += 1; - } - return self->buffer.sliceFrom(index); -} - -// Ported from std/mem.zig -SplitIterator memSplit(Slice buffer, Slice split_bytes) { - return SplitIterator{0, buffer, split_bytes}; -} - -void zig_pretty_print_bytes(FILE *f, double n) { - if (n > 1024.0 * 1024.0 * 1024.0) { - fprintf(f, "%.03f GiB", n / 1024.0 / 1024.0 / 1024.0); - return; - } - if (n > 1024.0 * 1024.0) { - fprintf(f, "%.03f MiB", n / 1024.0 / 1024.0); - return; - } - if (n > 1024.0) { - fprintf(f, "%.03f KiB", n / 1024.0); - return; - } - fprintf(f, "%.03f bytes", n ); - return; -} - diff --git a/src/util.hpp b/src/util.hpp deleted file mode 100644 index 66efe6dfd1736cfdb7d22023ed14b8d7b3a97c00..0000000000000000000000000000000000000000 --- a/src/util.hpp +++ /dev/null @@ -1,253 +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_UTIL_HPP -#define ZIG_UTIL_HPP - -#include -#include -#include -#include - -#if defined(_MSC_VER) -#include -#endif - -#include "config.h" -#include "util_base.hpp" -#include "heap.hpp" -#include "mem.hpp" - -#if defined(_MSC_VER) -static inline int clzll(unsigned long long mask) { - unsigned long lz; -#if defined(_WIN64) - if (_BitScanReverse64(&lz, mask)) - return static_cast(63 - lz); - zig_unreachable(); -#else - if (_BitScanReverse(&lz, mask >> 32)) - lz += 32; - else - _BitScanReverse(&lz, mask & 0xffffffff); - return 63 - lz; -#endif -} -static inline int ctzll(unsigned long long mask) { - unsigned long result; -#if defined(_WIN64) - if (_BitScanForward64(&result, mask)) - return result; - zig_unreachable(); -#else - if (_BitScanForward(&result, mask & 0xffffffff)) - return result; - } - if (_BitScanForward(&result, mask >> 32)) - return 32 + result; - zig_unreachable(); -#endif -} -#else -#define clzll(x) __builtin_clzll(x) -#define ctzll(x) __builtin_ctzll(x) -#endif - -template -constexpr size_t array_length(const T (&)[n]) { - return n; -} - -template -static inline T max(T a, T b) { - return (a >= b) ? a : b; -} - -template -static inline T min(T a, T b) { - return (a <= b) ? a : b; -} - -template -static inline T clamp(T min_value, T value, T max_value) { - return max(min(value, max_value), min_value); -} - -static inline bool mem_eql_mem(const char *a_ptr, size_t a_len, const char *b_ptr, size_t b_len) { - if (a_len != b_len) - return false; - return memcmp(a_ptr, b_ptr, a_len) == 0; -} -static inline bool mem_eql_mem_ignore_case(const char *a_ptr, size_t a_len, const char *b_ptr, size_t b_len) { - if (a_len != b_len) - return false; - for (size_t i = 0; i < a_len; i += 1) { - if (tolower(a_ptr[i]) != tolower(b_ptr[i])) - return false; - } - return true; -} - -static inline bool mem_eql_str(const char *mem, size_t mem_len, const char *str) { - return mem_eql_mem(mem, mem_len, str, strlen(str)); -} - -static inline bool str_eql_str(const char *a, const char* b) { - return mem_eql_mem(a, strlen(a), b, strlen(b)); -} - -static inline bool str_eql_str_ignore_case(const char *a, const char* b) { - return mem_eql_mem_ignore_case(a, strlen(a), b, strlen(b)); -} - -static inline bool is_power_of_2(uint64_t x) { - return x != 0 && ((x & (~x + 1)) == x); -} - -static inline bool mem_ends_with_mem(const char *mem, size_t mem_len, const char *end, size_t end_len) { - if (mem_len < end_len) return false; - return memcmp(mem + mem_len - end_len, end, end_len) == 0; -} - -static inline bool mem_ends_with_str(const char *mem, size_t mem_len, const char *str) { - return mem_ends_with_mem(mem, mem_len, str, strlen(str)); -} - -static inline uint64_t round_to_next_power_of_2(uint64_t x) { - --x; - x |= x >> 1; - x |= x >> 2; - x |= x >> 4; - x |= x >> 8; - x |= x >> 16; - x |= x >> 32; - return x + 1; -} - -uint32_t int_hash(int i); -bool int_eq(int a, int b); -uint32_t uint64_hash(uint64_t i); -bool uint64_eq(uint64_t a, uint64_t b); -uint32_t ptr_hash(const void *ptr); -bool ptr_eq(const void *a, const void *b); - -static inline uint8_t log2_u64(uint64_t x) { - return (63 - clzll(x)); -} - -void zig_pretty_print_bytes(FILE *f, double n); - -template -struct Optional { - T value; - bool is_some; - - static inline Optional some(T x) { - return {x, true}; - } - - static inline Optional none() { - return {{}, false}; - } - - inline bool unwrap(T *res) { - *res = value; - return is_some; - } -}; - -template -struct Slice { - T *ptr; - size_t len; - - inline T &at(size_t i) { - assert(i < len); - return ptr[i]; - } - - inline Slice slice(size_t start, size_t end) { - assert(end <= len); - assert(end >= start); - return { - ptr + start, - end - start, - }; - } - - inline Slice sliceFrom(size_t start) { - assert(start <= len); - return { - ptr + start, - len - start, - }; - } - - static inline Slice alloc(size_t n) { - return {heap::c_allocator.allocate_nonzero(n), n}; - } -}; - -template -struct Array { - static const size_t len = n; - T items[n]; - - inline Slice slice() { - return { - &items[0], - len, - }; - } -}; - -static inline Slice str(const char *literal) { - return {(uint8_t*)(literal), strlen(literal)}; -} - -// Ported from std/mem.zig -template -static inline bool memEql(Slice a, Slice b) { - if (a.len != b.len) - return false; - for (size_t i = 0; i < a.len; i += 1) { - if (a.ptr[i] != b.ptr[i]) - return false; - } - return true; -} - -// Ported from std/mem.zig -template -static inline bool memStartsWith(Slice haystack, Slice needle) { - if (needle.len > haystack.len) - return false; - return memEql(haystack.slice(0, needle.len), needle); -} - -// Ported from std/mem.zig -template -static inline void memCopy(Slice dest, Slice src) { - assert(dest.len >= src.len); - memcpy(dest.ptr, src.ptr, src.len * sizeof(T)); -} - -// Ported from std/mem.zig. -// Coordinate struct fields with memSplit function -struct SplitIterator { - size_t index; - Slice buffer; - Slice split_bytes; -}; - -bool SplitIterator_isSplitByte(SplitIterator *self, uint8_t byte); -Optional< Slice > SplitIterator_next(SplitIterator *self); -Optional< Slice > SplitIterator_next_separate(SplitIterator *self); -Slice SplitIterator_rest(SplitIterator *self); -SplitIterator memSplit(Slice buffer, Slice split_bytes); - -#endif diff --git a/src/util_base.hpp b/src/util_base.hpp deleted file mode 100644 index da1d3bf234deba7255089c19399e03a48d1ce737..0000000000000000000000000000000000000000 --- a/src/util_base.hpp +++ /dev/null @@ -1,77 +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_UTIL_BASE_HPP -#define ZIG_UTIL_BASE_HPP - -#include - -#if defined(_MSC_VER) - -#define ATTRIBUTE_COLD __declspec(noinline) -#define ATTRIBUTE_PRINTF(a, b) -#define ATTRIBUTE_RETURNS_NOALIAS __declspec(restrict) -#define ATTRIBUTE_NORETURN __declspec(noreturn) -#define ATTRIBUTE_MUST_USE - -#define BREAKPOINT __debugbreak() - -#else - -#define ATTRIBUTE_COLD __attribute__((cold)) -#define ATTRIBUTE_PRINTF(a, b) __attribute__((format(printf, a, b))) -#define ATTRIBUTE_RETURNS_NOALIAS __attribute__((__malloc__)) -#define ATTRIBUTE_NORETURN __attribute__((noreturn)) -#define ATTRIBUTE_MUST_USE __attribute__((warn_unused_result)) - -#if defined(__MINGW32__) || defined(__MINGW64__) -#define BREAKPOINT __debugbreak() -#elif defined(__i386__) || defined(__x86_64__) -#define BREAKPOINT __asm__ volatile("int $0x03"); -#elif defined(__clang__) -#define BREAKPOINT __builtin_debugtrap() -#elif defined(__GNUC__) -#define BREAKPOINT __builtin_trap() -#else -#include -#define BREAKPOINT raise(SIGTRAP) -#endif - -#endif - -ATTRIBUTE_COLD -ATTRIBUTE_NORETURN -ATTRIBUTE_PRINTF(1, 2) -void zig_panic(const char *format, ...); - -static inline void zig_assert(bool ok, const char *file, int line, const char *func) { - if (!ok) { - zig_panic("Assertion failed at %s:%d in %s. This is a bug in the Zig compiler.", file, line, func); - } -} - -#ifdef _WIN32 -#define __func__ __FUNCTION__ -#endif - -#define zig_unreachable() zig_panic("Unreachable at %s:%d in %s. This is a bug in the Zig compiler.", __FILE__, __LINE__, __func__) - -// Assertions in stage1 are always on, and they call zig @panic. -#undef assert -#define assert(ok) zig_assert(ok, __FILE__, __LINE__, __func__) - -#if defined(_MSC_VER) -#define ZIG_FALLTHROUGH -#elif defined(__clang__) -#define ZIG_FALLTHROUGH [[clang::fallthrough]] -#elif defined(__GNUC__) && __GNUC__ >= 7 -#define ZIG_FALLTHROUGH __attribute__((fallthrough)) -#else -#define ZIG_FALLTHROUGH -#endif - -#endif diff --git a/src/value.zig b/src/value.zig new file mode 100644 index 0000000000000000000000000000000000000000..b65aa06beaa0409ccf199b2cbd805fa5a48548d6 --- /dev/null +++ b/src/value.zig @@ -0,0 +1,1641 @@ +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/windows_sdk.h b/src/windows_sdk.h index e8872095597a02c0c538ef23d78df74574353122..a4707b3de0db313f9b37f945a437874fde4b2aa0 100644 --- a/src/windows_sdk.h +++ b/src/windows_sdk.h @@ -16,7 +16,7 @@ #include -// ABI warning - src-self-hosted/windows_sdk.zig +// ABI warning - src/windows_sdk.zig struct ZigWindowsSDK { const char *path10_ptr; size_t path10_len; @@ -34,7 +34,7 @@ struct ZigWindowsSDK { size_t msvc_lib_dir_len; }; -// ABI warning - src-self-hosted/windows_sdk.zig +// ABI warning - src/windows_sdk.zig enum ZigFindWindowsSdkError { ZigFindWindowsSdkErrorNone, ZigFindWindowsSdkErrorOutOfMemory, @@ -42,10 +42,10 @@ enum ZigFindWindowsSdkError { ZigFindWindowsSdkErrorPathTooLong, }; -// ABI warning - src-self-hosted/windows_sdk.zig +// ABI warning - src/windows_sdk.zig ZIG_EXTERN_C enum ZigFindWindowsSdkError zig_find_windows_sdk(struct ZigWindowsSDK **out_sdk); -// ABI warning - src-self-hosted/windows_sdk.zig +// ABI warning - src/windows_sdk.zig ZIG_EXTERN_C void zig_free_windows_sdk(struct ZigWindowsSDK *sdk); #endif diff --git a/src/windows_sdk.zig b/src/windows_sdk.zig new file mode 100644 index 0000000000000000000000000000000000000000..6dfdeb99fd0c2d0ba5f6e7934562a3a7b047bf32 --- /dev/null +++ b/src/windows_sdk.zig @@ -0,0 +1,22 @@ +// 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/zig_clang.cpp b/src/zig_clang.cpp index 21d0c5c0ca06e2500c1aee7ff0960cf8b77e7f73..31c440408310a65fbf0ec3a0ca73c3d25eaf020b 100644 --- a/src/zig_clang.cpp +++ b/src/zig_clang.cpp @@ -13,7 +13,6 @@ * 3. Prevent C++ from infecting the rest of the project. */ #include "zig_clang.h" -#include "list.hpp" #if __GNUC__ >= 8 #pragma GCC diagnostic push @@ -2186,7 +2185,7 @@ ZigClangASTUnit *ZigClangLoadFromCommandLine(const char **args_begin, const char // Take ownership of the err_unit ASTUnit object so that it won't be // free'd when we return, invalidating the error message pointers clang::ASTUnit *unit = ast_unit ? ast_unit : err_unit.release(); - ZigList errors = {}; + Stage2ErrorMsg *errors = nullptr; for (clang::ASTUnit::stored_diag_iterator it = unit->stored_diag_begin(), it_end = unit->stored_diag_end(); it != it_end; ++it) @@ -2204,7 +2203,10 @@ ZigClangASTUnit *ZigClangLoadFromCommandLine(const char **args_begin, const char llvm::StringRef msg_str_ref = it->getMessage(); - Stage2ErrorMsg *msg = errors.add_one(); + *errors_len += 1; + errors = reinterpret_cast(realloc(errors, sizeof(Stage2ErrorMsg) * *errors_len)); + if (errors == nullptr) abort(); + Stage2ErrorMsg *msg = &errors[*errors_len - 1]; memset(msg, 0, sizeof(*msg)); msg->msg_ptr = (const char *)msg_str_ref.bytes_begin(); @@ -2242,8 +2244,7 @@ ZigClangASTUnit *ZigClangLoadFromCommandLine(const char **args_begin, const char } } - *errors_ptr = errors.items; - *errors_len = errors.length; + *errors_ptr = errors; return nullptr; } diff --git a/src/zig_clang.h b/src/zig_clang.h index 2c358d880eefffeaa99302ff497a430f42b67e7f..f9ce9c34ed502ef092db44af88c416fb11e925ff 100644 --- a/src/zig_clang.h +++ b/src/zig_clang.h @@ -14,7 +14,7 @@ // ATTENTION: If you modify this file, be sure to update the corresponding // extern function declarations in the self-hosted compiler file -// src-self-hosted/clang.zig. +// src/clang.zig. struct ZigClangSourceLocation { unsigned ID; diff --git a/src/zig_llvm.cpp b/src/zig_llvm.cpp index e5b9df625c566cefed426e8d6a33a750505227f1..08823050ad1909a94c675f6661d59701b844420c 100644 --- a/src/zig_llvm.cpp +++ b/src/zig_llvm.cpp @@ -927,7 +927,7 @@ class MyOStream: public raw_ostream { }; bool ZigLLVMWriteImportLibrary(const char *def_path, const ZigLLVM_ArchType arch, - const char *output_lib_path, const bool kill_at) + const char *output_lib_path, bool kill_at) { COFF::MachineTypes machine = COFF::IMAGE_FILE_MACHINE_UNKNOWN; diff --git a/src/zig_llvm.h b/src/zig_llvm.h index f07684f2a404670d51fd9b994ef950f407b43f31..007d8afc1fa5dafe84f2cf1210cb34838839c404 100644 --- a/src/zig_llvm.h +++ b/src/zig_llvm.h @@ -60,7 +60,7 @@ enum ZigLLVMABIType { ZIG_EXTERN_C LLVMTargetMachineRef ZigLLVMCreateTargetMachine(LLVMTargetRef T, const char *Triple, const char *CPU, const char *Features, LLVMCodeGenOptLevel Level, LLVMRelocMode Reloc, - LLVMCodeModel CodeModel, bool function_sections, ZigLLVMABIType float_abi, const char *abi_name); + LLVMCodeModel CodeModel, bool function_sections, enum ZigLLVMABIType float_abi, const char *abi_name); ZIG_EXTERN_C LLVMTypeRef ZigLLVMTokenTypeInContext(LLVMContextRef context_ref); @@ -500,8 +500,8 @@ ZIG_EXTERN_C bool ZigLLDLink(enum ZigLLVM_ObjectFormatType oformat, const char * ZIG_EXTERN_C bool ZigLLVMWriteArchive(const char *archive_name, const char **file_names, size_t file_name_count, enum ZigLLVM_OSType os_type); -bool ZigLLVMWriteImportLibrary(const char *def_path, const enum ZigLLVM_ArchType arch, - const char *output_lib_path, const bool kill_at); +ZIG_EXTERN_C bool ZigLLVMWriteImportLibrary(const char *def_path, const enum ZigLLVM_ArchType arch, + const char *output_lib_path, bool kill_at); ZIG_EXTERN_C void ZigLLVMGetNativeTarget(enum ZigLLVM_ArchType *arch_type, enum ZigLLVM_VendorType *vendor_type, enum ZigLLVM_OSType *os_type, enum ZigLLVM_EnvironmentType *environ_type, diff --git a/src/zir.zig b/src/zir.zig new file mode 100644 index 0000000000000000000000000000000000000000..7e723fc6740b791d6801bdfb88a5014d51bc0dcf --- /dev/null +++ b/src/zir.zig @@ -0,0 +1,2701 @@ +//! 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.getTarget()); + const signed = try self.emitPrimitive(src, if (info.signed) .@"true" else .@"false"); + const bits_payload = try self.arena.allocator.create(Value.Payload.Int_u64); + bits_payload.* = .{ .int = info.bits }; + 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/zir_sema.zig b/src/zir_sema.zig new file mode 100644 index 0000000000000000000000000000000000000000..10543d2ee66230501041dbbc75acd95b0af77fb1 --- /dev/null +++ b/src/zir_sema.zig @@ -0,0 +1,1595 @@ +//! 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.comp.bin_file.allocateDeclIndexes(decl); + try mod.comp.work_queue.writeItem(.{ .codegen_decl = decl }); + } else if (prev_type_has_bits) { + mod.comp.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/test/cli.zig b/test/cli.zig index 77d79ed98ed8f33b27bcb8f8f4f4f50d9e47dd8d..7a0a7d64595272579cf8493dd342c0cc0e240a8b 100644 --- a/test/cli.zig +++ b/test/cli.zig @@ -58,7 +58,7 @@ fn printCmd(cwd: []const u8, argv: []const []const u8) void { std.debug.warn("\n", .{}); } -fn exec(cwd: []const u8, argv: []const []const u8) !ChildProcess.ExecResult { +fn exec(cwd: []const u8, expect_0: bool, argv: []const []const u8) !ChildProcess.ExecResult { const max_output_size = 100 * 1024; const result = ChildProcess.exec(.{ .allocator = a, @@ -72,7 +72,7 @@ fn exec(cwd: []const u8, argv: []const []const u8) !ChildProcess.ExecResult { }; switch (result.term) { .Exited => |code| { - if (code != 0) { + if ((code != 0) == expect_0) { std.debug.warn("The following command exited with error code {}:\n", .{code}); printCmd(cwd, argv); std.debug.warn("stderr:\n{}\n", .{result.stderr}); @@ -90,15 +90,15 @@ fn exec(cwd: []const u8, argv: []const []const u8) !ChildProcess.ExecResult { } fn testZigInitLib(zig_exe: []const u8, dir_path: []const u8) !void { - _ = try exec(dir_path, &[_][]const u8{ zig_exe, "init-lib" }); - const test_result = try exec(dir_path, &[_][]const u8{ zig_exe, "build", "test" }); + _ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-lib" }); + const test_result = try exec(dir_path, true, &[_][]const u8{ zig_exe, "build", "test" }); testing.expect(std.mem.endsWith(u8, test_result.stderr, "All 1 tests passed.\n")); } fn testZigInitExe(zig_exe: []const u8, dir_path: []const u8) !void { - _ = try exec(dir_path, &[_][]const u8{ zig_exe, "init-exe" }); - const run_result = try exec(dir_path, &[_][]const u8{ zig_exe, "build", "run" }); - testing.expect(std.mem.eql(u8, run_result.stderr, "All your codebase are belong to us.\n")); + _ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-exe" }); + const run_result = try exec(dir_path, true, &[_][]const u8{ zig_exe, "build", "run" }); + testing.expect(std.mem.eql(u8, run_result.stderr, "info: All your codebase are belong to us.\n")); } fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void { @@ -118,17 +118,20 @@ fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void { \\} ); - const args = [_][]const u8{ + var args = std.ArrayList([]const u8).init(a); + try args.appendSlice(&[_][]const u8{ zig_exe, "build-obj", "--cache-dir", dir_path, "--name", "example", - "--output-dir", dir_path, - "--emit", "asm", - "-mllvm", "--x86-asm-syntax=intel", - "--strip", "--release-fast", - example_zig_path, "--disable-gen-h", - }; - _ = try exec(dir_path, &args); + "-fno-emit-bin", "-fno-emit-h", + "--strip", "-OReleaseFast", + example_zig_path, + }); + + const emit_asm_arg = try std.fmt.allocPrint(a, "-femit-asm={s}", .{example_s_path}); + try args.append(emit_asm_arg); + + _ = try exec(dir_path, true, args.items); const out_asm = try std.fs.cwd().readFileAlloc(a, example_s_path, std.math.maxInt(usize)); testing.expect(std.mem.indexOf(u8, out_asm, "square:") != null); @@ -137,23 +140,25 @@ fn testGodboltApi(zig_exe: []const u8, dir_path: []const u8) anyerror!void { } fn testMissingOutputPath(zig_exe: []const u8, dir_path: []const u8) !void { - _ = try exec(dir_path, &[_][]const u8{ zig_exe, "init-exe" }); - const output_path = try fs.path.join(a, &[_][]const u8{ "does", "not", "exist" }); + _ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-exe" }); + const output_path = try fs.path.join(a, &[_][]const u8{ "does", "not", "exist", "foo.exe" }); + const output_arg = try std.fmt.allocPrint(a, "-femit-bin={s}", .{output_path}); const source_path = try fs.path.join(a, &[_][]const u8{ "src", "main.zig" }); - _ = try exec(dir_path, &[_][]const u8{ - zig_exe, "build-exe", source_path, "--output-dir", output_path, - }); + const result = try exec(dir_path, false, &[_][]const u8{ zig_exe, "build-exe", source_path, output_arg }); + const s = std.fs.path.sep_str; + const expected: []const u8 = "error: unable to open output directory 'does" ++ s ++ "not" ++ s ++ "exist': FileNotFound\n"; + testing.expectEqualStrings(expected, result.stderr); } fn testZigFmt(zig_exe: []const u8, dir_path: []const u8) !void { - _ = try exec(dir_path, &[_][]const u8{ zig_exe, "init-exe" }); + _ = try exec(dir_path, true, &[_][]const u8{ zig_exe, "init-exe" }); const unformatted_code = " // no reason for indent"; const fmt1_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "fmt1.zig" }); try fs.cwd().writeFile(fmt1_zig_path, unformatted_code); - const run_result1 = try exec(dir_path, &[_][]const u8{ zig_exe, "fmt", fmt1_zig_path }); + const run_result1 = try exec(dir_path, true, &[_][]const u8{ zig_exe, "fmt", fmt1_zig_path }); // stderr should be file path + \n testing.expect(std.mem.startsWith(u8, run_result1.stderr, fmt1_zig_path)); testing.expect(run_result1.stderr.len == fmt1_zig_path.len + 1 and run_result1.stderr[run_result1.stderr.len - 1] == '\n'); @@ -161,12 +166,12 @@ fn testZigFmt(zig_exe: []const u8, dir_path: []const u8) !void { const fmt2_zig_path = try fs.path.join(a, &[_][]const u8{ dir_path, "fmt2.zig" }); try fs.cwd().writeFile(fmt2_zig_path, unformatted_code); - const run_result2 = try exec(dir_path, &[_][]const u8{ zig_exe, "fmt", dir_path }); + const run_result2 = try exec(dir_path, true, &[_][]const u8{ zig_exe, "fmt", dir_path }); // running it on the dir, only the new file should be changed testing.expect(std.mem.startsWith(u8, run_result2.stderr, fmt2_zig_path)); testing.expect(run_result2.stderr.len == fmt2_zig_path.len + 1 and run_result2.stderr[run_result2.stderr.len - 1] == '\n'); - const run_result3 = try exec(dir_path, &[_][]const u8{ zig_exe, "fmt", dir_path }); + const run_result3 = try exec(dir_path, true, &[_][]const u8{ zig_exe, "fmt", dir_path }); // both files have been formatted, nothing should change now testing.expect(run_result3.stderr.len == 0); } diff --git a/test/compile_errors.zig b/test/compile_errors.zig index 6ae857d1a8322513b3d8373b5c17707bcc5f698a..7a33de4f19dcb0b87bc6d19f91babd2a1931fd0f 100644 --- a/test/compile_errors.zig +++ b/test/compile_errors.zig @@ -2355,7 +2355,7 @@ pub fn addCases(cases: *tests.CompileErrorContext) void { \\ exit(0); \\} , &[_][]const u8{ - "tmp.zig:3:5: error: dependency on library c must be explicitly specified in the build command", + "tmp.zig:3:5: error: dependency on libc must be explicitly specified in the build command", }); cases.addTest("libc headers note", diff --git a/test/stage2/cbe.zig b/test/stage2/cbe.zig index 06082218666ed784df05c322049a015b23fe30ed..2a176e036821519d57cfe267087c23ec56758536 100644 --- a/test/stage2/cbe.zig +++ b/test/stage2/cbe.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const TestContext = @import("../../src-self-hosted/test.zig").TestContext; +const TestContext = @import("../../src/test.zig").TestContext; // These tests should work with all platforms, but we're using linux_x64 for // now for consistency. Will be expanded eventually. diff --git a/test/stage2/spu-ii.zig b/test/stage2/spu-ii.zig index 1316f19d0d88d9d179c943edf13e097b44a4e90c..aa091c4174aee4f0017d0fb9e0b17a1f1a192a3a 100644 --- a/test/stage2/spu-ii.zig +++ b/test/stage2/spu-ii.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const TestContext = @import("../../src-self-hosted/test.zig").TestContext; +const TestContext = @import("../../src/test.zig").TestContext; const spu = std.zig.CrossTarget{ .cpu_arch = .spu_2, diff --git a/test/stage2/test.zig b/test/stage2/test.zig index ad81e463b960617c40c0220a87ab502bc431cec4..a22dc23d362d9ef77480f1605a155021a4a24783 100644 --- a/test/stage2/test.zig +++ b/test/stage2/test.zig @@ -1,8 +1,11 @@ const std = @import("std"); -const TestContext = @import("../../src-self-hosted/test.zig").TestContext; +const TestContext = @import("../../src/test.zig").TestContext; + +// Self-hosted has differing levels of support for various architectures. For now we pass explicit +// target parameters to each test case. At some point we will take this to the next level and have +// a set of targets that all test cases run on unless specifically overridden. For now, each test +// case applies to only the specified target. -// self-hosted does not yet support PE executable files / COFF object files -// or mach-o files. So we do these test cases cross compiling for x86_64-linux. const linux_x64 = std.zig.CrossTarget{ .cpu_arch = .x86_64, .os_tag = .linux, @@ -73,7 +76,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "Hello, World!\n", ); // Now change the message only @@ -105,7 +108,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "What is up? This is a longer message that will force the data to be relocated in virtual address space.\n", ); // Now we print it twice. @@ -181,7 +184,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "Hello, World!\n", ); } @@ -216,7 +219,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "Hello, World!\n", ); } @@ -241,7 +244,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "Hello, World!\n", ); } @@ -268,7 +271,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); } @@ -295,7 +298,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); } @@ -326,7 +329,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -359,7 +362,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -395,7 +398,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -432,7 +435,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -462,7 +465,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -496,7 +499,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -520,7 +523,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -559,7 +562,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "hello\nhello\nhello\nhello\n", ); @@ -596,7 +599,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -638,7 +641,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -690,7 +693,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -752,7 +755,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -785,7 +788,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -817,7 +820,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -842,7 +845,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -868,7 +871,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "", ); @@ -901,7 +904,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ ); \\ unreachable; \\} - , + , "hello\nhello\nhello\nhello\nhello\n", ); } @@ -920,7 +923,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ bar(); \\} \\fn bar() void {} - , + , "42\n", ); @@ -938,7 +941,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ bar(); \\} \\fn bar() void {} - , + , "42\n", ); @@ -954,7 +957,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ bar(); \\} \\fn bar() void {} - , + , // This is what you get when you take the bits of the IEE-754 // representation of 42.0 and reinterpret them as an unsigned // integer. Guess that's a bug in wasmtime. diff --git a/test/stage2/zir.zig b/test/stage2/zir.zig index 78d971d10415a7300c0acbcc96712539a6d77c54..f87fca374811ea961a9701dee0d4ec1139074b95 100644 --- a/test/stage2/zir.zig +++ b/test/stage2/zir.zig @@ -1,5 +1,5 @@ const std = @import("std"); -const TestContext = @import("../../src-self-hosted/test.zig").TestContext; +const TestContext = @import("../../src/test.zig").TestContext; // self-hosted does not yet support PE executable files / COFF object files // or mach-o files. So we do the ZIR transform test cases cross compiling for // x86_64-linux. @@ -156,7 +156,7 @@ pub fn addCases(ctx: *TestContext) !void { \\ %0 = call(@a, []) \\ %1 = returnvoid() \\}) - , + , &[_][]const u8{ ":18:21: error: message", }, diff --git a/test/tests.zig b/test/tests.zig index 6598a05ea79c70f13b307c59d85eb15266a6ab96..58b2b500943a44b2f9d6cdc53fa7bd22b6acf680 100644 --- a/test/tests.zig +++ b/test/tests.zig @@ -634,7 +634,7 @@ pub const StackTracesContext = struct { warn("Test {}/{} {}...", .{ self.test_index + 1, self.context.test_index, self.name }); - const child = std.ChildProcess.init(args.span(), b.allocator) catch unreachable; + const child = std.ChildProcess.init(args.items, b.allocator) catch unreachable; defer child.deinit(); child.stdin_behavior = .Ignore; @@ -643,7 +643,7 @@ pub const StackTracesContext = struct { child.env_map = b.env_map; if (b.verbose) { - printInvocation(args.span()); + printInvocation(args.items); } child.spawn() catch |err| debug.panic("Unable to spawn {}: {}\n", .{ full_exe_path, @errorName(err) }); @@ -666,23 +666,23 @@ pub const StackTracesContext = struct { code, expect_code, }); - printInvocation(args.span()); + printInvocation(args.items); return error.TestFailed; } }, .Signal => |signum| { warn("Process {} terminated on signal {}\n", .{ full_exe_path, signum }); - printInvocation(args.span()); + printInvocation(args.items); return error.TestFailed; }, .Stopped => |signum| { warn("Process {} stopped on signal {}\n", .{ full_exe_path, signum }); - printInvocation(args.span()); + printInvocation(args.items); return error.TestFailed; }, .Unknown => |code| { warn("Process {} terminated unexpectedly with error code {}\n", .{ full_exe_path, code }); - printInvocation(args.span()); + printInvocation(args.items); return error.TestFailed; }, } @@ -837,34 +837,27 @@ pub const CompileErrorContext = struct { } else { try zig_args.append("build-obj"); } - const root_src_basename = self.case.sources.span()[0].filename; + const root_src_basename = self.case.sources.items[0].filename; try zig_args.append(self.write_src.getOutputPath(root_src_basename)); zig_args.append("--name") catch unreachable; zig_args.append("test") catch unreachable; - zig_args.append("--output-dir") catch unreachable; - zig_args.append(b.pathFromRoot(b.cache_root)) catch unreachable; - if (!self.case.target.isNative()) { try zig_args.append("-target"); try zig_args.append(try self.case.target.zigTriple(b.allocator)); } - switch (self.build_mode) { - Mode.Debug => {}, - Mode.ReleaseSafe => zig_args.append("--release-safe") catch unreachable, - Mode.ReleaseFast => zig_args.append("--release-fast") catch unreachable, - Mode.ReleaseSmall => zig_args.append("--release-small") catch unreachable, - } + zig_args.append("-O") catch unreachable; + zig_args.append(@tagName(self.build_mode)) catch unreachable; warn("Test {}/{} {}...", .{ self.test_index + 1, self.context.test_index, self.name }); if (b.verbose) { - printInvocation(zig_args.span()); + printInvocation(zig_args.items); } - const child = std.ChildProcess.init(zig_args.span(), b.allocator) catch unreachable; + const child = std.ChildProcess.init(zig_args.items, b.allocator) catch unreachable; defer child.deinit(); child.env_map = b.env_map; @@ -886,19 +879,19 @@ pub const CompileErrorContext = struct { switch (term) { .Exited => |code| { if (code == 0) { - printInvocation(zig_args.span()); + printInvocation(zig_args.items); return error.CompilationIncorrectlySucceeded; } }, else => { warn("Process {} terminated unexpectedly\n", .{b.zig_exe}); - printInvocation(zig_args.span()); + printInvocation(zig_args.items); return error.TestFailed; }, } - const stdout = stdout_buf.span(); - const stderr = stderr_buf.span(); + const stdout = stdout_buf.items; + const stderr = stderr_buf.items; if (stdout.len != 0) { warn( @@ -927,12 +920,12 @@ pub const CompileErrorContext = struct { if (!ok) { warn("\n======== Expected these compile errors: ========\n", .{}); - for (self.case.expected_errors.span()) |expected| { + for (self.case.expected_errors.items) |expected| { warn("{}\n", .{expected}); } } } else { - for (self.case.expected_errors.span()) |expected| { + for (self.case.expected_errors.items) |expected| { if (mem.indexOf(u8, stderr, expected) == null) { warn( \\ @@ -1032,7 +1025,7 @@ pub const CompileErrorContext = struct { if (mem.indexOf(u8, annotated_case_name, filter) == null) return; } const write_src = b.addWriteFiles(); - for (case.sources.span()) |src_file| { + for (case.sources.items) |src_file| { write_src.add(src_file.filename, src_file.source); } @@ -1079,7 +1072,7 @@ pub const StandaloneContext = struct { zig_args.append("--verbose") catch unreachable; } - const run_cmd = b.addSystemCommand(zig_args.span()); + const run_cmd = b.addSystemCommand(zig_args.items); const log_step = b.addLog("PASS {}\n", .{annotated_case_name}); log_step.step.dependOn(&run_cmd.step); @@ -1179,7 +1172,7 @@ pub const GenHContext = struct { const full_h_path = self.obj.getOutputHPath(); const actual_h = try io.readFileAlloc(b.allocator, full_h_path); - for (self.case.expected_lines.span()) |expected_line| { + for (self.case.expected_lines.items) |expected_line| { if (mem.indexOf(u8, actual_h, expected_line) == null) { warn( \\ @@ -1240,7 +1233,7 @@ pub const GenHContext = struct { } const write_src = b.addWriteFiles(); - for (case.sources.span()) |src_file| { + for (case.sources.items) |src_file| { write_src.add(src_file.filename, src_file.source); } diff --git a/tools/update_clang_options.zig b/tools/update_clang_options.zig index ea63e767bb96ef5d275fdd4b1413c35c52a47f03..8b7811aa266c0b5e40e4b984d6abd7bdacda82c7 100644 --- a/tools/update_clang_options.zig +++ b/tools/update_clang_options.zig @@ -116,19 +116,19 @@ const known_options = [_]KnownOpt{ }, .{ .name = "E", - .ident = "pp_or_asm", + .ident = "preprocess_only", }, .{ .name = "preprocess", - .ident = "pp_or_asm", + .ident = "preprocess_only", }, .{ .name = "S", - .ident = "pp_or_asm", + .ident = "asm_only", }, .{ .name = "assemble", - .ident = "pp_or_asm", + .ident = "asm_only", }, .{ .name = "O1", @@ -346,7 +346,7 @@ pub fn main() anyerror!void { for (blacklisted_options) |blacklisted_key| { if (std.mem.eql(u8, blacklisted_key, kv.key)) continue :it_map; } - if (kv.value.Object.get("Name").?.value.String.len == 0) continue; + if (kv.value.Object.get("Name").?.String.len == 0) continue; try all_objects.append(&kv.value.Object); } } @@ -365,11 +365,11 @@ pub fn main() anyerror!void { ); for (all_objects.span()) |obj| { - const name = obj.get("Name").?.value.String; + const name = obj.get("Name").?.String; var pd1 = false; var pd2 = false; var pslash = false; - for (obj.get("Prefixes").?.value.Array.span()) |prefix_json| { + for (obj.get("Prefixes").?.Array.span()) |prefix_json| { const prefix = prefix_json.String; if (std.mem.eql(u8, prefix, "-")) { pd1 = true; @@ -465,7 +465,7 @@ const Syntax = union(enum) { self: Syntax, comptime fmt: []const u8, options: std.fmt.FormatOptions, - out_stream: var, + out_stream: anytype, ) !void { switch (self) { .multi_arg => |n| return out_stream.print(".{{.{}={}}}", .{ @tagName(self), n }), @@ -475,8 +475,8 @@ const Syntax = union(enum) { }; fn objSyntax(obj: *json.ObjectMap) Syntax { - const num_args = @intCast(u8, obj.get("NumArgs").?.value.Integer); - for (obj.get("!superclasses").?.value.Array.span()) |superclass_json| { + const num_args = @intCast(u8, obj.get("NumArgs").?.Integer); + for (obj.get("!superclasses").?.Array.span()) |superclass_json| { const superclass = superclass_json.String; if (std.mem.eql(u8, superclass, "Joined")) { return .joined; @@ -510,19 +510,19 @@ fn objSyntax(obj: *json.ObjectMap) Syntax { return .{ .multi_arg = num_args }; } } - const name = obj.get("Name").?.value.String; + const name = obj.get("Name").?.String; if (std.mem.eql(u8, name, "")) { return .flag; } else if (std.mem.eql(u8, name, "")) { return .flag; } - const kind_def = obj.get("Kind").?.value.Object.get("def").?.value.String; + const kind_def = obj.get("Kind").?.Object.get("def").?.String; if (std.mem.eql(u8, kind_def, "KIND_FLAG")) { return .flag; } - const key = obj.get("!name").?.value.String; + const key = obj.get("!name").?.String; std.debug.warn("{} (key {}) has unrecognized superclasses:\n", .{ name, key }); - for (obj.get("!superclasses").?.value.Array.span()) |superclass_json| { + for (obj.get("!superclasses").?.Array.span()) |superclass_json| { std.debug.warn(" {}\n", .{superclass_json.String}); } std.process.exit(1); @@ -560,15 +560,15 @@ fn objectLessThan(context: void, a: *json.ObjectMap, b: *json.ObjectMap) bool { } if (!a_match_with_eql and !b_match_with_eql) { - const a_name = a.get("Name").?.value.String; - const b_name = b.get("Name").?.value.String; + const a_name = a.get("Name").?.String; + const b_name = b.get("Name").?.String; if (a_name.len != b_name.len) { return a_name.len > b_name.len; } } - const a_key = a.get("!name").?.value.String; - const b_key = b.get("!name").?.value.String; + const a_key = a.get("!name").?.String; + const b_key = b.get("!name").?.String; return std.mem.lessThan(u8, a_key, b_key); }